The keyboard was never the bottleneck
The art of programming isn’t disappearing. It’s migrating
There’s a lot of talk right now about AI “replacing programmers.” You’ll also hear that APIs are dead, or that programming itself is disappearing.
These claims all point to something real. Generative AI has changed how software gets written. It has lowered the cost of producing working code to a degree that would have been difficult to imagine even a few years ago.
But they misidentify where the difficulty in programming actually lives. The keyboard was never the bottleneck. Generative AI makes that harder to ignore.
The limiting factor in programming has never been typing speed, or even the ability to produce syntactically correct code. It has been the ability to understand a problem, define its boundaries, anticipate change, and build something that continues to work as conditions evolve.
Those challenges do not disappear when code becomes easier to generate. If anything, they become more visible. What we are seeing is not the disappearance of programming, but a shift in where the work happens.
To understand that shift, it helps to look at programming not as a single activity, but as a set of levels with different goals, constraints, and failure modes.
The levels
Not all programming work is the same. It spans a range.
1. Solution-specific computing At the lowest level, programming is solution-specific. It solves one problem, in one way, under a fixed set of assumptions. This is the world of scripts, quick automations, and notebook experiments. Once the problem is solved, the code has no further role. Its usefulness ends with the immediate task.
2. Repeatable local workflows At the next level, the goal shifts from solving a single problem to solving a class of problems. The code runs more than once, usually under the control of a single person or a small team. Variation begins to appear in inputs and conditions, and consistency starts to matter. What worked once must now work again, under slightly different circumstances.
3. Shared systems At a higher level, systems support a growing and evolving set of uses across multiple users. The code is no longer private or isolated. Other people depend on it, often in ways that were not fully anticipated at the start. Behavior must be predictable and observable, and changes must be coordinated. The system becomes something that others rely on, not just something that works.
4. Socio-technical systems At the highest level, systems operate within human institutions. They are shaped by policy, incentives, interpretation, and accountability. The “system” is no longer just the code. It includes the people who use it, the organizations that depend on it, and the expectations that surround it. At this level, technical decisions are inseparable from human context.
A simple example helps make this concrete.
At the lowest level, you might build a tiny shopping agent to track down an out-of-print book you have been looking for. It solves one problem, in one way, under a fixed set of assumptions. Once the book is found, the code has done its job.
At the next level, you might build a small system to repeatedly search across multiple sources for hard-to-find books. Now you are solving a class of problems. The system needs to handle variation in inputs, different sources, and repeated use over time.
At a higher level, you might build an e-commerce platform that allows others to search, buy, and sell rare books. Now multiple users depend on the system. You need stable interfaces, predictable behavior, and coordination across components such as inventory, payment, and fulfillment.
At the highest level, you are no longer just building a system. You are operating within an ecosystem. You must handle international currencies, shifting tax regulations, fraud prevention, and dependencies on external services you do not control. The system must evolve as conditions change, often in ways that cannot be anticipated in advance.
The work becomes less about solving a specific problem, and more about remaining useful as the problem itself continues to change.
Where AI is strong
Generative AI is very effective at the first level.
It can produce working code quickly, iterate through variations, and handle common patterns with ease. It significantly reduces the effort required to solve a known problem under fixed assumptions. This is why it feels so powerful. It collapses the cost of getting something to work right now.
It also provides meaningful assistance at the second level, especially during prototyping. It can help generate initial implementations and explore variations across a class of problems.
However, as you move up the levels, the nature of the work changes in ways that are not addressed by faster code generation.
What changes
The central challenge in programming is deciding what to build and ensuring that what is built can survive contact with reality. The target of programming spans a progression. At first, the goal is to solve a specific problem. Then it becomes solving a class of problems. Eventually, it is about remaining useful as new problems emerge. The first stage is concerned with producing answers. The second introduces abstraction. The third requires anticipation.
At higher levels, constraints do not remain fixed. Inputs vary over time. Users behave in ways that are difficult to predict. Dependencies change. Requirements evolve, sometimes gradually and sometimes abruptly. Under these conditions, solutions can no longer be narrow and specialized. They must support a range of possible futures, including ones that were not anticipated at the outset.
This is not simply a matter of scaling runtime behavior. It is a matter of scaling design itself. Systems must be designed in a way that allows them to absorb change, support extension, remain coherent across teams, and persist long enough to evolve.
The boundary
Generative AI is strong at solving known problems under fixed assumptions. It is much less effective at generalization, especially when that generalization must hold under changing constraints.
This limitation appears in a consistent pattern. Code that works in isolation often fails when inputs vary. It breaks when coordination is required across components. It becomes unreliable when exposed to real-world conditions over time.
AI systems handle local, immediate correctness well. They struggle with maintaining coherence across a system as it grows in scope and evolves over time.
Why structure doesn’t go away
At lower levels of computing, it is often possible to proceed without much structure. A single user solving a single problem can rely on informal assumptions and implicit understanding. At higher levels, this is no longer sufficient.
As soon as multiple actors depend on a system, expectations must be made explicit. Interfaces must be stable enough to support independent work. Changes must be managed in a way that does not disrupt others. Behavior must be predictable, not just correct in a single instance.
These needs are often expressed through APIs and related structures. The terminology may change, but the underlying requirement does not. Systems that support multiple users and evolving use cases require shared contracts.
If anything, this requirement becomes more pronounced in an environment where code is produced more quickly and by a wider range of contributors, often with less shared context.
What this means
AI does not replace programming. It compresses and commoditizes the lower levels of work, while exposing the importance of the higher ones. This creates an opportunity. As the cost of producing code drops, more attention can be directed toward designing systems that can evolve under change.
This is close to what Douglas Engelbart described as getting better at getting better: improving our ability to improve. Whether that opportunity is realized is not guaranteed. Lower costs can just as easily lead to more rapid production of solution-specific code without corresponding gains in system design.
As code generation becomes easier, the value shifts toward planning, architecture, and the exercise of judgment about possible futures. These are not eliminated by automation. They become more visible and more important.
And So…
We are not seeing the end of programming.
We are seeing a shift in where the difficulty resides, and where attention is required.
We’re seeing the opportunity to get better at computing.


