Every spec-driven tutorial starts with an empty folder. You run a command, describe a feature, and watch an agent build it clean. It is a lovely demo and it describes almost none of the software that actually exists.
Your real situation is a repository with four years of history, patterns nobody wrote down, and a handful of modules everyone is afraid to touch. You cannot stop shipping to write specs for all of it, and you cannot ask an agent to work against a contract that does not exist. So spec-driven development gets filed under “great idea, wrong codebase” and you go back to describing changes in a chat and hoping.
That framing is the mistake. You do not add specs to an existing codebase by specifying everything. You add them where the next change is about to land, and you let coverage grow from there.
Why the greenfield playbook fails here
On an empty repo, the spec comes before the code, so intent is captured while it is still cheap. On an existing repo, the code came first and the intent behind it is scattered across commit messages, closed tickets, and people’s memory. Writing a spec now means reverse-engineering a decision that was made two years ago by someone who may have left.
That is real work, and it does not parallelize into a single heroic sprint. Trying to spec the whole system up front produces a pile of documents that are already drifting by the time the last one is written. You end up with spec drift on day one, which is worse than no specs at all, because now the documents actively lie.
The unit that works is the change, not the system. You do not owe the codebase a complete specification. You owe the next change a contract.
Spec at the boundary of the next change
Here is the move. When a change is about to touch a piece of the system, write the spec for that piece, scoped to what the change needs, and no wider.
Say you are about to modify the coupon logic in checkout. Before the agent starts, you write down what checkout does today that must stay true, what this change is allowed to alter, and how you will recognize a correct result. That is a spec. It covers checkout, not the whole app. It took twenty minutes because you only specified the surface the change actually touches.
Now the agent has a contract. It knows the invariants it is not allowed to break, which is exactly the information that stops it from being locally correct but globally incoherent, fixing your feature while quietly breaking an adjacent one. The spec did not need to cover the universe. It needed to cover the blast radius.
Repeat this on every change, and something useful happens: the parts of the system that change often accumulate specs fast, and the parts nobody touches stay unspecified, which is fine, because unspecified stable code is not the thing hurting you.
There is a second payoff that is easy to miss. The first time you spec the surface of a change, you pay the reverse-engineering cost once. The next change to that same module starts from a contract that already exists, so you are editing a spec instead of reconstructing intent from scratch. The modules that churn are exactly the ones where this compounding matters most, and they are the first to cross the ratchet. You are front-loading the archaeology on the code that will make you dig again anyway, and skipping it on the code that never asks.
Read the code into a first draft
You do not have to write these specs from a blank page. The code is already the most accurate description of what the system does, it is just in the wrong form. An agent can read the module you are about to change and produce a first-draft spec of its current behavior: the inputs it accepts, the invariants it seems to hold, the paths it covers.
That draft will be wrong in places, and being wrong is useful. Correcting a draft is far faster than authoring from nothing, and every correction you make is a decision you are recovering from the code and writing back down. This is the practical version of reading a repo into product artifacts: the repository stops being an opaque wall and starts yielding the stories, criteria, and constraints buried in it.
For this to work, the agent needs to actually understand the shape of the code, not just grep it. That is the argument for codebase maps rather than bigger prompts: a model of how the modules relate gives the draft-spec far better odds of naming the right invariants.
Keep the ratchet turning
The goal is not one hundred percent coverage. The goal is a ratchet that only turns one way: every change leaves behind a spec, so the specified surface grows monotonically and never shrinks. You do not need a freeze, a rewrite, or a coverage mandate. You need a rule that a change does not ship without a contract for the surface it touched. That rule is cheap to state and it fits inside the work you were already going to do, because you had to understand the surface to change it safely anyway. The spec is just that understanding, written down where the agent and the next person can both read it.
Kept up, this produces exactly the coverage you want. Hot paths that change monthly become well-specified because they keep passing through the ratchet. Cold code stays bare, and that is correct, because it is not the code generating your incidents. This is the lightweight version of spec-driven development, where the spec earns its place per change instead of arriving as a bureaucratic tax on everything.
Where PaellaDoc fits
Adding specs to a living repo is exactly the situation PaellaDoc is built for. It can read an existing codebase into a local model, draft the behavior of the module you are about to change, and let you correct it into a real contract before the agent starts. That spec then stays attached to the code that implements it, so the next time the module changes, you are editing a contract that is already there instead of reverse-engineering the whole thing again.
You do not stop the world to adopt spec-driven development. You attach a contract to the next change, then the one after that, and let the specified surface grow at the speed the codebase actually moves.
FAQ
Do I have to spec my whole codebase before this helps? No. That is the failure mode. You spec the surface of the next change, ship it, and repeat. Coverage grows on the parts that change often, which are the parts that need it.
What about the stable modules nobody touches? Leave them unspecified. Stable code that never changes is not generating your incidents. Spend the effort where change is actually landing.
Can an agent write the first spec for old code? Yes, as a draft. It reads the module and describes its current behavior, and you correct the draft. Fixing a wrong draft is much faster than writing from a blank page, and every fix recovers a decision the code was hiding.