The bet: the spec is the asset, the code is the output
We recently shipped a mid-sized full-stack web application — a .NET backend, a TypeScript/React frontend split across two portals, object storage for documents, and a relational database — with a small team and a fixed scope. What made the project unusual was not the stack. It was the method: we committed to spec-driven development (SDD) as the backbone, and leaned heavily on AI agents for both planning and implementation.
There is no shortage of writing on what SDD is and why it matters in theory. This is not that. This is a field report on what actually happened — the toolchain we assembled, the velocity we did not expect, and the four places where reality diverged from the plan and forced us to adapt. Two of those divergences changed how we work permanently. One of them made us build our own tooling.
Our central bet was simple: the specification, not the code, is the source of truth. If we could keep an accurate, living description of what the system should do, then the implementation — increasingly produced by AI agents — became a downstream artifact we could regenerate, correct, and reason about. Code was the output. The spec was the asset.
To make that bet work, one document had to stay alive throughout: the PRD (Product Requirements Document), our single source of truth. The product owner authored it by hand and refined it with AI assistance. It was never meant to be frozen — as requirements sharpened, we added companion documents to capture decisions the PRD alone could not, most importantly an api-contract that became the binding protocol between frontend and backend. Keeping the PRD and its companions current was the price of admission for everything else.
The toolchain and the rhythm
We ran a hybrid method with two toolsets for two distinct jobs.
Planning with BMAD
We used BMAD's SM (scrum-master) agent to generate our sprint plans. This gave us a structured decomposition of the work into sprints without a human spending days in planning meetings. The output was a set of sprints.
Building with KIRO
Here is the move that defined our process: we treated each sprint as a spec. The content BMAD generated for a sprint became the seed for a KIRO specification. We then ran KIRO's skill commands sequentially against that spec:
- /kiro-requirements expanded the sprint's intent into comprehensive, testable requirements.
- /kiro-design turned those requirements into a technical design.
- /kiro-impl executed the implementation against that design.
When the commands surfaced conflicting arguments — places where requirements or design pulled in incompatible directions — we ran KIRO's /kiro-validate command to resolve the conflict before proceeding. Validation was not a routine step. It was the pressure-release valve we reached for when the pipeline told us something did not add up.
The KIRO skills were installed into our repository via the open-source cc-sdd project, which packages them as installable agent skills. End to end, the pipeline looked like this: BMAD generates the sprint plan, each sprint becomes a KIRO spec, then /kiro-requirements feeds /kiro-design feeds /kiro-impl — with /kiro-validate as needed — producing a working increment.
The velocity we did not plan for
The single most striking outcome was speed.
In the world we came from, work was sized for human throughput. A standard CRUD feature — create, read, update, delete — was naturally four cards on a board, because that is roughly how much one developer could meaningfully pick up, build, and review as discrete units.
With AI agents executing the spec, that same CRUD collapsed into a single card. The unit of work grew because the cost of executing a well-specified unit dropped.
Scaled up, the effect was dramatic: a sprint scoped as two weeks of human work was completing in one to two working days. The bottleneck moved off the keyboard entirely. It was no longer "how fast can we type the implementation" — it was "how fast can we specify, validate, and verify the work."
That is the headline benefit, and it is real. But every challenge below is, in some sense, a second-order consequence of the same compression. When you go ten times faster, the parts of your process that were quietly tuned for the old speed break in interesting ways.
Where reality diverged — and what we learned
1. One driver, by design
What we expected: several team members would split the work and drive specs through KIRO in parallel, the way we always had.
What happened: the work kept funneling through a single driver. The interesting part is why — and why we now believe that is the right shape, not a failure of it.
In the old world, a CRUD feature split cleanly into four cards that several developers could pick up independently. That principle — dividing work into separable cards and parcelling them out — simply does not transfer to AI-assisted SDD. We no longer develop cards; we develop an entire sprint at a time. And each sprint depends on the ones around it: they share scope, contracts, and assumptions. When the units of work are this large and this interdependent, splitting them across people invites collision and rework. Developing them individually, through one driver, turned out to be the best option — not merely the one we defaulted to.
This is worth stating plainly, because it is rarely mentioned: the AI-assisted SDD workflow, as we ran it, was not naturally a parallel, multi-person activity. For a small team where effectively one developer drives the pipeline, this was not just survivable — it was efficient. The open question is how it holds at larger scale, where the only safe axis of parallelism is across genuinely independent sprints, not within a single one.
2. The visibility gap — and the /board command
What we expected: progress would be visible the way it always had been — cards moving across a board.
What happened: because we generated sprint plans with BMAD's SM agent rather than in a connected project-management tool, we had no board. No Jira, no Trello, no managed kanban wired into the work. And with sprints completing in a day or two, the absence was acute: the work was moving faster than ever, and nobody could see it moving.
A PM or SM asking "where are we?" had no good answer. The honest answer lived in long specification READMEs and in the codebase itself — neither of which is something a stakeholder can glance at.
So we built our own tool: a custom command we call /board. It inverts the usual relationship between the board and the work. Instead of the board being a separate system that humans update by hand, the board is generated from the source of truth — the specs and the code — on demand.
The workflow is deliberately frictionless for a non-developer: open the codebase, check out the branch you care about, run /board, and the command reads the current state of the specs and renders it as a kanban dashboard in the browser, with the familiar columns — To Do, In Progress, Code Review, Done.
The result is a board that can never drift from reality, because it is derived from reality rather than maintained alongside it. There is no manual card-dragging to forget. When the code and specs change, the next run reflects it. This single tool closed the visibility gap our speed had created, and it became the primary way everyone understood project status.
3. The frontend-first assumption and the binding tax
This was the most expensive lesson, and the most generalizable.
What we assumed: we had a polished visual prototype that everyone had signed off on. So we treated the frontend as done and scoped the project as "build the backend." The prototype was, in our minds, the finished product's front half.
What happened: the prototype was built on mock data, shaped however was convenient to make the screens look right. When we reached the binding phase — wiring the real backend to the real frontend — the two halves did not fit. The frontend rendered against data structures the backend did not produce, and the backend returned structures the frontend did not know how to render.
The work we had not budgeted for was substantial:
- We had to strip the mock data out of the frontend.
- We had to restructure the objects returned from specific endpoints to match the frontend's existing rendering patterns — or adjust the rendering, case by case, to match the contract.
The fix we landed on was the api-contract: a dedicated document defining how the frontend connected to each backend endpoint. With the contract in hand, coded endpoints bound to the frontend cleanly. The catch was timing — we only created it around the third sprint, at the moment frontend and backend actually had to meet. Had we written it the instant the frontend was signed off, the mismatch, and most of the rework, would never have accumulated.
The lesson: "the frontend looks finished" is not the same as "the frontend is integrated." A prototype proves the design; it does not prove the data contract. The contract between layers deserves to be a first-class artifact — written at frontend sign-off, not discovered during binding.
There is a second, related lesson: a feature-rich prototype is not the same as a well-written frontend. It is tempting to read visual completeness — every screen present, every interaction wired, every state mocked — as engineering completeness. It is not. A prototype optimized to look finished is often built on conveniently shaped mock data and throwaway structure, precisely the things that have to be torn out when real integration begins. The richness of the prototype told us nothing about the integration-readiness of the code beneath it.
4. Implementation drift and the limits of the README
What we expected: the specs, being comprehensive, would keep implementation faithful to intent.
What happened: around the third and fourth sprints, we began noticing implementation misses — features the spec called for that had not actually landed. The cause was mundane and instructive: the specification READMEs had grown too long to hold in view. A spec comprehensive enough to drive AI implementation is, by that very fact, too long for a human to scan and verify at a glance.
This is where /board earned its keep a second time. Reading and re-verifying a wall-of-text README is a poor way to catch what is missing. Looking at a board of cards is a good one. The visual representation surfaced the gaps the prose had hidden — this card never moved; that feature was never built. The tool we built to solve a visibility problem turned out to be our best defense against implementation drift.
The deeper lesson: in AI-assisted SDD, comprehensiveness and reviewability are in tension. The more complete a spec is, the less humanly auditable it becomes. You need a derived, visual, glanceable view of state — not just the authoritative text — to close that gap.
What we would keep, and what we would change
We would keep:
- The PRD as a single source of truth, kept alive. It anchored intent; companion documents like the api-contract handled refinement and integration. One authoritative spec, continuously updated, made everything else legible.
- The BMAD-plans / KIRO-builds split. One agent toolset for planning, another for the requirements-to-design-to-implementation pipeline, gave each phase a tool fit for its job.
- Treating each sprint as a spec. This was the conceptual hinge of the whole method, and we would do it again.
- /board. Deriving the status view from specs and code rather than maintaining it by hand removed an entire category of drift.
We would change:
- Treat the sprint — not the card — as the unit, and don't fight its interdependence. If you need parallelism, find it across genuinely independent sprints; otherwise let one driver own the chain.
- Write the api-contract at frontend sign-off, not at first binding. Make API response shapes — validated against the frontend's real rendering needs — a first-class artifact from the moment the design is locked. Never let a mock-data prototype stand in for an integrated frontend.
- Evolve the board from a mirror into an actor. Our next /board would not just show state — it would act on it: kept in real-time sync with the codebase, and able to trigger the model itself to validate, analyze dependencies, and implement features directly from the board. The glanceable view becomes the control surface.
The one sentence to carry away
Spec-driven development, paired with AI agents for planning and implementation, did what it promised: it compressed two-week sprints into a day or two and shifted our effort from typing code to specifying and verifying it. That shift is genuine, and it is large.
But speed does not come free. Going an order of magnitude faster broke the parts of our process quietly tuned for the old pace — our assumptions about how work divides across people, our reliance on hand-maintained boards, our faith that a pretty prototype meant an integrated frontend, and our belief that a comprehensive spec is also a reviewable one. None of those were failures of the idea of SDD. They were the growing pains of running it at a new velocity.
If there is one sentence to carry away, it is this: in AI-assisted spec-driven development, the work is no longer the bottleneck — visibility and verification are.
