I first hit the limits of vibe coding at a hackathon (as one is allowed to do). Four of us were shipping features with agents, and the project fell apart fast. We kept running into:

  • Merge conflicts, and agents overwriting each other’s code
  • Agents guessing at what we meant
  • Stale code left behind
  • Features that didn’t integrate well with each other

That’s the trade-off with vibe coding. You test a bunch of ideas and see what sticks, and the code is disposable. But it got me thinking: when you’re building software that has to last, how do you plan and execute a design with agents?

My answer has been to write a specification, or spec, before the agent writes any code. I start with a prompt outlining what I want to build: goals, non-goals, and core features. The agent scaffolds a spec Markdown file from a template I prepared, and I iterate on it until the agent and I are aligned.

A spec looks a lot like a lightweight product requirements document (PRD) or technical design document. I review it several times, catch bad assumptions, and trim flawed ideas before any code is written. Once the requirements and boundaries are clear, the agent starts implementation.

Greenfield vs. Mature Software Projects

A spec’s scope shifts with where the software is in its lifecycle:

  • Brand-new software: the first spec is broad. You’re defining domain models, foundational architecture, and core conventions from scratch.
  • Mature systems: the spec narrows to one change and how it lands on what already exists. You’re documenting migration steps, failure handling, backwards compatibility, and rollback.

My homelab has one of each. The spec behind my observability pipeline picked the log collector, the database, and the detection scheduler, then decided where every component would run. Weeks later, a much narrower spec rebuilt the main ClickHouse log table, because its partitioning made ClickHouse rewrite about 15 GiB every few hours just to expire old rows. I noticed because the case fan spun up every time it happened. That spec spent most of its words on the backfill, disk headroom, and keeping the cutover safe.

Anatomy of a Spec

A spec doesn’t need to be a 20-page document. It only needs enough structure to remove the ambiguity where agents wander off course. Mine had these sections:

  • Context and problem: why the change exists. An agent needs the why to solve the right problem.
  • Goals and non-goals: what success looks like, and what not to touch. Agents love to add “helpful” functions, extra config flags, and refactors nobody asked for.
  • Alternatives considered: the options I rejected, and why.
  • Contracts and interfaces: how components talk to each other: function signatures, API routes and request/response schemas, database schemas. Fixed boundaries keep agents from inventing their own data structures.
  • Failure planning and edge cases: expected failures (timeouts, network drops, malformed input), how errors surface, and fallbacks, retries, and rate limits. Agents default to naive error handling unless told otherwise.
  • Migration and compatibility: for established software, how the change lands: schema migrations, breaking changes and deprecations, and rollback steps if the deploy fails.
  • Constraints and approved dependencies: which tools, libraries, and patterns to use. Otherwise an agent will install a package for a trivial problem, or pull in a framework when a short function would do.
  • Acceptance criteria: a checklist of testable requirements. It’s the definition of “done” for both you and the agent.
  • Decision log: a dated line for each decision or change of direction during the build.

Pros and Cons

Pros

  • Cheaper than unwinding code: a wrong design in code means ripping out tests, imports, docs, and helper libraries. A wrong design in Markdown is a few deleted paragraphs.
  • Agents stop guessing the architecture: key decisions (libraries, schemas, contracts) are locked down before any code is generated.
  • Sessions stay consistent: when a constraint isn’t written down, each agent invents its own. Two agents, or two chat sessions, make conflicting choices unless they’re anchored to the same spec. That’s what bit us at the hackathon.
  • Less scope creep: goals and non-goals give the agent a place to stop.
  • Durable decision records: weeks or months later, the spec explains why an approach was chosen and which alternatives were rejected.

Cons

  • Spec drift: reality changes during implementation. If you hit an edge case and fix it only in code, the spec goes stale unless you have the discipline to update it.
  • Over-engineering: an exhaustive spec for a prototype or hackathon project is wasted effort if the code gets thrown away.
  • Front-loaded friction: you have to sit down and think through contracts, edge cases, and interfaces before writing any code.
  • Specs are mostly LLM-written: I treat them as likely untrustworthy until I’ve read them closely.
  • You can still spec the wrong problem: I’ve been deep into a spec before asking whether it solved the right problem at all. That question belongs in a PRD, before the spec exists.

Fighting Spec Drift

When implementation turns up an edge case the spec didn’t cover, don’t just patch the code. Update the spec in the same change, with the reason. While the build is running, the spec is the agent’s source of truth. If it’s wrong, the next session builds from the wrong thing.

What’s changed since

I wrote this in September 2026. A week later, agents were writing specs faster than I could read them, and plenty of them ran well past “a spec doesn’t need to be a 20-page document.” A spec I don’t have time to read doesn’t work as an approval step.

So the format changed:

  • A spec is an approval: something I can read in five minutes and say yes or no to. Nothing gets built until I add an approved: date, and at most three specs per project are active at once.
  • A word budget: 1,000 words, plus 250 for validation. The sections shrank to six: problem, decision, not doing, interfaces changed, validation, and open questions.
  • Context lives elsewhere: research, measurements, and the full comparison of options go in a dated research note the spec links to. A change of direction during the build is one dated line with a PR link.
  • Specs freeze when built: once the work ships, the spec gets a short “As built” paragraph and is never edited again. What’s true now lives in architecture notes.

That last one settles something I went back and forth on: a spec is the source of truth while it’s being built, and history after.

Closing

Spec-driven design has worked well for me, especially on mature software that already has a lot of context and documentation. The limit turned out to be my own reading time, not the agents, and the budgets keep specs inside it. I’m curious what others are doing with agentic workflows and documentation.