Most discussions about AI replacing programmers get the question wrong. They ask: “Can AI write code?” Of course it can. The better question is: “What was the programmer actually doing that mattered?”

We think the answer is surprisingly simple. A programmer’s real work has always been two things: designing constraints and constructing context. AI is about to make this division visible — and reveal which one is the actual source of value.

Two Jobs, Not One

When you watch a programmer work today — really work, not just type — they spend their time on two distinct activities.

The first is constructing context. This means taking a fuzzy requirement (“users should be able to pay each other”) and translating it into something precise enough for code to be written. What’s the payment flow? What states can a transaction be in? What happens on failure? Context construction is the act of resolving ambiguity into specificity.

The second is designing constraints. This means choosing the rules that bound the solution space. Which language? What type system? Which architectural pattern? What passes CI and what doesn’t? Constraints are the walls of the maze. They don’t solve the maze — they define it.

For decades, these two activities were entangled because the same person had to do both. You couldn’t design constraints without also constructing context, because you were the one writing the code. But AI agents have begun to decouple them. An agent can navigate the maze. It cannot build the maze. And this decoupling is about to reshape what it means to be a programmer.

Context Is Becoming Commodity

Context construction — the translation of requirements into implementation-ready specifications — is valuable work. But it’s not uniquely the programmer’s domain.

A technically literate product manager can write a spec that an AI agent turns into working code. We see this already with “vibe coding”: someone with taste but no engineering background describes what they want, and the agent produces it. The context they provide isn’t perfect, but it’s good enough. And “good enough” is the death knell of any skill’s market premium.

This doesn’t mean context construction is easy or unimportant. It means it’s shareable. The ability to articulate what you want, in enough detail for an agent to act on it, is a skill that many people can develop — not just those with computer science degrees. As agents get better at tolerating ambiguity, the bar for useful context will drop further.

The programmer’s defensible value lies elsewhere.

Constraints: The Hard Kind

Here is something we’ve learned from building systems where AI agents generate code in production: the only constraints that actually work are the ones machines can verify.

You can write a beautifully detailed CLAUDE.md file. You can craft the perfect system prompt. You can explain your architectural vision in eloquent prose. And the agent will violate it the moment the context window gets crowded or the task gets complex. Natural language constraints are suggestions. They are soft. They degrade gracefully — which in engineering means they degrade.

What doesn’t degrade? Type systems. Schema definitions. Compiler errors. CI pipelines that reject non-conforming code before it ever reaches a human reviewer.

Consider the difference between telling an agent “use our payment API correctly” versus giving it an auto-generated typed client where every method signature, every required field, every enum variant is enforced at compile time. In the first case, the agent might call the wrong endpoint, pass a string where an integer is expected, or skip a required field — and you won’t know until runtime, if you’re lucky. In the second case, the agent literally cannot produce code that misuses the API. The constraint is not communicated — it is imposed.

This is the critical distinction. A constraint that relies on the agent’s understanding is not a constraint. It’s a hope. A real constraint operates at the level of the toolchain: the type checker, the linter, the schema validator, the CI gate. It makes the wrong thing impossible rather than merely discouraged.

This is why choice of language matters more now than it did five years ago. When humans write code, a dynamically typed language is a productivity trade-off — you move faster, you catch errors later. When agents write code, the lack of static types removes an entire feedback channel. The agent can’t learn from a type error if there is no type error. You’ve widened the solution space in exactly the way that makes agents less reliable.

The same logic applies up the stack. A REST API with loosely validated JSON bodies is an invitation for agent mistakes. A gRPC service with a protobuf schema is a constraint surface. A database with no foreign keys or check constraints is a blank canvas. A database with rigorous constraints is a specification that rejects bad data at the boundary.

Every architectural decision a programmer makes is, at its core, a decision about how narrow or wide the agent’s action space should be.

The Dialectic of Intelligence and Constraint

There’s a deeper tension here that most people miss. Intelligence and constraint are not merely complementary — they are adversarial.

A more capable agent will find paths through your constraint space that you didn’t anticipate. It will technically satisfy every rule while producing something you never intended. Anyone who has written a sufficiently complex type system has encountered this: the code compiles, the tests pass, and the result is still wrong in some way you didn’t think to formalize.

This creates a dialectic. You design constraints. The agent, through its intelligence, reveals gaps in those constraints — not maliciously, but as a natural consequence of exploring a large solution space. You tighten the constraints. The agent finds new gaps. And so the system evolves.

This is not a problem to be solved. It is the job. The programmer’s role in an agent-driven world is to be the other side of this dialectic — the one who observes what the agent produces, identifies where the constraints failed, and redesigns them.

And this is precisely why constraint design cannot itself be automated. The agent cannot be both the intelligence exploring the space and the external force bounding it. You cannot ask the maze-runner to also be the maze-builder, because the maze exists to constrain the runner. Constraints encode values, priorities, and trade-offs that are external to the system they govern. They are, in the deepest sense, an exercise in judgment about what matters.

Constraint Design Is Management

This realization leads somewhere unexpected: constraint design is not really engineering. It’s management.

Not management in the sense of tracking Jira tickets and running standups. Management in the sense of making decisions about how a system of agents — whether human or artificial — should be bounded, directed, and evaluated.

When you choose to enforce a strict type system, you are making a management decision: we value correctness over speed of iteration. When you design a CI pipeline that rejects code below 80% test coverage, you are encoding a value: untested code is not acceptable here, regardless of who or what wrote it. When you select an event-driven architecture over request-response, you are constraining the entire system’s communication patterns — not because one is objectively better, but because one aligns better with your priorities for this particular system.

These are not technical decisions in the narrow sense. They are value decisions expressed through technical mechanisms. And this is why AI agents cannot make them: they have no values of their own. An agent can optimize within a constraint space, but it cannot decide what the constraint space should be. That requires knowing what you care about — reliability vs. speed, consistency vs. flexibility, safety vs. capability — and encoding those priorities into the formal structures the agent operates within.

Traditional tech leads often miss this. Many of them spend their time tracking delivery schedules and reviewing pull requests — which is to say, they manage the output of the system rather than the constraints of the system. In a world where agents produce the output, that form of management becomes obsolete. What remains valuable is the tech lead who designs the type system, shapes the CI pipeline, and chooses the architectural patterns that make the agents productive and safe.

Here’s a counterintuitive corollary: the best tech leads in an AI-native world will also be the best users of AI. If your skill is constraint design, then AI agents are your medium. The better you understand how agents behave — where they drift, where they break, where they surprise you — the better you can design the constraints that channel their intelligence into useful work.

Vibe Code and Survivability

There’s a popular criticism of AI-generated code: it’s throwaway. No human can maintain it. If the AI disappears, you’re stuck with incomprehensible spaghetti.

This criticism is correct — but only for unconstrained AI-generated code. Code produced within a well-designed constraint system tells a different story.

If the type system is rigorous, the generated code must conform to it — which means the types serve as documentation. If the architecture enforces clear module boundaries, the generated code is modular by construction — not by the agent’s good judgment, but by the impossibility of violating the structure. If the CI pipeline demands meaningful tests, the generated code arrives pre-tested.

In other words, well-constrained vibe code is not a mess that dies when the AI goes away. It’s a codebase that humans can read, navigate, and modify — because the constraints that guided its generation also make it comprehensible.

This flips the survivability argument. The question isn’t whether AI-generated code can survive without AI. The question is whether the constraints that shaped it were good enough to make the code self-explanatory. And that is entirely a function of the constraint designer’s skill.

What Programmers Become

So here is the picture that emerges.

Context construction — the translation of intent into specification — is a skill that will be widely distributed. Product managers, designers, domain experts, and yes, programmers will all contribute context. The tools will get better at consuming messy, ambiguous context and producing reasonable output. This is good. It democratizes software creation.

Constraint design — the art of defining the formal boundaries within which agents operate — is the programmer’s enduring contribution. It requires understanding type systems, compiler theory, architectural patterns, and database design. But more fundamentally, it requires the ability to make value judgments about what should be possible and what should be forbidden, and to express those judgments in forms that machines can enforce.

The programmer of the future is not someone who writes code. They are someone who designs the space in which code gets written. They are, in the most literal sense, a constraint designer — part engineer, part manager, part philosopher.

And if that sounds like a strange combination, consider that it always was. We just couldn’t see it clearly until the code-writing part was taken away.