Spec-Driven Development in a Real Codebase
Twelve months ago "spec-driven development" needed an introduction. Today GitHub's Spec Kit sits above 120,000 stars, OpenSpec above 60,000, AWS built a whole product around the workflow with Kiro, and search interest has roughly quadrupled year over year, with "what is spec driven development" among the fastest-rising questions (Google Trends). The definitional part is quick. The interesting part is what happens when the workflow meets a codebase that already exists.
What spec-driven development is
Spec-driven development puts the specification first and makes it the artifact the code is generated from. You write down what to build and why; an agent turns that into a technical plan; the plan becomes an ordered task list; implementation comes last, executed task by task with the spec as the reference for every decision.
The tools agree on this pipeline almost exactly. Spec Kit walks it as slash commands: /speckit.specify for requirements and user stories, /speckit.plan for the technical approach, /speckit.tasks to cut the plan into actionable pieces, /speckit.implement to execute them. OpenSpec runs the same loop as change folders in plain markdown: propose a change, apply it, archive it when merged. Kiro phases it as requirements, then design, then sequenced tasks. Three independent teams, one conclusion: agents produce better software when the thinking is written down before the code, in a form a human can correct.
Part of the surge is a correction. The industry spent 2025 vibe coding, and spec-driven development is the swing back toward writing intent down before generating anything from it.
Greenfield is the demo, brownfield is the job
Every spec-driven demo starts from an empty directory, for a good reason: on greenfield the spec is the complete truth about the system. Nothing contradicts it, because nothing else exists.
Most engineering happens somewhere else. The repository is five years old and enforces its opinions through ten thousand existing symbols. A spec describes the future; in a brownfield repo the plan step also has to describe the present, and describe it accurately: which symbols own the behavior being changed, what conventions the surrounding code follows, which tests already stand over the area. The pipeline has a slot for this work (Spec Kit's plan phase, OpenSpec's proposal, Kiro's design doc). What it lacks is a reliable way to fill the slot: the planning agent surveys the codebase the way any agent does, by searching and reading until the window fills up, and the survey's blind spots become the plan's blind spots.
How ungrounded plans fail
The failures are specific and they repeat. A plan schedules "add a retry helper" because the survey never surfaced the one in lib/net, and the codebase ends the week with two. A task says "change the signature of resolveTenant" and no task mentions its callers, because nobody enumerated them; execution discovers each one as a compile error, and the task list's tidy ordering dissolves mid-run. Two tasks look the same size in the document, yet one touches a symbol with three callers and the other a symbol with sixty and a downstream set that crosses a service boundary; the estimate, the ordering, and the review plan would all be different if that number had been on the page. We have written about the underlying question before: what breaks when you change a function is the question every task in a plan silently contains.
What makes these failures expensive is where they surface. A wrong guess during planning costs a sentence to fix. The same wrong guess during implementation costs a rewrite of every task built on top of it, and agents are much better at following a plan than at noticing the plan itself is broken.
The plan step is a survey
For each task, a grounded plan needs four facts: the symbols the task touches, who calls them, what sits downstream of a change, and which tests cover the area. Each fact is a query with an exact answer. find_node resolves a symbol to its definition and signature. relate with kind callers enumerates the call sites; with kind blast_radius it walks the downstream set, the number that sizes a task before anyone commits to it (the case for that number is its own post). list_tests_for returns the tests standing over a symbol, which tells the plan whether a task inherits a safety net or must build one.
When the planning agent can run these over MCP, the plan phase stops being a reading exercise. /speckit.plan or an OpenSpec proposal comes out with file paths, line bounds, caller counts, and a named impact set attached to every task, and reviewing the plan means checking claims against a graph instead of trusting a survey nobody can inspect.
The loop closes at the other end. After implementation, diff_impact takes the finished diff and reports which symbols, tests, and routes it actually reaches: the mechanical check that the change stayed inside the blast radius the plan declared.
What this looks like in practice
A concrete pass through Spec Kit's flow with a graph attached. The spec says: users should be able to cancel a booking within 24 hours without a fee. During /speckit.plan, the agent resolves the booking cancellation path, pulls callers on the fee-calculation function it intends to change, and finds a second consumer in the invoicing module that the spec never mentions. That discovery happens in the plan document, where it costs one clarifying question to the spec's author. The tasks that come out name both call sites, cite the three tests that cover the fee path, and flag the invoicing task as the risky one because its downstream set is four times larger.
None of that required the agent to be smarter. It required the plan step to have access to the same facts a senior engineer would check before signing off on the estimate.
Known limits
The graph contributes no requirements. What to build and why stays a human document, and no traversal makes a bad product decision good. Archived specs drift the moment the code moves again, so treat them as history and the graph as the present tense. And parsed edges have blind spots (dispatch through string keys, reflection, handlers wired by configuration), so in codebases that lean on those patterns the impact set understates reality and the plan should say so out loud.
Symvanta serves the survey half of this workflow: a live code graph over MCP that a planning agent queries mid-spec, branch-aware and cross-repo. If you run Spec Kit, OpenSpec, or Kiro against a codebase that existed before this year, the plan step is where the graph pays for itself, and seeing it run against your own repository takes about fifteen minutes. Book a 15-minute demo