Beyond Vibe Coding: Spec-Driven AI Frameworks for Tech Debt

By Serge Aleynikov - September 17, 2026

Beyond Vibe Coding

The first time I asked an AI agent to build a feature from scratch I felt like a wizard. Typed a messy paragraph, hit enter, watched two hundred lines of code emitted out of thin air. High-fived the monitor. Felt ridiculous for two days afterward, like my decades-long code-writing experience expired worthless. But still I was tempted to go back and try more.

Six weeks later, same agent, live multi-tier production app. I wanted to throw the laptop through the window.

It hallucinated a field that had never existed in my data structures, used coding style inconsistent with my codebase, quietly deleted a section of code that was critical to the correctness of the component, and left the test suite looking like it had been run over by a truck. That is the real edge of “vibe coding.” The model is still uncanny at 0-to-1 work in an empty folder. Give it a codebase with actual weight and the prompts start rotting context, inventing breaking changes, and scattering silent bugs. “Pretty please don’t break master” is not a CI strategy. I checked.

If these things are going to act like engineers we have to stop treating them like oracles. Blueprints. Contracts. Checks that actually run.

Spec-Driven Development is the unglamorous answer. You write structured specs the model can be held to. It then works inside real constraints instead of improvising. Difference between a sticky note that says “make login better” and a ticket that has acceptance criteria and a definition of done.

SDD and TDD Are Not the Same Thing

People keep collapsing these two. They shouldn’t.

TDD lives at the unit level. Failing test, minimal code, refactor. It answers one tight question: does this small piece do what I just claimed? Still one of the best design tools we have.

SDD sits higher. You write the actual specification-requirements, constraints, non-goals, acceptance criteria-before anyone (or any model) starts implementing. Different question entirely: did we build the thing we said we were going to build?

Aspect Traditional TDD Spec-Driven Development (SDD)
Primary artifact Failing unit test Structured specification
Scope One small unit or behavior Whole feature or system slice
Main question Does this unit do what I claimed? Did we build what we agreed?
Feedback loop Seconds (red-green-refactor) Per feature, with review gates
Best at Unit correctness and design feedback Aligning intent, especially with AI

They fit together cleanly. SDD points at the destination. TDD keeps the individual steps from falling apart. Skip the first and you can end up with beautifully tested code that solves the wrong problem. Skip the second and the elegant spec still ships bugs.

I spent some time putting five of these frameworks through real, messy codebases: OpenSpec, OpenGap, BMAD, GitHub SpecKit, Graphify. Also poked at a couple of lighter options. What follows is less a neat taxonomy and more what actually held up when things got ugly.

Landscape Snapshot

Framework Core Focus & Paradigm Primary Strengths Ideal Target Use Case Relationship to TDD
OpenSpec Change-centric diffs & brownfield isolation Token-efficient, fast, lightweight Legacy codebases, refactoring, rapid iterations Specs define the change; TDD verifies the units
OpenGap Compliance, linting & behavioral gap analysis Prevents scope creep, strict contract checks Auditing AI code, security-first, regulated apps Contracts often feed or become acceptance tests
BMAD Multi-agent team simulations Parallel streams, role-driven delegation Complex enterprise platforms, multi-tier systems QA agent naturally drives TDD-style validation
GitHub SpecKit Gated 4-phase SDD & team standards Persistent “constitution” rules, review gates Medium-to-large teams, greenfield development Explicitly supports tests inside Tasks/Implement
Graphify Visual & graph-based architectural specs Deep topology mapping, zero-token local index Microservices, polyglot codebases, system mapping Shows which surfaces actually need new tests

OpenSpec

Brownfield work is where most of these tools fall over. OpenSpec doesn’t. It refuses to document the entire system and instead focuses on the delta. You drop small change specs into /openspec/changes/ and leave the main specs alone. The model only gets the context it needs for that specific job. Token bills stay reasonable.

You still write the unit tests yourself, the normal way. OpenSpec’s job is mostly to keep the AI from wandering off and “improving” the authentication layer while it’s supposed to be adding a billing endpoint.

I used it to add a subscription tier to an Express app that had grown teeth. Defined only the new /checkout contract and the User model changes. Then ordinary TDD for the billing logic. The model stayed in its lane. That alone felt like progress.

GitHub SpecKit

This one is heavier. Four-stage pipeline-Specify, Plan, Tasks, Implement-and you are not allowed to jump ahead. The Constitution file is where you write the non-negotiables. Short-lived JWTs. Rate limiting on every endpoint. That kind of thing.

It is the cleanest combination of SDD and TDD I saw. High-level spec sets the destination; inside the Tasks phase you can drop straight into red-green-refactor for each piece. Some teams even put “all new code must be TDD-driven” in the Constitution and mean it.

I ran a multi-tenant OAuth service through it. The Constitution killed three half-baked ideas before any code was written. TDD handled the individual pieces. Fewer embarrassing pull requests. The overhead is real, though. Solo work or tiny changes feel like wearing a suit to the grocery store.

BMAD

When the feature is simply too big for one context window, BMAD is the multi-agent approach that didn’t completely fall apart on me.

It spins up specialized agents-PM, Architect, Dev, QA-and keeps their contexts separate. The PM breaks things down, the Architect maps data flow, the Dev writes code, the QA agent validates. It is a full standup without the video-call performance.

The QA agent is where TDD lives most naturally. While the others work from the high-level spec, QA can generate failing tests first and force the Dev agent to make them pass. I threw a messy monolith-to-microservices split at it. Architect produced the gRPC contracts, Dev implemented the handlers, QA drove the cross-service tests in proper TDD style. Still needed a human watching the handoffs, but it was the only framework that didn’t just collapse under the size of the problem.

OpenGap

This one is less about generating code and more about refusing to let the model quietly break things three folders away.

It treats your requirements as a contract and keeps checking the generated code against it. Invisible drift-those moments when the AI “fixes” a bug by deleting your edge-case validation-gets flagged. In regulated work this is almost non-negotiable. Spec says every transaction must hit the audit log. TDD makes sure the logging functions themselves work. OpenGap blocks any PR that optimizes the audit trail out of existence. Same pattern for PII scrubbing.

It sits downstream of both SDD and TDD and acts as the last behavioral gate. Not glamorous. Extremely useful.

Graphify

Most retrieval approaches still treat code as a bag of text. Graphify builds an actual directional graph with local Tree-sitter parsers. Zero LLM tokens for the parsing step.

Its practical value is making TDD less random. Change a core utility used by fourteen services and the graph shows the real blast radius so you know which tests actually need attention. You still write the tests. You just stop testing the wrong surfaces.

To illustrate its work I used it on an Elixir app that implements a distributed order processing engine which saved orders to Postgres. The Graphify’s local AST parse mapped the full runtime layout of the application. Here is a sample output:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
┌──────────────────────────────────────────────────────────────────┐
│                   ORDER PROCESSING SYSTEM GRAPH                  │
└──────────────────────────────────────────────────────────────────┘

                 [OrderSystem.Application]
                            │
              ┌─────────────┴─────────────┐
              ▼                           ▼
  [OrderSystem.OrderRegistry]  [OrderSystem.OrderSupervisor]
                                          │ (spawns dynamically)
                                          ▼
                             [OrderSystem.Pipeline.Processor]
                                    │               │
                            (uses)  │               │ (dispatches)
                                    ▼               ▼
                       [Core.Order Schema]  [PaymentBehaviour]
                                                    ▲
                                                    │ (implements)
                                                    │
                                        [Pipeline.StripeAdapter]

Two Lighter Options

Aider plus a strict conventions file works surprisingly well for solo work. The conventions act as a lightweight spec; you can still drive individual changes with normal TDD.

There are also harnesses that treat the test suite itself as the specification. The agent is not finished until every generated test passes. Pure TDD raised to system level. Feels extreme until you try it on something that cannot afford to be wrong.

Cost and Overhead Reality Check

Framework Token Efficiency / LLM Cost Setup Overhead Maintenance Effort TDD Integration Cost
OpenSpec Low - only active diffs are fed to the model Minimal (under 5 minutes) Low - specs evolve with normal commits Low (tests stay local)
GitHub SpecKit Moderate - refinement loops add some overhead Medium - needs team agreement on the Constitution Medium - reviews required at each gate Medium (tests often written in Tasks)
BMAD Higher - multi-agent runs mean parallel model calls High - roles and handoff rules need defining High - someone still has to supervise the virtual team Medium-High (QA agent orchestration)
OpenGap Moderate - automated verification on PRs Medium - needs solid upfront contracts Low/Automated - fits into normal CI Low (contracts can feed tests)
Graphify Lowest LLM cost - local AST work uses zero tokens Low - one CLI command to index Low - incremental re-indexing via hooks Low (improves TDD targeting)

What I’m Actually Using

Vibe coding was a fun phase. Production code is not. The combinations that keep working for me:

None of these tools are magic. They just force a discipline most of us already knew and kept skipping: decide what “done” actually means before the model starts inventing it. Everything after that is just making sure the pieces hold when someone else has to live with them.