AGENOVA
guide

The design decisions your coding agent made without asking

Storage, retries, boundaries, partial failures: an agent asked for a feature decides them all in code. Why the spec has to come first, and be short.

Agenova Editorial7 min read

You asked for a feature and got a design

Ask a coding agent for a feature and you get code, immediately and in volume. It compiles, it reads well, and it often solves a problem next to the one you actually have.

The agent is not bad at coding. What happened is quieter: on the way to that code it made a dozen design decisions on your behalf. Where the data lives. What happens on a timeout. Whether a duplicate request is refused or processed twice. Whether this is an event or a scheduled job. Which existing module it reused, or did not know existed. None of those decisions was visible as a decision. Each arrived already spread across the diff.

Review happens at the worst moment

That changes what review can do. A reviewer looking at a finished diff is judging whether the code is correct, and reviewers are much better at that than at judging whether the code should exist in this shape at all.

By then the effort is sunk and the diff is too large to argue with. "This should have been an event, not a cron job" is a sentence before the code exists and a rewrite after. So the comment is not made, or it is made and deferred, and the design that nobody chose becomes the design the system has.

Two kinds of defect slip through this way with particular regularity:

  • The second implementation. The agent writes a clean retry loop inline, unaware that the codebase already has a retry helper with different defaults. Both paths now behave differently under the same outage, and neither file shows it.
  • The catch-all branch. A mapping handles most of its cases and closes with an else that returns a plausible default. The missing case does not fail — it silently does the wrong thing. A requirement written as "route to the correct variant" survived review precisely because nobody can disagree with it.

Move the decision before the code

The remedy is to split the work in two. Before writing implementation code, the agent produces a specification and stops. A person accepts it, or rejects it, and only then does implementation start. The design becomes a paragraph you can reject in seconds instead of a diff you have to unpick.

This only works if the specification is built to be rejected. That rules out the long, polished design document. A spec nobody finishes reading is not reviewed, it is approved by fatigue. The useful version is short and forces the decisions an agent leaves implicit when nobody asks:

  1. What is out of scope. Disagreement lives in the exclusions far more often than in the list of things to build. A spec without exclusions has not been thought through.
  2. The failure modes. A partial write, a timeout, a duplicate request, a malformed payload. Left alone, generated code assumes the happy path and wraps the rest in a generic catch.
  3. The data that moves. Which store owns what, what is written, what is read, what is now duplicated. Most architectural regret is about data ownership.
  4. Why this might be wrong. One honest paragraph naming a condition under which the design is the wrong one. If none can be named, the design is not understood well enough to build.

Two more habits make the spec carry information. Every sentence should be one a reviewer could disagree with — "handle errors gracefully" cannot be rejected, so it decides nothing. And the agent should say what it searched for in the existing code before proposing something new, because naming the search is what lets a reviewer say "we already have that".

Do not ask for a spec for everything

A process that demands a specification for a one-character fix teaches the team to route around it. Typos, renames with no behavioural change, dependency bumps that do not change an API you call, a small fix whose cause and effect are both obvious, a revert: these should go straight through. The exemption list is part of what keeps the rule respected.

The cost of the rest is one extra round trip before implementation, which is small next to a feature-sized change.

Check the code against what was accepted

Accepting a spec is half the loop. The other half is checking the implementation against it: for each decision the spec records, point at the code that implements it. A short table — decision, location, status — makes a missing decision visible as an empty cell instead of an absence nobody notices.

And let the spec go once the code merges. A specification kept as documentation slowly diverges from the code it described, and the next person to read it builds against a system that no longer exists. Its job was to make one decision reviewable at one moment.

Where to start

Spec-First Architect Ruleset turns this into four Cursor rules: rules/00-spec-before-code.mdc, the always-on gate with its exemption list; rules/10-architecture-constraints.mdc, for designs that would quietly violate the system they land in; rules/20-spec-conformance-review.mdc, the decision-by-decision check against the accepted spec; and rules/30-spec-format.mdc, which keeps specs short enough to be read. It ships SPEC-TEMPLATE.md to adapt to how your team argues about design, EXAMPLE.md with one task from request to conformance check, and ANTI-PATTERNS.md with the six failures the rules were written from. The rules are provider-neutral and work as plain instructions outside Cursor too.

If the agents you are specifying also read untrusted content, why your agent cannot tell your instructions from the text it reads covers the design constraint that adds.

Share this page

Put this into practice.

Spec-First Architect Ruleset

This guide has a matching toolkit, built and maintained for production use.

$29.99

Open Spec-First Architect Ruleset

Read next.