Generating code is not hard anymore.
A good model can write a diff, scaffold a service, write tests, explain an API, migrate a component, and keep going long after a human would have gotten tired of typing. That part is largely solved.
The hard part is generating the right code.
Right code is not code that compiles. It is code that fits the requirement, respects the architecture, handles the business constraint, passes the tests that actually matter, and stays understandable when the context changes next month.
That gap is where the software factory trap lives. But to understand the trap, you first need to understand what code actually is, and what it leaves out.
Code Is The Surface
Conal Elliott, in a Haskell Foundation interview, describes the program as the surface of a larger object.
The executable is not the whole product.
The full thing includes the specification, the derivation, the purpose, the correctness arguments, the tradeoffs, and the path through the conceptual space that produced the final code.
Code is efficient because it strips all of that away. The runtime does not need the meeting where the tradeoff was made. It does not need the Slack thread where the customer clarified the edge case. It does not need the product judgment that made one behaviour acceptable and another unacceptable. None of that is needed to run.
So we remove it.
The program needs those things to be understood, extended, and safely changed. But not to run. And so the context that gave the code its meaning quietly disappears, leaving only the executable surface.
For most of software history, that was manageable. Experienced engineers carried the missing context in their heads, or in documentation nobody updated. They knew which objects mattered, which source had authority, which proof counted, where the flow could break, and which shortcut was fake. They did ontology and epistemology privately, without calling it that. It was just what it meant to be a senior engineer.
The problem is that private expertise does not scale across a swarm. Tribal knowledge does not become usable by a software factory just because it exists somewhere in the organization. It has to be captured at the moment of work — in decisions, review comments, design docs, customer conversations, and traces — so agents can read it when they need it. Manual recap culture will not scale to a factory either. The capture has to become native to the workflow, so the context is already legible when agents need to use it.
If the factory cannot read the context that shaped the decision, it cannot reliably extend the work that came from it.
This is the foundational problem. Not that agents cannot write code. They can. The problem is that agents are being asked to generate the next version of a system from a fraction of the context that created the current one. They produce work that is syntactically correct and semantically incomplete. It looks like progress. It requires a senior engineer to audit it before anything can be trusted.
That is the bottleneck. It was always there. AI made it visible.
The Factory Is Not The Agent
When an agent completes a task today, you typically get:
What you actually need is closer to:
That second record is not decoration. It is what lets anyone (human or system) verify the work without replaying every trace from the beginning. Without it, review becomes reconstruction. Someone has to rebuild the entire chain of intent, context, implementation, and proof from the output alone. That takes longer than the agent saved.
The mistake is thinking the factory is the agent. It is not.
A new engineer is not useful just because they are smart. They need onboarding, goals, repo access, ownership boundaries, permissions, tickets, review loops, escalation paths, and a definition of done. Intelligence is necessary but not sufficient. It has to be organized into labor.
Agents are the same. The current class of coding agents can read files, edit code, run commands, inspect failures, and sometimes repair their own mistakes. The tools are real. What they are missing is not another tool. It is the surrounding structure: which context sources are authoritative, what permissions apply, how to prove the work, and what becomes stale when inputs change.
Without that structure, the failure mode is predictable. The agent writes code. The abstraction is wrong. The implementation passes a narrow test while violating the product intent. The human engineer spends more time reviewing, unwinding, and re-explaining than they would have spent doing the work directly.
A factory is supposed to produce more value than waste. Many homegrown factories do the opposite because they automate code production while leaving requirements, proof, context, and review as human reconstruction work.
This is why most internal "software factory" efforts become a trap. They start with a reasonable premise: give agents access to the repo, give them goals, let them write code, have humans review the result. At first that feels like leverage. Then the second-order system appears.
Who owns the requirements before the agents start? Which sources are authoritative? How much context should each worker get? What permissions does it have? Which tests actually prove the requirement? When the source context changes, what becomes stale?
At that point, the team is not building software with agents. It is maintaining the factory that makes agents usable.
The Team Shape Is Changing
There is a second dimension to this problem, and it runs underneath the first.
Agent-heavy engineering does not always look like a normal team of engineers sitting under a manager, working together on the same codebase. In one conversation with a founder who had already built an agent-native workflow, he described something closer to Napoleonic planning. The goal is set at the top. Each engineer owns a domain. Then each engineer goes off with their own agent workers and figures out how to get that part done.
That pattern makes sense. If one engineer can effectively command several agents, the engineer starts to look less like an individual contributor and more like a domain owner with a small army. Execution becomes cheap and local. Every domain owner can move fast. Every local surface area can evolve quickly.
But without shared doctrine, that becomes fragmentation.
The shared layer matters more, not less: contracts, API protocols, proof standards, source authority, architecture boundaries, and review rules. If engineers are becoming domain owners with agent armies, the company needs a way to coordinate those armies without turning the manager into a full-time traffic controller.
The harder version of this is cross-factory coordination. Two engineers may each have their own agents operating deep in different parts of the codebase, only to discover that they are touching the same abstraction or making incompatible assumptions. Most agent systems route that conflict through a human: agent to human, human to human, human back to agent. That works for short loops. It breaks down when agents have been operating for hours and the relevant context lives several abstraction layers below the plan.
A software factory built for this environment needs a communication layer where agents can surface conflicts, compare source authority, propose shared contracts, and escalate only the decisions that actually require human judgment. The goal is not to remove humans from architecture. The goal is to stop wasting human attention on reconstructing context that the system could have made legible in the first place.
That is a second version of the software factory trap. The leverage is real, but so is the coordination burden. And the coordination burden grows faster than the team expects, because it is invisible until the moment two agent armies collide.
The Proof Also Needs Proof
To get out of the trap, you need two things working together. Most software factory designs have neither.
The first is ontology.
Ontology tells the system what things exist and how they relate. In this setting, it means the factory has a working model of the objects that matter: requirements, sources, stakeholders, tasks, artifacts, permissions, tests, proof claims, product surfaces, dependencies, and decisions. It knows how those things relate. A requirement can support a task. A source can authorize a requirement. A test can support a proof claim. A source change can invalidate an artifact.
This is not exotic philosophy imported for its own sake. It is what senior engineers already do implicitly. They build a map in their head before touching a system. They know which state transition matters, which telemetry proves it, which user-facing claim is not covered by a backend test, and which dependency change makes the work stale. Agentic engineering has to make that implicit map explicit. Not because it is good process, but because agents cannot inherit expert judgment that was never written down.
Ontology keeps everyone — human and agent alike — operating on the same model of the system. Not in the vague sense of "shared understanding," but in the operational sense: the same requirement, source, task, proof claim, and product surface can be referenced without each party reconstructing its own private version of reality.
But ontology alone is not enough.
The second thing you need is epistemology.
Epistemology tells the system how claims about those things become trustworthy. This is where most factory designs break, because they track work without tracking proof.
"The API returned 200" is a claim. It can be known through a runtime trace, test, log, or telemetry record. But it does not prove the user can complete onboarding. That is a different claim, and it needs different evidence. "This output is stale" is another claim. It depends on whether the artifact actually used the source that changed, whether that source was admitted as authoritative context, and whether the change was material enough to invalidate the result.
A claim is not valid until the proof type matches the claim type. A screenshot does not prove durable state. A passing backend test does not prove a user journey. A clean summary does not prove the generated work still matches the source requirement. These substitutions happen constantly in agent workflows, not out of dishonesty, but because the system was never forced to distinguish between them.
This is where ontology and epistemology work together. Ontology gives the factory the work map. Epistemology gives it the proof rules. Together, they turn "the agent did something" into "this claim became true under these constraints, with this evidence, and here is what would make it stale."
When both are encoded into the workflow, the factory can verify more of its own work before a human sees it. It can catch that a requirement was cited but not implemented. It can catch that a test proves the wrong layer. It can catch that a source changed after the task was generated. It can check whether the requirement was admitted from the right source, whether the task used the right context, and whether a lower-level implementation decision should be escalated because it changes an architecture boundary.
The human should not have to reconstruct the entire chain from intent to implementation to proof. The system should package that chain so the human can focus on the judgment calls that actually require a human. That is the mechanism by which the review burden can shrink — not by removing human judgment, but by giving human judgment the right input rather than a pile of output to reverse-engineer.
We saw the gap directly in our own internal work. One run produced dozens of contracts, dependency edges, manifests, and reviewable receipts. The machinery performed. It did a large amount of structured work. But the product outcome was broader than the machinery had been forced to prove. Backend contracts passed. The end-to-end user claim remained unproven. The factory had generated an impressive amount of evidence for the wrong object.
That is the proof trap inside the factory trap: a system can look rigorous while proving the wrong thing.
What The Public Data Actually Shows
The productivity studies tend to get cited selectively, usually to support whichever argument the author started with. Here is what they say together.
In a controlled Microsoft Research experiment, developers with GitHub Copilot completed a bounded JavaScript task 55.8% faster. In a GitHub code-quality study, Copilot users were 53.2% more likely to pass all ten unit tests in the exercise. Those are real results, and they matter. They are gains on well-scoped tasks with clean proof surfaces — exactly the conditions where the factory machinery problem does not yet appear.
In METR's early-2025 study of 16 experienced open-source developers working on 246 real issues in their own repositories, AI access made tasks take 19% longer. Stack Overflow's 2025 survey found that more developers distrust AI accuracy than trust it, and that the most common frustration is output that is "almost right, but not quite." Harness's 2026 DevOps Modernization report found that 47% of very frequent AI coding users said manual downstream work — QA, code reviews, remediation — had become more problematic.
The pattern is not "AI coding tools do not work." The pattern is that faster code production can expose a slower verification system. When the code arrives faster than the review process can absorb it, the bottleneck moves downstream and becomes invisible to the metric being optimized. You measure time-to-diff and it looks good. You measure time-to-trusted-outcome and it looks worse.
DORA's 2025 research is blunt about the organizational dimension: AI is an amplifier of whatever is already there. Strong teams get stronger. Weak processes get weaker faster. That framing aligns with everything above. If the factory already has good requirements engineering, source authority, and proof discipline, agents make it faster. If it does not, agents make the gap more expensive.
Rolling Your Own Software Factory
A founder I spoke with had already built the workaround.
His team was not debating whether agents could write code. They had moved past that. Their workflow ran roughly: large requirements documents broken into technical specs, specs turned into work breakdown structures, work handed to recursive agent workers, output wrapped in property and contract tests, result reviewed against the original requirements. When source context changed, they rewrote impacted code rather than patching a stale partial edit.
That is a real software factory. It operationalizes the things this article has been describing: requirements as the starting point, source authority made explicit, work decomposed recursively, proof obligations defined before execution, and staleness handled by rewriting rather than accumulating drift.
It is also a lot to own.
The signal that mattered in that conversation was not "we like AI." It was closer to: we have already built the internal machine, we know exactly why it matters, and we would rather pay for a maintained version than keep owning this custom machinery ourselves.
That cuts against a common narrative about AI, that every company will simply generate all its own software and stop paying for tooling. The reality is more complicated. When machinery becomes painful, fast-changing, and non-core, some teams look for a maintained layer rather than owning it indefinitely. The value moves up a level. This is not a new pattern. It is the same reason teams buy CI systems, observability platforms, auth providers, and deployment tools rather than building every layer themselves.
What teams want is not more agents. They want faster, better outcomes without becoming the factory maintenance team.
Old Process, New Runtime
There is a historical parallel worth drawing out, because it clarifies something important about why this problem has been hard to solve before.
A lot of old engineering discipline failed not because the ideas were wrong, but because the enforcement layer was human.
Ideas like HATEOAS — Hypermedia as the Engine of Application State — and CORBA — the Common Object Request Broker Architecture — came from a genuine problem: make distributed systems more understandable, navigable, and consistent. Self-describing interfaces. Standardized API contracts. Requirements traced to implementation. Correctness arguments that could be audited. The technical value was real.
The coordination cost was brutal.
Getting large organizations to agree on API standards meant navigating hundreds of engineers across dozens of teams with different histories, preferences, and local incentives. Maintaining discipline across gRPC, REST, GraphQL, SOAP/XML, and one-off designs meant either building translation machinery, convincing teams to retool their interfaces, or constantly policing drift. In practice, even teams that started with strong API discipline could decay, because enforcing homogeneity became too much work for the humans responsible for it.
Requirements engineering suffered the same fate. Trace links between requirements and implementation became documents nobody updated. Correctness arguments became ceremony. Proof obligations became checkboxes. Not because engineers were careless, but because maintaining all of it manually, across a large organization, in parallel with actually shipping software, was unsustainable.
Agents change that tradeoff.
A coding agent does not have personal preferences about API style. It does not need to be persuaded in an architecture meeting. It does not get tired of maintaining trace links or resent being asked to separate a user-journey claim from a backend proof claim. It needs the contract in a readable form, the actions in an executable form, and the proof obligations in a checkable form. What used to require organizational enforcement can become execution context delivered at the moment of work.
This means some old process-heavy ideas become newly practical when they are turned into runtime inputs rather than human-enforced policy. Self-describing actions. Schemas. Interface contracts. Source authority records. Trace links between requirements and implementation. Tests that an agent can consume while doing the work rather than audit afterward.
This is also the business case for requirements engineering in an AI context. It is not a return to heavyweight process. It is the recognition that agents can maintain the discipline that human organizations found too expensive to sustain, If the workflow is designed to make that discipline executable rather than documentary.
More Context Is Not The Answer
The intuitive response to everything above is: just give the agent more context. Connect Slack. Connect Google Drive. Connect Linear. Connect the repo. Give it every transcript, every ticket, every trace, every doc, every comment. Let it figure things out.
Sometimes that helps. Often it creates a new problem.
If an agent uses a small, approved context bundle, the system can say what the output depended on:
If R17 changes, you can ask what work became stale. You can answer that question.
If the agent read half the repo, forty docs, old Slack threads, random traces, and five stale planning notes, the system can only say:
"this may depend on everything"
That is useless. Everything becomes related to everything. Staleness detection becomes impossible. Source authority becomes ambiguous. The factory cannot verify its own work because it cannot reconstruct what the work depended on.
The problem is not context quantity. The problem is context shape.
The codebase itself is a poor primary context source when treated as the whole context. It gives the agent the final pieces of prior puzzles. It does not reliably explain why those pieces have that shape, which tradeoffs produced them, which customer promise they protect, or which shortcut would be unacceptable. Connecting all available company knowledge can actually increase the surface of ambiguity rather than resolve it, because the agent may be able to search everything and still not know which source is authoritative, which decision superseded another, which thread was merely discussion, or what should become stale when a requirement changes.
This is where context graphs become useful, but only when we are precise about what makes them valuable. A context graph is not valuable because it contains everything. It is valuable because it records what mattered, who or what authorized it, what depended on it, and what becomes stale when it changes. The important object is not "all context." It is the local neighborhood around the work: the requirement, the sources that authorize it, the contracts it touches, the assumptions it depends on, the proof surfaces that would validate it, and the sources that would invalidate it if they changed.
A graph that cannot be navigated is just another context dump. A useful graph is scoped, indexed, and programmatically accessible. It lets the factory hand an agent the right slice of the work map instead of dragging the whole company into the prompt.
And the graph has to be paired with workflow. A graph sitting in a database does not make an agent better by itself. The agent has to know which slice to read, which edges are authoritative, which claims are stale, which source wins when two sources disagree, and which proof surface counts for the work it is doing. Context shape is set by workflow design, not by the quantity of information available.
Synchronous And Asynchronous Work
Not every task should run through the same factory flow, and getting the sequencing wrong is its own trap.
Some work is genuinely suited to long-running autonomous missions. Backend migrations, API changes, infrastructure refactors, and many internal system changes have clear requirements, strong proof surfaces, and minimal dependence on human taste. If the requirements are well-formed and the proof obligations are explicit, an agent can decompose the work, execute it, verify it, and return a reviewable packet without constant human involvement. The factory's job there is to make the autonomy trustworthy, not to supervise every step.
Other work needs synchronous human collaboration. Product-facing UI, interaction design, onboarding flows, copy, and user experience often require back-and-forth iteration. You can still use ontology and epistemology in those flows — in fact, having explicit requirements makes the collaboration sharper, because the team has to articulate what experience it is trying to create and what would count as success. But the proof is different. A user-facing experience cannot be reduced to "the handler passed." It needs human judgment about clarity, trust, feel, and whether the interface creates the intended experience.
The mistake is forcing every task into one mode. Treating judgment-heavy, experience-dependent work as a long-running autonomous mission produces plausible output that requires extensive human reconstruction. Treating bounded, well-specified backend work as something that needs constant human collaboration wastes the leverage the factory was built to provide.
A useful software factory knows the difference. It uses asynchronous missions for work with strong proof surfaces and synchronous collaboration for work where the proof is human judgment. In both modes, requirements engineering matters; not as ceremony, but as the mechanism that determines which mode is appropriate and what done means when the work arrives.
The Trap
The software factory trap is building just enough machinery to make agents useful, and then discovering that the machinery has become its own product.
You maintain prompts. You maintain skills. You maintain context pipelines. You maintain docs. You maintain review rituals. You maintain test conventions. You maintain agent permissions. You maintain trace exports. You maintain scripts that glue all of this together. Every model change, tool change, workflow change, and team habit change forces the factory to change too.
At small scale, this is interesting engineering. At company scale, it becomes a second codebase nobody budgeted for.
And yet the work underneath the trap is real. Requirements formation. Source authority. Bounded context. Proof obligations. Review packets. Staleness detection. Coordination protocols. These are not optional layers that cautious teams add out of anxiety. They are the layers that determine whether the factory produces trusted outcomes or produces reconstruction labor.
This is the same reason teams eventually buy CI systems, observability platforms, auth providers, and deployment tools. They could build all of it. Some do. Most eventually decide they do not want to own every layer of machinery required to make their real work reliable. Agentic engineering is entering that phase. The homegrown factory effort eventually collides with the cost of keeping it current, and teams start to ask whether someone else should own that layer.
The buyer signal is not "we like agents." It is:
We need relief from owning the homegrown software factory, so we can focus on the bigger problems.
The bigger problems are not maintaining a software factory/engineering team, but rather the problems that led to it being required in the first place. After all, you don't put together an engineering team for the sake of it; you do it because you need to build something the market requires. That's where your energy should go, because that's where the novel problems lie.
What This Actually Requires
Go back to Conal Elliott's frame for a moment.
The program is a projection of a larger object. The code is the shadow. Behind it sits the specification, the derivation, the correctness argument, the purpose, the path through the conceptual space that produced the final shape. The runtime does not need any of that. But extending the work, correcting it, or knowing whether it should exist at all — those require access to the larger object, not just its shadow.
This is what separates how different engineers engage with the same codebase.
A junior engineer sees the files. They work on what is in front of them. A senior engineer can reconstruct enough of the surrounding context to understand what the code is doing and why. A staff engineer asks a different question entirely: is this the right thing to build? What is this connected to? What changes if we do this and what breaks if we do not? They are not operating on the code. They are operating on the larger object behind it.
Current coding agents mostly operate at junior-to-senior altitude. They can read files, follow specs, fix tests, repair failures, and generate syntactically correct work. What they cannot do reliably is the staff engineer move: step back from the immediate task and ask whether the frame is right, what the work is actually connected to, and whether it should exist at all.
That altitude gap is where the babysitting tax lives. It is not that agents cannot write code. It is that every output still requires a human to perform the staff engineer check that the agent skipped. Someone has to ask: does this fit the architecture? Does this serve the real requirement? Did we build the right thing? That question cannot be answered from the shadow alone.
The teams that will move fastest are not the ones generating the most code. They are the ones whose factory can operate closer to staff-engineer altitude; not just executing within a frame, but having enough of the larger object available to question the frame. That means the factory needs the derivation, not just the diff. The correctness argument, not just the passing test. The product intent, not just the spec chunk. The connection to business reality, not just the ticket.
When that context is encoded into the workflow, agents can infer things that previously required a human to infer. They can catch that a requirement contradicts an existing architecture decision. They can flag that a proof surface proves the wrong layer. They can notice that two agent workers are making incompatible assumptions three abstraction levels below the plan. They can ask — before the code is written — whether this work should exist at all.
That is not more context. It is context at the right altitude.
The software industry has always known humans would move up an abstraction layer as tools handled more low-level work. What was less obvious is that moving up requires the larger object to come with you. You cannot delegate reliably to a system that only has the shadow.
My bet is not that more agents solve this. My bet is that the factories built around agents (the ones that preserve the derivation, the correctness argument, the product intent, and the expert judgment that usually stays trapped in a staff engineer's head) are where the durable leverage lives.