Agentic Software Development, But Done Well (with SDD)
Vibe-code the perfect app, one prompt at a time? That's not how it works, in my view – and here I'll show you how agentic software development is done properly.
Spec Driven Development is not vibe coding
That’s how I see it. Even if, after the first few implemented features, I stop reading the code. Of course I first look at what Claude Code or Codex made of my requirements, and fine-tune my rule files here and there. But then comes the point where I notice that the architecture is being respected and the quality is more than sufficient – for everything that gets generated for me.
So where is the difference? In contrast to vibe coding, as I know it from friends and colleagues, where you keep sending prompts to the agent until the desired result is reached, this is about a sustainable process that you can adjust at any time. In this process, SDD frameworks like OpenSpec produce a change history and further artefacts in the form of text files. That doesn’t sound very exciting at first, but I think I can convince you that it makes an enormous difference.
Context is King
You hear that all the time lately. Context here means that a prompt should send enough of the right information to an LLM – but not too much – in order to get a good answer that isn’t hallucinated.
Thanks to internal memory systems, the systems from providers like OpenAI or Anthropic now know a lot about their users. But that can cause problems too, for instance when the agent mixes up two projects and proceeds from the wrong assumptions. Or blends private matters with professional ones.
This is exactly where the artefacts – the text files mentioned above, which are clearly assigned to one project – make the big difference. In my Claude Code CLI, for example, I have just disabled the internal memory function, because more than once it built on information that belonged to another project or was badly outdated.
Context is also created with every prompt from the user; but end the session and it’s lost. Of course you can also write context into your CLAUDE.md or AGENTS.md, or into other files or a database the agent has access to. With SDD, though, you additionally make sure those are kept up to date automatically.
The SDD process and its artefacts
I’m describing the OpenSpec process here. Other frameworks such as GitHub Spec Kit or BMAD differ in places, but the basic idea stays the same.
The process is made up of the following functions.
1. Propose
We start with the first change – a spec change, as it’s called in OpenSpec. That could be a request such as “Build me a web app where I can play chess against an AI”. This is where the framework starts planning with you. It asks questions: which technology should be chosen, is a backend necessary, which AI should be the chess opponent. This feedback round is comparable to classic stakeholder management and is not meant to replace it – in fact it can ideally be run together with the stakeholders. The goal is to create a shared understanding, for everyone, of what is actually going to be built. And by “everyone” I also mean whoever might implement it later, be that AI agents or human developers. On the requirements side, other parts of the company join in, such as the marketing department or someone from business intelligence.
This process step first produces an artefact called proposal.md, in which all of this is summarised. From that, a design.md, a tasks.md and at least one new spec are then created automatically. More on that in detail later.
It may also be that the framework suggests straight away implementing the change as several spec changes. With the example chosen above, that would be very likely and also sensible.
Once all generated artefacts have been reviewed for content and adjusted where necessary, the change is ready for implementation.
This is what a requirement in a spec looks like. The example comes from the authentication of another project:
### Requirement: Refresh tokens rotate and detect reuse
The system SHALL persist only hashes of refresh tokens, SHALL bind them to an
account and token family, SHALL rotate the presented token on every successful
refresh, and SHALL revoke the entire family when an already-rotated token is
presented.
#### Scenario: Rotated refresh token is replayed
- **WHEN** a client presents a refresh token that was already replaced
- **THEN** the system revokes the token family and returns an unauthorized
problem without issuing credentials
#### Scenario: The same refresh token is presented concurrently
- **WHEN** two requests present the same valid current refresh token at the same time
- **THEN** at most one rotation succeeds and replay handling revokes the family
before the losing request returns unauthorized
2. Apply
This process step is the implementation. Since we’re talking about agentic development here, I assume this is also handled by another AI agent. The “thinking work” was already done in the first step, and the tasks.md can be worked through and ticked off piece by piece.
In theory, this step can of course also be taken over by a human.
3. Verify
Here the framework checks whether the implementation matches the specification. In this step I usually also run a code review in parallel and check the implementation by hand, where that’s sensible and necessary. With security-relevant changes I still always look at the produced code in detail myself.
4. Update
Should something not be right in step 3 – I notice that the UI perhaps feels awkward to use, that the design wasn’t implemented appropriately, or that something else doesn’t match my expectations – there is an optional intermediate step: the update. Keeping to this step ensures that changes are worked back into the artefacts created in step 1.
At this point I also consider whether to adjust something in my project rule files, such as the AGENTS.md, in order to prevent something similar from happening again in subsequent spec changes.
If an update was necessary, it’s back to step 2.
5. Sync
This step checks whether an already existing spec is affected by the current spec change, and makes sure that the new system behaviour is updated there.
6. Archive
The current spec change is moved into an archive folder, which also secures the history of all previous changes.
openspec/
├── changes/
│ ├── add-spectator-mode/ ← in progress, not archived yet
│ │ ├── specs/ ← the delta: what this change alters in the behaviour
│ │ ├── design.md
│ │ ├── proposal.md
│ │ └── tasks.md
│ └── archive/ ← completed changes, dated
│ ├── 2026-09-03-add-app-skeleton/
│ ├── 2026-09-04-add-table-ui/
│ ├── …
│ └── 2026-09-08-tell-a-full-room-its-full/
└── specs/ ← the current system behaviour
├── app-delivery/
├── estimation-rounds/
├── game-sessions/
├── …
└── table-ui/
The folder names in the archive are the change history: dated, chronological and named by content.
Greenfield vs. brownfield
Usually you don’t start a project with a framework like OpenSpec already in place, so the specs that describe the system behaviour and the features along with their function are missing. A sync then becomes difficult – there’s nothing there for a sync to work against.
For this, OpenSpec offers a solution in the form of a further function, which can optionally be used before all other steps or independently of them.
0. Explore
Here the existing codebase is examined for processes and interrelations. Requirements can be worked out, and specs created initially. This can also be very helpful for finding bugs – or generally to get an understanding of an unfamiliar codebase.
Advantages and disadvantages of SDD
Against vibe coding, SDD wins on almost every point. But that’s an easy opponent, and one I picked myself. The more interesting comparison is properly run conventional development: tickets, reviews, ADRs and a team that knows what it’s doing. There it looks less clear-cut, and there are real disadvantages there too.
Agent independence
As mentioned earlier, the agent that ultimately does the writing of the code is interchangeable here. Thanks to the precise specification, you can switch to almost any other model or agent at this point without problems, if the aim is for instance to save tokens and costs. The “thinking work”, meaning the propose, can be handled by a large and expensive model, and the working through by a local model running overnight. I believe that approaches like this in particular will become more and more important in future.
Quality
The quality of the architecture and of the code goes up, because an agent quickly gets a coherent picture of the software without having to read through hundreds or thousands of files. The change history helps in retracing old decisions that would otherwise only be findable as code changes in the Git history.
Costs
Here too I see savings potential, depending on the use case. The behaviour of the system is quickly comprehensible, and unnecessary reading of the code can be dropped. Of course the whole process requires additional effort and therefore tokens as well, but you arrive directly at the desired goal, without big detours.
Review load
At some point I no longer read the code in detail, I wrote that above. But I do have to read the specs, and there are quite a lot of them. In the project further down, that’s 331 requirements in three weeks, so roughly sixteen a day. The quality depends on this in the end, and so does whether what comes out is what I had in mind. That review accuracy drops the more there is to review is something we know from code reviews, and with specs it’s no different. If I simply wave the propose through, what I end up with is neatly specified random features.
Spec drift
Sync decides agentically whether an existing spec is affected by a change. If it misses a case, the spec says X and the code does Y. With classic documentation that’s annoying. Here it’s more dangerous, because the spec is my main access point to the system. If I no longer read the code, I don’t notice the divergence either. It helps to run a spec review every now and then; sometimes OpenSpec also points out itself that something isn’t quite right. Usually, though, a strict verify is entirely sufficient.
Small stuff
SDD for a CSS fix is nonsense. Only once a change affects the system behaviour do I use SDD.
Artefacts
The process produces artefacts for you that can be used in many ways and that build up a knowledge base for the project.
- They can be used directly for documentation or transferred into a wiki that non-technical colleagues can understand too.
- Code comments can be generated that describe not only the function of the code concerned but also the bigger picture – and these can likewise be kept up to date automatically.
- The system behaviour of one project can be used directly in other projects it has interfaces with, creating cross-system documentation.
- New employees find an up-to-date overview of a project and can have an AI explain it to them quickly, without having to struggle through poorly documented code.
- Rewrites of a project in a different programming language or with different frameworks are also greatly simplified.
Examples from practice
Over the past few months I’ve tried out several SDD frameworks, and in the end I stuck with OpenSpec. To me it seems the best suited for the widest variety of project types. Among them were this website with its self-written blog system, a few smaller web projects (static/single page), a retro game as a hobby project using the Godot engine and GDScript, as well as a project I wrote for this article – and because I needed it myself and wanted to test it. You can find the source code including all specs and archived changes here: github.com/thomas-negele/planningpoker
I deliberately built this project in a tech stack unfamiliar to me: the backend in Go, the frontend with Svelte. Or better, “had it built”? In any case, all the functions are the way I wanted them, and more from my backlog will follow. If a Go or Svelte pro wants to give an assessment: go right ahead. I’m fairly certain that no major issues will be found.
Why didn’t I build it in my familiar tech stacks? With that I also wanted to show myself that everyone can do everything now.
Another project that isn’t public yet I’m currently developing the same way. This one is a larger platform designed for many users and meant to scale accordingly: a backend in Spring Boot and Kotlin, a back office for administration, a public web client and native mobile apps for iOS and Android. The specs cover the entire project, which lives in a mono repo. Every feature is implemented across the system, in the backend and in all clients. CI/CD all the way to the test environment is part of the specs as well. In my experience, a project like this would have kept several teams busy for months; I was able to build it alone in days, mostly on the side while working on other projects. With this type of project in particular, a great deal of coordination effort between teams simply falls away: the specs and their changes are the single source of truth, and I only steer the overall project. My decisions matter here; the code is cheap, and the tech stack has become almost irrelevant.
A few numbers? 21 days, 18 of them with commits. 36 completed spec changes, 46 capabilities, 331 requirements. Concretely that’s 77 HTTP endpoints, 19 pages in the web frontend, 18 database migrations, plus the mobile app for both platforms and the admin area. Total effort roughly 80 to 100 hours. Application code and test code in a 1:1 ratio.
Closing remark
The famous paper “Programming as Theory Building” by Peter Naur argues that software is not the code or the documentation, but rather the mental model that lives in the heads of the developers. The paper is from 1985; agentic software development was still a long way off back then. Let me now bring the mental model from the “heads” of the AI agents into the equation – and there I recognise Spec Driven Development as the bridge, and the specs in the project directory as the mental model. The specs themselves are perhaps more like documentation; but the change history that an AI agent can gather from the history of spec changes and the Git history, and “understand”, corresponds for me to a developer’s mental model. Minus the emotions, of course, that a developer carries around on release day or after two days of bug hunting.
Software development was never about writing code either. That part was perhaps the most time-consuming, but not the most important – certainly the most replaceable. Whether by a different development team or a different tech stack.
Far more important – and this is where most problems came from when it wasn’t done properly – was a clean requirements analysis, the creation of a shared understanding for everyone involved, then checking against the requirements and adjusting the processes and standards within a team or project. Which is exactly what SDD frameworks like OpenSpec map ideally.
And last but not least, because the argument often comes up about who even is the owner of the code if you no longer write it yourself: my counter-question would be – who is the owner of the code that was written one or two years ago? What happens when the developer who set up that microservice almost single-handedly leaves the company? Whether clean specs and their history might not help more there, I’ll leave standing with a wink.
// contact
Discuss this article
If this article sparked a question, project idea or disagreement, send me a message with the article context included.
Get in touch