The problem isn't usually an AI coding problem. It's a spec problem.
The popular advice is still wrong in a very specific way. It tells you to debug the output, tighten the prompt, retry, patch the code, and keep moving. That feels productive right up until you're on your fifth regeneration, burning context, and fixing code that was always going to be wrong because the instructions were vague.
Yes, the backlash is real. People call spec-driven development "waterfall with markdown." They complain about frozen specs, drift, and token-burning ceremony. Those complaints aren't fake. A Reddit thread on Claude Code calls out the "insane token usage and rework cycles" that come from iterating on flawed AI output, which is exactly the failure mode sloppy specs create in the first place, not evidence that specs are useless (Claude Code rework discussion). The hard truth is simpler. A thin spec costs minutes. Rework costs evenings.
If you build solo, you can't afford to treat AI like a junior engineer who can magically infer your intent from a half-formed sentence and a messy repo. You need a repeatable way to say what you want, what you don't want, what constraints matter, and how you'll judge success. That is the work.
Table of Contents
- Your AI Coding Is a Mess Because Your Specs Are Vague
- The Core Shift from Prompting to Specifying
- Anatomy of a High-Fidelity Spec
- A Practical Workflow for Solo Builders
- Choosing Your Spec-Driven Tooling
- When You Should Still Debug the Output
- Start Shipping with Structured Specs
Your AI Coding Is a Mess Because Your Specs Are Vague
AI usually fails before it writes a line of code.
The expensive part is not generation. It is the cleanup that follows a weak spec. You get code that looks plausible, passes a glance test, and still bakes in the wrong assumptions about state, edge cases, data shape, or what must stay untouched. Then the session turns into archaeology.
That is why Addy Osmani's advice matters. Start with a clear, concise spec, let the model expand it into a plan, and review the plan before execution (Addy Osmani on writing a good spec). The spec is not project paperwork. It is the control surface.
The most significant waste is rework
I see the same failure pattern over and over with solo builders. They ask for a feature in plain English, get a decent-looking draft, then spend the next hour patching the same category of mistake across multiple files.
A vague request like "add team billing with Stripe" leaves too much open. Who can manage seats. What happens on downgrade. Which webhook events matter. Whether existing account models can change. How to handle failed payments. The model fills those gaps with guesses, and now you are debugging guesses.
Use a simple rule.
Practical rule: If the output is wrong in a repeated way, stop editing the code and fix the spec that produced it.
That is the scalable move. A patched file helps once. A tighter spec helps every regeneration, every follow-up task, and every adjacent feature that touches the same behavior.
Why the backlash still misses the point
Some criticism of spec-driven AI work is valid. A long markdown file nobody reads is overhead. A spec that drifts from the repo is stale almost immediately. A process that turns a small CRUD change into a ritual burns time and tokens.
But that is not the failure mode I see most often.
Solo builders usually underspecify the work. They expect the model to make product calls, infer architecture rules, preserve hidden invariants, and define done on its own. That is where the mess starts. If the acceptance test lives only in your head, the model cannot target it. Writing acceptance criteria an agent can actually verify closes a big part of that gap.
Useful specs are short and operational. They state constraints, existing context, failure cases, and checks. Teams that already use behavior-driven habits will recognize the pattern. BDD best practices and CI/CD map cleanly to AI coding because both approaches work better when behavior is explicit before implementation.
The practical trade-off is simple. Spend ten extra minutes specifying the work, or spend the next hour debugging code that was wrong for predictable reasons. I stopped choosing the second option.
The Core Shift from Prompting to Specifying
Prompting is conversational. Specifying is operational.
That distinction matters more than most AI coding advice admits. A prompt says, "build me X." A spec says, "build X inside these boundaries, with these inputs, these outputs, these assumptions, and these checks."

A prompt asks and a spec constrains
Most bad AI coding sessions start with a request like this:
Add team billing to my app. Use Stripe. Keep it simple.
That is not a spec. That's a wish.
A useful spec names the behavior, the boundaries, and the failure conditions. It tells the model what exists already, what must not change, what edge cases matter, and what counts as complete. This is why Osmani's advice to begin with a high-level vision, have the AI expand it into a detailed plan, and run structured review loops is so effective. The value isn't a longer prompt. It's reduced ambiguity.
If you already think about prompts as callable interfaces, this clicks faster. A good companion read is treating AI prompts like functions with strict inputs and defined outputs. The same idea applies here. You get better behavior when you define the contract.
Why this changes the agent's job
Without a spec, the agent has to invent missing intent. Sometimes it gets lucky. Often it invents the wrong thing with high confidence.
With a spec, the job becomes narrower:
- Interpret less because the requirements are stated.
- Ask better questions because the gaps are visible.
- Generate smaller batches because the work is broken down.
- Verify against something real because acceptance criteria exist.
That last point is where most workflows still break. A model can write code quickly, but your validation loop has to keep up. If you want a practical bridge between spec writing and testable behavior, BDD best practices and CI/CD are useful because they force expected behavior into a format you can check, not just discuss.
The shift is simple. Stop asking the model to be clever. Start forcing it to be precise.
Once you make that shift, "Stop debugging AI output. Start writing better specs instead" stops sounding like philosophy and starts feeling like basic engineering hygiene.
Anatomy of a High-Fidelity Spec
Most advice dies at "write better specs." That's not enough. You need a shape you can reuse.
The minimum useful spec for solo builders is short, explicit, and hostile to ambiguity. It should fit in the model's context without turning into a giant planning artifact nobody wants to maintain.

The six parts that matter
A high-fidelity spec needs six parts.
| Part | What it does | What to write |
|---|---|---|
| Goal | Defines the intended outcome | One paragraph on the user-visible result |
| Scope boundaries | Prevents gold-plating | Include a blunt "not building" list |
| Technical requirements | Anchors the implementation | Existing files, services, libraries, architectural limits |
| Acceptance criteria | Defines done | Observable pass or fail checks |
| Validation scenarios | Surfaces edge cases | Happy path, failure path, weird path |
| Key assumptions | Exposes hidden bets | Anything that might be false |
This structure works because hidden assumptions are what usually blow up AI output. Augment's writing on AI code quality argues that the hardest failures come from "invisible assumptions" embedded in generated code, and that code review is too late if the intent was never explicit in the first place (spec-driven development and hidden assumptions).
If you want a shorter version than the full template below, this minimal spec format is a good baseline.
Before and after
Here is a weak spec.
Add an endpoint for invoice downloads. It should be secure and work with our current auth.
Here is the same request in a form an agent can execute.
Goal
Add a backend endpoint that lets an authenticated user download a PDF invoice for their own account.Scope boundaries
Build the endpoint and auth check. Do not redesign billing. Do not add admin invoice search. Do not change existing auth middleware.Technical requirements
Use the existing Express API. Reuse current session auth. Invoice records already exist in the billing table. PDF files are stored in the current object storage path used by billing exports.Acceptance criteria
Authenticated users can download only invoices linked to their account. Unauthenticated requests fail. Requests for another user's invoice fail. Missing invoices return a clear error. Existing billing routes still pass tests.Validation scenarios
Valid user downloads own invoice. Logged-out user requests invoice. Authenticated user requests another account's invoice. Invoice record exists but file is missing.Key assumptions
Billing table stores a stable invoice-to-user relationship. Current object storage permissions allow server-side retrieval.
That second version is longer, but it's not bloated. It's executable.
Use this quick contrast when you're drafting:
- Bad means "build X."
- Better means "build X for this user, in this codebase, under these constraints."
- Best means "build X, avoid Y, prove it with Z."
Write specs so a tired future version of you can tell if the model did the right thing without rereading the whole codebase.
A Practical Workflow for Solo Builders
Solo builders do not need more AI chat. They need a repeatable loop that fits inside a normal dev session and leaves behind an artifact they can reuse next week.

The five-step loop
Use a workflow that starts with repo context and ends with a spec revision, not a hand-edited patch pile.
Audit context
Pull the minimum repo context needed to make correct choices. That usually means the route or entry point, the data model involved, any existing permission checks, related tests, and one or two nearby implementations that show the house style. Dumping the whole repo into the model creates noise. Starving it creates guesses.Draft the spec
Write the task in a format you can scan quickly: goal, scope boundaries, technical constraints, acceptance criteria, validation scenarios, assumptions. Keep it short enough to read in one pass. Be specific enough that the model cannot widen scope.Hand it to your agent
Use Cursor, Claude Code, Codex, Gemini, or whatever sits in your editor. For anything beyond a tiny edit, ask for a plan and changed-file list before code. That catches bad assumptions while they are still cheap.Validate against acceptance criteria
Review the result against the spec, not against the vague version in your head. Run the obvious scenarios. Check whether the implementation reused the right auth, state transitions, storage path, naming conventions, and tests.Fix the spec first
If the output missed the shape of the task, change the spec and rerun. Do not spend thirty minutes polishing code built on the wrong assumptions. That is how a five-minute feature turns into an afternoon.
The waste is rarely in generation itself. It shows up in clarification, cleanup, and debugging after the model committed to the wrong interpretation. As noted earlier, that pattern is common enough to be familiar to anyone shipping with AI regularly. The practical fix is simple: review the plan, tighten the assumptions, and make the model prove it met the acceptance criteria.
A short walkthrough helps here:
A real example with an API endpoint
Take a small feature: POST /api/projects/:id/archive.
A weak handoff looks like a note you wrote to yourself at 6 p.m.:
- Archive a project
- Only owners should do it
- Update the UI too
That prompt invites invention. The model has to guess what "archive" means, where ownership is enforced, whether this is soft delete or status change, and how far "UI too" reaches.
A usable workflow is tighter.
First, audit context. Open the current project routes, the ownership check already used elsewhere, the project status enum, and tests around deletion, visibility, or listing. Pull only those files into the conversation.
Next, write the spec. State that archive is a soft state change. State that owners can archive and collaborators cannot. State that archived projects disappear from active lists but still show up in user history if that view already exists. State that the task does not include schema redesign, a new permission system, or admin features.
Then hand it off with a planning step. Ask the agent which files it expects to change and why. If it proposes a new authorization layer for a route that already has one, stop there.
Validation gets easier because the review has a target. Check whether the code reused the current permission path. Check whether it updated listing logic without touching unrelated screens. Check whether tests cover the state transition and forbidden access. If the model treated archive like delete again, the problem is probably in the spec language around state, not in the final diff.
This loop feels slower only if you compare it to typing one vague sentence and hoping for the best. Compare it to the full feature cycle instead. For solo builders, the gain is fewer retries, fewer hidden side effects, and fewer evenings spent reverse-engineering code the model should never have written.
Choosing Your Spec-Driven Tooling
Tool choice matters less than people think. For solo builders, the job is simpler. Pick the lightest setup that keeps specs close to code, easy to update, and hard to ignore during implementation.

Start simple before you buy process
Plain markdown in the repo is still the best default.
A small structure is enough:
specs/feature-name.mdspecs/templates/minimal-spec.mddocs/architecture.mddocs/assumptions.md
This setup works because it fits the actual dev loop. You can write the spec, commit it with the code, diff it in review, and update it when the implementation changes. Drift becomes visible instead of hiding in a chat thread or a forgotten doc.
Template kits can help if you keep overbuilding features because your prompts stay fuzzy. They give you repeatable sections, which is useful. They also create a new failure mode. You start filling out forms for tiny changes that never needed ceremony in the first place.
Where dedicated tools help and where they hurt
The useful question is practical. Which tool reduces retries for the kind of work you do?
Use this table as a filter:
| Option | Strength | Weakness |
|---|---|---|
| Markdown in repo | Cheap, versioned, easy to edit beside code | No guardrails, easy to skip if you're rushing |
| Template kits | Consistent structure across tasks | Can turn into ritual with little payoff on small changes |
| Spec-focused tools | Guided intake, reusable formats, repo-aware drafts in some cases | Another layer to maintain and learn |
| Chat-only workflows | Fast for throwaway tasks | Context disappears fast, constraints get lost, drift shows up early |
The trade-off is not speed versus quality. It is setup cost versus retry cost.
For example, a one-file copy change does not need a spec platform. A feature that touches routing, auth, data shape, and UI state usually does. Solo builders get the most value from tooling in the middle ground, where the task is too big for a single prompt and too small to justify heavyweight process.
Community discussion around spec drift makes the core problem clear. Tooling helps only when it keeps the spec attached to the code and the current task, instead of turning it into stale prose (discussion on spec drift and tooling).
One practical option in this category is Tekk.coach. It connects to a GitHub repo, reads the codebase, runs a structured interview, and produces a first draft spec you can hand to Cursor, Claude Code, Codex, or Gemini. That is useful if blank-page spec writing is your bottleneck. It is less useful if your work is mostly small edits where opening another tool costs more than it saves.
The right tool leaves you with a living artifact in the repo. The wrong one gives you a polished document nobody updates.
When You Should Still Debug the Output
"Fix the spec first" is the default rule. It isn't a religion.
Sometimes the spec is fine and the model still blows it. It may misunderstand a library, invent a helper that doesn't exist, or wire the right logic through the wrong part of your app. In those cases, stubbornly rewriting the spec again and again just wastes another cycle.

A quick diagnosis rule
Ask one question first.
Did the failure come from missing intent, missing context, or bad execution?
That split matters more than people think. Commentary on AI-assisted review argues that the bottleneck isn't only human review. It's the mismatch between the AI's high output rate and old feedback mechanisms, which is why you need smaller batches and stronger validation. The useful distinction is between spec problems and implementation problems. Missing requirements are one thing. Model or tooling limitations are another (AI review bottlenecks and feedback loops).
Use this table when you're deciding:
| Symptom | Likely cause | What to do |
|---|---|---|
| Agent built the wrong feature shape | Spec problem | Rewrite requirements and boundaries |
| Agent changed unrelated files | Context problem | Narrow the file set and architecture notes |
| Agent used a fake API or wrong library behavior | Implementation problem | Debug or manually correct |
| Agent missed an edge case you never stated | Spec problem | Add validation scenario |
| Agent fails despite clear requirements and tests | Model or tool limitation | Step in directly |
The exceptions that matter
You should usually debug the output when:
- The bug is local and the intended behavior is already explicit.
- The model chose a wrong API call even though the spec named the right library and usage constraint.
- The tooling failed around environment, dependency resolution, or external services.
- You are at the finish line and a manual fix is cheaper than another full regenerate cycle.
A good heuristic is this. If fixing the spec would teach the agent something reusable, rewrite the spec. If you're correcting a one-off mistake in otherwise valid work, debug the code.
That distinction keeps you from becoming dogmatic. Specs are the scalable fix. Debugging is still the right move when the failure isn't a spec failure.
Start Shipping with Structured Specs
Structured specs are how solo builders stop burning hours on code they never asked for.
A workable spec is short enough to maintain and specific enough to run against. It names the user action, the expected system behavior, the files or surfaces in scope, the constraints that matter, and the checks that decide pass or fail. That gives the model a target you can evaluate. If you're trying to ship full-stack apps rapidly, that target matters more than another round of clever prompting.
The practical loop is simple. Write the spec in a repeatable format. Generate from it. Compare the output against the spec, not against your vague intent. If the output drifts, fix the spec first when the mistake points to missing scope, unclear constraints, or unstated edge cases.
This is what scales for one person.
Tekk.coach fits that workflow by turning a rough idea into a structured spec you can run through your dev loop, revise, and reuse. Connect the repo, define the job, then iterate on the spec until the output becomes predictable.
Part of the Spec-Driven Development pillar — a 52-page honest playbook on shipping with AI coding agents.

