Engineering

We Pointed AI at Ten Years of Code and Asked It to Understand. It Couldn't.

Khaled Elmorabea
August 24, 2026
0
Minutes
We Pointed AI at Ten Years of Code and Asked It to Understand. It Couldn't.

Summarize and analyze this article with 👉

💬 ChatGPT or 🔍 Perplexity or 🤖 Claude or 🔮 Google AI Mode or 🐦 Grok (X)

Late 2025. We were running what the industry now calls RPI (Research, Plan, Implement) on a decade-old mobile SDK.

The loop looked disciplined. The agent researched the codebase and built its understanding of the feature. Then it planned the implementation, and we sat with it through the planning, answering every question it raised about architecture, design decisions, conventions, and edge cases. Then it was implemented.

On paper, a rigorous workflow. In practice, the real work started after the output landed.

The output wasn't wrong in obvious ways, which is worse than being wrong in obvious ways. Code that looks right but doesn't cost you review time, not typing time. So we kept correcting and re-prompting, pushing the AI toward code we could have written ourselves, faster. It was delaying us more than it was helping us.

We didn't stop at RPI. We built a thoughts repo, an external memory, so the agent didn't forget on Tuesday what it learned on Monday. We kept enriching our Claude rules. Our SDK is Objective-C and Swift on iOS, Kotlin and Java on Android, with ten years of conventions living in senior engineers' heads. The agent wrote working code in the wrong style: wrong patterns, wrong error handling, wrong threading conventions. Every violation meant a human catching it, correcting it, and encoding the correction back into the rules. Getting AI to respect the way we implement features wasn't a prompt. It was a process, and a slow one.

The honest lessons from that stretch:

  1. An agent can't infer intent that was never written down. Legacy code encodes decisions, not reasons.
  2. Following conventions doesn't come free. Every norm you want respected must be made explicit, checked, and re-encoded when violated.
  3. Speed doesn't arrive with the tool. We were reviewing everything, correcting constantly, and the throughput we were aiming for wasn't there.

Keep the timing in mind too: the models available in late 2025 were not the models available today. We came close to writing off AI entirely.

To explain why we didn't, I need to back up and tell you why we were rewriting this SDK in the first place.

Why We Were Rewriting at All

Two motives, in order of importance.

The first was where the industry is going. Debugging is shifting from people reading dashboards to LLMs and agents reading data. Our customers were already feeding their debugging data to LLMs, and an LLM is only as good as the context you hand it. Fragmented feeds make poor context. One coherent capture of everything that happened in a session makes very good context. We wanted the SDK to hand customers context rich enough to level up their entire LLM usage. Our existing architecture could not do that.

The second was a bill ten years in the making. Our SDK was built for one product. Five more were added on the same foundation, and every one of those decisions was right at the time it was made. Ten years later the bill arrived, and it's a bill any long-lived codebase will recognize:

  • Different parts of the system disagreed with each other. Each captured its own copy of what happened, its own way, so two views of the same moment could tell two different stories.
  • The same plumbing was built six times. Six implementations of one problem. Six places to fix every bug.
  • Changing one thing broke another. Which is why "small" features took a quarter.

A practical taste of that coupling: ask the codebase a simple question, "when does a session start?", and you'd get different answers depending on where you asked. Each area carried its own copy of the logic. Multiply that by every shared concept: screenshots, user steps, network state, foreground transitions. The boundaries we assumed we could refactor around didn't exist. They were never drawn.

Logs Are a Product, Not an Attachment

The first motive needs more detail, because it's the mindset shift underneath the whole rewrite.

For ten years, we treated logs the way most of the industry did: as an attachment. A crash report carried its own copy of the logs. A bug report carried its own copy. Each product decided how long its copy lived, how it was stored, and who could see it. Logs were a supporting artifact that rode along with events. Duplicated, siloed, passive. Nobody used logs. People scrolled them, one report at a time.

The industry has stopped thinking this way, and the direction is consistent. Serious observability players are landing on the same four principles: logs as a queryable, correlated signal rather than an event attachment. A unified lifecycle instead of per-product policies. OpenTelemetry as the collection standard. And AI as the consumption layer, with the goal of a logging product where you don't need to read log lines to understand your system.

Our new SDK is built for that world. Logs become a first-class signal with their own lifecycle: captured once, correlated with everything (the crash, the session, the trace, the user journey) through shared identity instead of duplication, and stored in a queryable engine instead of buried inside individual report payloads.

That changes what debugging looks like. Instead of scrolling one report's attachment, you search across every occurrence of a crash group for the common pattern. You filter sessions by error level, endpoint, or screen. You investigate app behavior with no event as the entry point at all. "What's happening with our checkout flow this week?" becomes a question logs can answer directly.

This is where it meets the LLM vision. An agent investigating a production issue can't reason over six disconnected copies of the truth. It can reason over one coherent, queryable stream. Logs stop being something a human scrolls and become context an agent can think with. That's the product the new SDK is built to feed.

The Plan That Was Too Slow

The original plan was the responsible one: refactor in place.

  • Build a safety net first. Automate the SDK through a demo app before touching anything.
  • Untangle module by module.
  • Don't change product behavior. Millions of sessions a day run through this thing, and paying customers don't care about your architecture diagrams.

We ran the refactor plan for months. It was slow, and it's worth being precise about why.

Refactoring in place means every change is judged against ten years of accumulated behavior. Before you can move a line, you have to know what it protects: which bug from years ago, which OEM quirk, which customer edge case. That knowledge is scattered across the code, a decade of documents, and a few senior heads. No single place holds it. So every step was the same three moves: understand the old intent, change it carefully, verify nothing else moved. The verification, not the change, was the work.

And it gets worse. Our intent was never to preserve the behavior. It was to change it, to fulfill the LLM vision. Refactoring in place toward different behavior is a nightmare. Every behavioral change wants to cascade through the coupled system, and every milestone still has to ship safely on top of it. Under that pressure, the honest move each time was to duplicate code rather than trigger the cascade right now. Milestone by milestone, the vision drifts. You set out to unify the system, and the process itself pushes you to fork it. The same mechanism that keeps each release safe is the one that quietly kills the destination.

This is also why an LLM was never going to speed it up. In a refactor, correctness is defined by existing behavior, so every piece of model output has to be checked by a human who is an expert in that part of the system. The loop becomes: the model proposes, the human spots what it silently broke, explains the missing context, the model revises. Repeat. That back-and-forth is the cost. You pay full expert attention on every round, plus the rounds. On work like this, the model doesn't multiply the expert. It queues behind them.

Sitting inside that loop, our real goal became clear, and it's the lens for everything that follows in this post: decrease the context the LLM needs, and decrease the burden on our engineers. Legacy refactoring maximizes both. To be fair to our past selves, it's not that the behavior was undocumented. Every RCA is documented. Ten years of them. But that's exactly the problem. The documentation itself is a huge legacy, far more than fits any context window, with no way for a model to know which page matters for the line it's about to change. So the model works under-informed anyway, and the engineer pays for every piece it didn't have. Whatever approach we landed on would have to invert both sides of that equation.

Meanwhile, the coupling cost wasn't holding steady while we worked.

The Rewrite Math

Every engineering leader knows why "let's just rewrite it" is usually a failing project. The timeline is long. You maintain two branches, the rewrite and master, while feature requests and customer escalations keep landing on master. Priorities collide, the rewrite branch starves, and eighteen months later someone quietly kills the project. We knew that script. We had no intention of starring in it.

But the math had a new variable. The thing that kills rewrites, the sheer volume of code to produce, is exactly the thing AI had become good at. On greenfield code you can run TDD from the first commit, test every part extensively, and validate each new section against its integration with the previous one before moving on. That's the terrain where AI is a genuine force multiplier. On legacy code, we had months of scar tissue proving the opposite. Every change touches behavior scattered across a decade of code and docs, and you pay for it in review.

Which left us holding a contradiction. The rewrite needed AI's speed to be viable, and our own experience with AI had been the grind you read about above.

The bet we made: the problem was never the tool. It was the terrain, and the direction of use. Point the agent at a greenfield target. Stop asking it to infer intent from tangled code. Hand it the intent, written down, and let it execute. That phrase, the direction of use, became how we talk about AI internally: same tool, opposite results, depending on which way you point it.

We didn't ask the team to take that bet on faith. We got proof first.

Hack Week

In February 2026, a hack week changed the direction. More importantly, it changed the team.

A small group took the inverted approach and ran it flat out: spec-first, agent-executed. Human owns the design and the research prompt. Agent owns the extraction and the scaffolding. Human reviews both. Greenfield target.

In one week, they built a complete, shippable product with AI.

Not a demo. Not a prototype with three happy paths. A product you could put in someone's hands. No argument I could have made in a planning meeting would have done what that artifact did. The team had spent months watching AI produce plausible-but-wrong output on legacy code. You cannot talk an experienced engineering team out of their own evidence. You have to show them different evidence.

There's a book I return to constantly: Switch, by Chip and Dan Heath. Their model says change fails when you only address the rational mind (the Rider) and ignore the emotional one (the Elephant). Their first prescription is to find the bright spots: don't argue about what's broken, find what's working and clone it. The hack week was our bright spot. It also did what the Heaths call shrinking the change. Nobody was asked to believe "we can rewrite the SDK with AI." They were shown "we built one product in one week." The big change became a repeat of a small, witnessed success. When the ask is that concrete, the Elephant moves.

Same team. Same model family. Opposite direction of use, and a room full of engineers who had seen it work with their own eyes. That flip changed the economics of the entire project.

The Destination

Before scaling the workflow, we did the thing most rewrites skip: we agreed on the end state, precisely.

Not "cleaner code." A different kind of thing entirely: stop building products on a product. Build a platform.

  • One kernel. Collectors plug in.
  • One source of truth, so products can't disagree.
  • Capture once, serve many. One capture feeds every product view, including ones that don't exist yet.
  • Governed from the backend. Any piece tuned or turned off per customer, no app update.
  • OpenTelemetry-aligned by design, because an agent can't debug from six disconnected feeds.

The picture says it in one look. The frontend is thin on purpose: producers are pure collectors, no enrichment, just capture and forward. Everything smart happens once, behind the gateway. The client enriches events with debugging data and contexts, one DataHub aggregates the debugging data, one store aggregates the contexts, and one transport carries it all to the backend. No product builds its own copy of any of this again.

One more thing about how the destination was drawn. One of the best things you can do when working with AI is put a talented engineer in the driver's seat. So our best SDK engineers worked on this together, the people who had personally carried v1's modules for years. AI amplifies the judgment of whoever is steering it. It does not replace it.

One architectural choice is worth calling out before the workflow: we followed the dependency injection approach from Mobile System Design by Tjeerd in 't Veen, an amazing book that shaped how we wired the whole system. In short: vanilla DI, no frameworks, no magic containers. Dependencies are created outside and injected in, the hierarchy flipped inside out, deepest components initialized first. Every type knows only its direct dependencies, never its transitive ones. Modules expose one small static setup function instead of leaking their internals. Interfaces are kept deliberately rare: concrete types everywhere, abstraction only at the true boundaries like network and storage. Singletons avoided, because "there will only ever be one" is a promise codebases never keep.

The payoff is exactly what the book promises. When abstraction exists only at the lowest boundary, you can mock at the very bottom of the stack and run everything above it as real production code. You test the largest possible surface of what actually ships instead of a Swiss cheese of mocks proving that mocks work. It pays off twice in an agent-executed workflow: the tests are trustworthy because they exercise real code, and the wiring is legible because there's no framework magic for an agent to misread. This made our life in testing much, much easier.

The Workflow, Concretely

"We use AI" is not a workflow. Ours, in brief: every module moves through five phases (Research → Spec → Plan → Implement → Demo), one owner per module, end to end. Around 29 modules, 45 written specifications, 35 architecture decision records. Each phase runs on its own agent command with a clean context. A spec isn't done until it passes a 10-point Definition of Done, because if an agent is going to write the code, the spec is the contract. AI review runs before human review. Four quality gates with named owners stand between research and the feature flag. If you've read anything about spec-driven development this year, none of that will surprise you.

The two parts you won't find in the guides:

The research command builds the old behavior first. Before designing anything, it pulls every documented support issue, every RCA, every feature we implemented before, so the agent designs with the past in view instead of rediscovering it in production.

A context engine feeds every phase. We built it on top of Cognee, a graph-based LLM memory framework, and fed it everything the organization knows: every issue and RCA we've documented, every support case, every feature we shipped, every ADR we agreed on during the rewrite, every behavior change we made. Research draws on it to reconstruct old behavior. Because it's a shared engine rather than a one-off prompt, the same context comes back where it pays off again: reviewing the spec, reviewing the code, and testing.

What Slowed Us Down, and How We Unblocked

The workflow above reads clean. Getting there wasn't. Here are the walls we hit, in the order we hit them.

Decision velocity. The bottleneck at this stage wasn't code, and it wasn't reviews. It was decision-making. We spent enormous energy going back and forth on proposals, technical and non-technical, and the root of it was an honest mistake: everyone wanted the best decision, the one that fixed the status quo for good. We were treating every choice as permanent, forgetting the thing we had just built: an AI-friendly project we could iterate on again and again. When regeneration is cheap, a good-enough decision today beats the perfect decision in three weeks.

The fix was about people, not process. We delegated decision-making to our talented engineers and empowered them to make the call, with us as the fallback only when something is critical. We review the state of the architecture regularly to spot issues. What we find is either postponed to the next stage or addressed immediately if that's applicable. Honestly, this unblocked us, and it gave our engineers the room to innovate and land better decisions than the back-and-forth ever produced.

Not resurfacing old mistakes. A rewrite's quietest failure mode is faithfully reintroducing bugs that took a decade to find the first time. This is what the context engine is really for: production history as part of the agent's context, backed by a behavioral registry extracted from years of git history, roughly 1,066 distinct behavior patterns the new SDK had to honor. When work touches a path with a history, that history is in the review context. The new code doesn't get to be innocent of the old code's scars.

PR reviews. To understand why this one hit hardest, you need to see how we were executing. We were optimizing for speed. We broke the whole vision into small modules and assigned a module per engineer, per platform. Each owner researched their module, surfaced the decisions it required an ADRs (Architectural Decision Record), and asked another engineer to review them. We reviewed in the middle, not to approve every choice, but to spot patterns across modules, keep coaching the team back to the goal of the project, and catch issues early enough to act on them.

That structure was fast, and it exposed its own problem. Modules depend on the kernel, and documenting the kernel was never going to be enough, because the kernel itself was still changing. When it changed, every module depending on it could change too, especially service modules like the data layer, which everything touches. That meant real rework, in the middle of a rewrite that was supposed to be buying us time.

All of it, one module per engineer, ADRs flying, a kernel still evolving, rework landing, poured into a single funnel: the pull request.

The PR Review Problem

When agents write the code, review becomes the job. Our merge volume went up dramatically, and our review capacity didn't, because review capacity is made of engineers, all of them, not just the senior ones, and engineers don't scale by prompt.

What it looked like from inside: our staff engineers were pulled into review at a rate that pushed their own authorship toward zero. Review discipline that worked at the old team size didn't survive the new PR volume. And AI makes it trivially easy to produce large diffs no human can hold in their head. Code generation had become industrial while review was still handcrafted.

What we changed:

  • Small PRs by design. During the plan phase, big modules are broken down into small phases, so PR size stays reviewable by construction and review stays streamlined instead of heroic.
  • AI review as the first pass, always. Per-platform review-rules checklists, and a review skill that checks PRs against repo standards and against the module's spec, because "matches our conventions" and "matches what we agreed to build" are different questions.
  • Dedicated review slots on the calendar, treated like build infrastructure, not like a favor. In each slot the team agrees on which PRs go under review, the PR owner gives an introduction to the change, what it does and why, and reviewer assignment is agreed on the spot, so no PR sits waiting for a volunteer.
  • Review authority distributed earlier. Concentrating sign-off in two people is a bottleneck.

On tooling: we use an AI code-review product, as part of that first automated pass. Before a PR reaches a human, the PR owner runs the context engine against it, validating that the change didn't break anything in the PR's own scope or any old behavior the system has to honor.

One confession: all of this did not completely solve the review problem. It decreased the burden, meaningfully, but review remains the tax an agent-executed workflow costs. We treat it as a constraint to keep managing, not a problem we've closed.

The Wiring Wall

A few months into full-team execution, we audited what was actually merging. Roughly 17% of merged volume was wiring and integration work, not new capability. Modules were landing beautifully, and meanwhile two of our most senior engineers, one per platform, were spending a big part of their time making sure the wiring was integrated well and reviewing the wiring and integration PRs. The people whose judgment we needed most on architecture were consumed by glue work nobody had assigned.

The diagnosis, once we said it out loud: our architecture had nodes but no edges. Specs described modules. Nothing described connections. Agents are exceptional at generating nodes. The edges, activation, configuration, sessions, backend contracts, were being paid for by humans, off the books.

If you take one thing from these walls: AI moves the bottleneck, it doesn't remove it. Ours moved from writing code to deciding, reviewing, and integrating it. Plan for where the bottleneck lands, not where it used to be.

Here's the wall underneath all the other walls. RPI assumes you can plan enough upfront to avoid changing course later. You can't. Even with the most powerful model and the most thorough planning, something always shows up after you start: a missed detail, a new requirement, a limitation in another part of the system. No model fixes that, because it's not a planning problem.

So the fix isn't more research upfront. It's a system designed to change, on purpose, in small pieces, not one that absorbs change as rework when it happens anyway.

So we didn't leave the lessons as retro notes. We rebuilt the flow around them.

The Workflow, Rebuilt

Each module owner spent multiple days researching before writing a line, and more days reviewing the research output. Old behavior, backend contracts, kernel APIs, sibling modules. Then implementation started, and the ground moved. The kernel evolved. A sibling module landed and changed a contract. A decision made inside another module's implementation invalidated an assumption baked into yours. Research done at the start was stale by mid-implementation, and the spec built on it was stale with it. We were paying the full upfront research cost per module, the review cost on top, and still paying the rework cost after.

Here is the new version of the workflow, where every stage exists because a specific wall taught us it had to. Stage by stage, with the why.

Closed product squads on layered, versioned contracts. Kernel at the bottom, a senior-owned product infrastructure skeleton on top of it, modules plugging into that. Each layer is a versioned contract the layer above builds against. A contract change starts on a new version, gets agreed, gets announced to everyone building on it, and migrates on cadence. Never silently. Why: the kernel-churn rework taught us that documenting a moving foundation isn't enough. Modules need something stable to integrate against, and changes need a broadcast, not a diff.

Spec, skeleton, and integration tests ship as one deliverable, behind a hard merge gate. A module's first artifact is its spec, plus a full skeleton wired end-to-end through the system as a thin slice, plus integration test cases for the whole module, generated against a fixed backbone of failure classes, the same checklist for every module, so coverage is systematic rather than inspired. Our bet on the skeleton: it lets us review code quickly and align on direction easily, because the whole shape of the module is visible in one small running slice, so course corrections happen before the volume arrives. Nothing else proceeds until that wired slice is reviewed and merged. Why: nodes without edges. Wiring is now the first merged thing, not the last discovered debt, and the senior reviews one thin slice instead of drowning in every subtask.

Every module is broken down into small phases, with a cap on work in progress. Each phase states what it does and the problem it solves, carries its own estimate and deadline sized from project history, and review is planned as a phase of its own. Before any production code, a phase starts with its own spec and its failing tests, shown red. Why: small phases keep PRs reviewable by construction, verification gets designed before generation instead of reverse-engineered after it, and review takes real hours. Plan it, don't absorb it into someone's evenings. The WIP cap is there because ten half-integrated modules are worth less than three integrated ones.

After green, a mutation stage. The changed code is systematically mutated: conditionals flipped, lines removed, operators swapped, null checks dropped. Then the tests re-run. Every mutant must be killed by a failing test. A survivor is a hole in the tests, fixed before the PR opens. Why: when an agent writes both the code and the tests, a green suite proves nothing until you've proven the tests can fail. A suite that can't detect a planted bug isn't a safety net. 

PR review runs a chaos pass, including re-injecting our old bugs. Beyond standard review, the reviewer mutates the diff's logic, removes lines, and re-injects known bug patterns from our production history: threading races, cached-config bugs, past incident signatures. Then it reports what the tests caught and what escaped. Escapes become filed issues and new tests. Why: the context engine holds a decade of scars, and this stage checks the new code against them actively instead of trusting that history was absorbed.

Every phase closes with a receipt, and every module closes with developer feedback. The session that did the work reports how it went: how many corrections, where the model drifted, how the PR review landed. But numbers alone don't improve a workflow, so every change inside the LLM loop gets feedback or micro-feedback from the developer driving it. As part of closing a module, each developer shares qualitative feedback from all the sessions they worked through: where the model fought them, which instructions it kept missing, what they'd change in the flow. All of it feeds a weekly rollup that tunes the commands, the repo context, and how we size scope. Why: the workflow itself is now under observability, quantitative receipts plus qualitative developer feedback, so the whole team improves the flow together, through the same loop the code does.

Product flow maps, regenerated as modules land. Everything the process produces, specs, contracts, wiring, gates, gets compiled into the product's end-to-end business flows in plain language, with gaps flagged: dead-ends, unwired paths, gates nobody sets. Why: edges stay visible continuously instead of being audited after the damage.

One more thing, to be honest about it: this is not the final workflow. It's the current one. The models get better with every release, and every improvement moves the bottleneck again, which means the workflow has to be rebuilt continuously, not designed once. 

Treat your AI development process the way you treat your architecture: versioned, reviewed, and expected to change.

This Is Not Just Our Story

While we were living this, the industry data caught up. Google now reports 75% of its new code is AI-generated. Meta set targets for most engineers to cross that line this year. And the 2026 benchmarks make the same point we learned the hard way: AI-generated PRs wait about 4.6x longer to be picked up for review, and teams using AI feel roughly 20% faster while shipping measurably slower, because the constraint moved to verification and most orgs never redesigned for it.

That's the real shift for engineering leaders. Your generation capacity is no longer the thing to design around. Your verification capacity is. Our engineers didn't become prompt-writers. They became spec authors, agent orchestrators, and verifiers. The senior ones became the people who encode judgment into the system, in specs, review rules, and context files, instead of dispensing it one review at a time.

What I'd Tell You if We Were Having Coffee

  1. Before committing to a refactor, name the coupling you're assuming you can untangle. If you can't name it in specifics, you can't untangle it.
  2. Legacy behavior extraction is a product, not a spike. Give it an owner, a timeline, and gates.
  3. When AI fails, change the direction of use before you put the tool down. We spent months on RPI, external memory, and rule enrichment before the real unlock: inverting who owns intent. The tries weren't wasted. They told us exactly what the agent couldn't do.
  4. Don't argue for the change. Find a bright spot and let the team witness it. One shipped hack-week product moved our team further than a quarter of planning decks.
  5. Specs are the new senior work. The 10-point Definition of Done did more for our code quality than any model upgrade.
  6. Budget for decisions, reviews, and edges. Whatever share of effort you've allocated to everything that isn't generation, you're probably low. We were.

Back to Late 2025

The agent we once babysat through implementations we could have written faster ourselves is part of the system now shipping a kernel that runs in production today.

The first milestone is live: the new kernel running silently alongside v1 in production, 16 subsystems in, measured across 2.7 million cold starts. 59 threads down to 7. 279K lines down to 48K. A quarter of the binary size. Typical startup 0.59s to 0.24s, 2.5x faster while doing more work. That's before the remaining collectors land, and they cost milliseconds, not tenths.

On speed: I won't publish a multiplier, because there's no clean baseline. Nobody ran a control team building the same SDK by hand. What I can share is what's built: 16 of roughly 29 planned modules, live in production on both platforms. Judge the pace from that, knowing the same team also spent months failing at it first.

Nothing about our ambition changed. Everything about the direction changed.

Most teams evaluating AI right now are living our late 2025: running RPI against a tangled system, drowning in plausible-but-wrong output, concluding the technology isn't ready. The question was never whether AI can understand your legacy code. It's whether your team can write down what you actually want built.

We couldn't, at first. Learning to was the rewrite.

This is the first post in a series on AI-native engineering from inside a production SDK rewrite. Next up: the spec-first workflow in full detail, with the commands, the gates, and the 10-point Definition of Done.

Stop feeding agents a shadow of reality.

Request a demo
Recognised by the teams who use it most
G2 Momentum Leader badge for Mobile Crash Reporting categoryG2 Leader badge for DevOps categoryG2 High Performer badge for Enterprise DevOps category

Frequently Asked Questions About AI-Native Engineering

Can AI understand and refactor a legacy codebase?

Not reliably. Legacy code encodes decisions without the reasons behind them, and an agent cannot infer intent that was never written down. Pointing an agent at a tangled, decade-old system produces plausible-but-wrong output that costs more review time than it saves. The unlock is not a better model, it is writing the intent down first.

Why does AI coding work on greenfield projects but fail on legacy code?

On greenfield code, correctness is defined by a spec you control, so you can test each new section as it lands and the agent becomes a genuine force multiplier. On legacy code, correctness is defined by ten years of accumulated behavior scattered across the codebase, so every change has to be verified by a human expert. Same tool, opposite results, depending on the direction of use.

What is spec-first (spec-driven) development?

Spec-first development means a human owns the design and writes a precise specification before any code is generated, and the agent executes against that contract. The spec becomes the deliverable that carries the most senior engineering judgment. If an agent is going to write the code, the spec is the contract, and it is not done until it passes an explicit Definition of Done.

Why is verification, not code generation, the new bottleneck?

When agents write the code, review becomes the job. Generation capacity is now effectively unlimited, but review capacity is made of engineers and does not scale by prompt. 2026 benchmarks show AI-generated pull requests wait roughly 4.6x longer for review, and teams using AI feel about 20% faster while shipping measurably slower, because the constraint moved to verification and most teams never redesigned for it.

How do you stop an AI rewrite from reintroducing old bugs?

Feed production history into the agent's context. A behavioral registry extracted from years of history captures the distinct patterns the new system has to honor, so when work touches a path with a known incident, that history is in the review context. A chaos pass then actively re-injects past bug signatures into the diff and confirms the tests catch them, instead of trusting that history was absorbed.

Does AI actually make engineering teams faster?

Only if you redesign the workflow around it. AI moves the bottleneck, it does not remove it. Teams that adopt agents without rebuilding for decision-making, review, and integration often feel faster while shipping slower. The teams that gain speed treat their AI development process like their architecture: versioned, reviewed, and expected to change.