Specs Belong in the Repo: Why We Made OpenSpec the Default
The hardest part of spec-driven development was never the tooling. It was answering one question honestly: why should a specification live in the codebase when it will be out of date in six months? Here's how we got there, and why we landed on OpenSpec.
The argument I kept having with my team was never about AI. Nobody needed convincing that the agents were useful. That debate ended a while ago. The argument was about a folder of markdown files, and whether it had any business being committed alongside the code.
I've spent more of the last year on that question than on any model, prompt or IDE. We eventually standardised on OpenSpec as the default way we work. Getting there was less about picking a tool than about winning one specific objection.
The problem wasn't the agent
Give the same ticket to three engineers with three agents and you get three different features. Not three different implementations. Three different products. One handles the empty state, one invents a retry policy nobody asked for, one quietly changes the contract of a shared endpoint. Every one of them passes review, because review is where you check that code works, not where you discover you all built different things.
That's not a model failure. The model did exactly what it was asked. The failure is upstream: the intent was never written down anywhere a machine or a colleague could read it. It lived in a Slack thread, a stand-up, and someone's head.
When generating code was the slow part, ambiguity was survivable. You found out you'd misunderstood after two days and adjusted. When generating code takes minutes, ambiguity compounds at the same speed as everything else. The bottleneck moved from typing to deciding, and we hadn't moved any process to match.
What we tried before OpenSpec
The first attempt was a shared prompt library. It died quietly. Prompts are personal, they drift, and nobody reviews them. Within a month everyone had their own fork in a note-taking app.
The second was one large context file per repo describing how we build things. Better, and I still recommend it, but it's a description of the system, not of the change in front of you. It tells the agent our conventions. It says nothing about what we actually agreed to build this week.
Then we looked at the heavier spec-driven frameworks. They're thorough, genuinely well designed, but they come with phase gates, their own setup, and a shape that assumes you're starting something new. Most of our work is brownfield: a decade-old service, a small change, a deadline. Anything with a mandatory ceremony at every stage gets skipped the first time someone is under pressure, and once it's skipped once it's optional forever.
Why OpenSpec stuck
Three reasons, in order of how much they mattered.
- The loop is small enough to actually run. Propose, apply, archive. You write a proposal with the requirements and a task list, the agent implements against it, and when it's done the change gets archived. Three steps is something a tired engineer will do at 5pm. Nine steps is not.
- It's fluid, not gated. You can go back and change the design halfway through implementation without violating the process. That sounds minor. It's the difference between a practice people follow and a practice people route around, because real work always doubles back.
- It doesn't care which agent you use. Half my team is in one assistant, half in another, and that split changes every quarter. The specs are plain markdown in the repo, so nothing we write is hostage to a vendor's roadmap. We've already changed tools once since adopting it and lost nothing.
The objection that took longest to answer
None of the above is what people pushed back on. The question I got, over and over, from the engineers whose opinion I respected most, was this: why does a specification need to live in the codebase, when in twelve months it won't describe what the code does anymore?
It's a fair question, and it comes from scar tissue. Everyone has opened a design doc, believed it, and shipped a bug because the code had moved on two years earlier. Documentation that lies is worse than no documentation. If specs in the repo were just design docs with a shorter commute, the objection would be right.
What changed my answer was noticing that staleness is a location problem before it's a maintenance problem. A spec in a wiki goes stale because nothing forces it into your eyeline when you change the behaviour it describes. Nobody's pull request has ever been blocked by a Confluence page. A spec in the repo sits in the diff, next to the code, in front of a reviewer, at the exact moment it becomes wrong. That's not a guarantee it stays current, but it's the only place where staying current is even structurally possible.
The second half of the answer is that not everything is meant to survive. A change proposal is deliberately disposable: it captures why we're doing this, what we agreed, and what the tasks are, and then it gets archived when the work lands. It isn't pretending to be permanent truth. The permanent part is the small set of specs describing how the system behaves now, and those are updated as a side effect of the work, not as a documentation chore somebody schedules for a quiet Friday.
So when someone tells me their specs have rotted, my honest read is that the archive step isn't happening. That's a discipline bug, not an argument against writing them down. And the failure mode is visible: a stale file in a repo gets noticed and deleted. A stale page in a wiki gets cited in a design review three years later by someone who wasn't there.
What actually changed
The most valuable shift wasn't speed. It was that disagreement moved earlier. We now argue about scope and edge cases in a proposal that took twenty minutes to write, instead of in a review of eight hundred generated lines that someone is emotionally invested in. Rejecting a paragraph is cheap. Rejecting an afternoon is not, so people don't, and mediocre decisions ship.
The second thing: onboarding got dramatically easier, for humans and agents alike. A new engineer reading the archive gets the reasoning behind decisions, not just the outcome. That was always the promise of good documentation, and it's the first time I've seen it survive contact with a real deadline, because writing it is now the fastest path to the code rather than a tax paid afterwards.
Where it's still awkward
I'm not going to pretend this is free. Three honest caveats.
- Small changes don't deserve a proposal. Forcing one on a two-line fix is exactly the bureaucracy people accused me of importing, and it burns credibility you need later.
- It needs a strong reasoning model to be worth it. On a weaker model you get a beautifully structured spec and an implementation that ignores half of it, which convinces everyone the process is the problem.
- The archive step is the one nobody wants to do, and it's the one holding the whole argument up. If you only enforce one thing, enforce that.
Default, not mandate
We didn't mandate it. Mandates produce compliance artifacts: specs written after the fact to satisfy a checklist, which is the worst possible version of this. We made it the default. It's what a new repo is set up with, it's what I do when I pair with someone, and if you want to skip it on a change you just skip it and nobody files a ticket.
Adoption came from the same place it always does: people tried it on something painful, it worked, and they came back on their own. The tooling debate resolved itself once the practice made sense. That order matters. I spent a long time getting it backwards.
The models will keep getting better, and none of that changes the constraint. If you can't say precisely what you want, a faster agent just gets you to the wrong thing sooner. Writing the spec is the work. Everything after it is increasingly automatable, which is exactly why it belongs in the repo, under review, with the code.
