Agentic Development Part 7: Best Practices — Spec-Driven + Agentic Development
17 min read

Agentic Development Part 7: Best Practices — Spec-Driven + Agentic Development

Twelve articles have built enough of a foundation to discuss how it all comes together. The Spec-Driven Development series covered how to write good specs, API contracts as the source of truth, practical workflows from spec to code, and how acceptance criteria are derived into tests. The Agentic Development series covered agent autonomy, its internal components, task design, context engineering, guardrails, and multi-agent orchestration. This closing article isn’t a re-summary of the previous eleven articles — but a synthesis that faces an often-avoided question: does all of this mean the pipeline from requirements to deliverable can run like a magic machine that automatically produces correct code? The answer is no, and understanding why not — and what the practical consequences are — is actually the core of this entire series.

Not an Abracadabra Machine

A picture forms too easily when someone first reads about the PRD → technical spec → agent execution → deliverable pipeline: put in requirements, wait a moment, out comes correct code. This picture is wrong, and letting it go uncorrected is the fastest way to feel disappointed with the whole approach after trying it.

What actually happens inside that pipeline isn’t a single linear pass. It’s a collection of nested loops, each containing iterations:

flowchart TD
    A[PRD / Requirement] --> B[Draft Technical Spec]
    B --> C{Human Review}
    C -- Revise --> B
    C -- Agreed --> D[Plan / Task]
    D --> E[Agent Executes Task]
    E --> F{Observe / Verify against Spec}
    F -- Gate Fails --> E
    F -- Gate Passes --> G{Task Group Done?}
    G -- Not yet --> D
    G -- Yes --> H{Review Deliverable}
    H -- Spec Miss / Emergent Requirement --> I[Update Spec]
    I --> J{Evaluate Impact on Existing Work}
    J -- Not Affected --> D
    J -- Affected --> B
    H -- Matches Intent --> K[Done]

Iteration happens at three levels at once: within a single task execution (the agent repeats the plan-act-observe loop until the gate passes), between task groups (the cross-task-group checkpoints covered in Part 4 of SDD and Part 3 of Agentic), and at the spec level itself (when deliverable review reveals something that needs updating in the spec before the next iteration starts).

What changes with spec-driven isn’t “iteration is eliminated” — iteration remains and should remain. What changes is the structural quality of that iteration. With vibe coding, iteration happens reactively: see a result that looks wrong, re-prompt with different wording, hope the result improves, with no record of what changed and why. There’s no clear criterion for when iteration can stop, because there’s no agreed definition of “done” from the start.

With spec-driven, every iteration has a clear criterion for when it counts as passed — acceptance criteria written before execution began (SDD Part 2), automated gates verifying them (Agentic Part 5). Iteration stops not when things look okay, but when previously agreed conditions are met.

If your expectation is “a good spec produces correct code in a single execution without iteration”, that expectation needs adjusting before you start. A more accurate expectation: “a good spec makes every iteration verifiable against clear criteria, so the iterations that happen are meaningful and converge on the correct result — not spinning without direction.”

Agent Output Is a Hypothesis, Not Truth

This is the implication most rarely discussed honestly: language models are probabilistic. From the exact same spec, run twice, they can produce two implementations of different quality. Even from an excellent spec, an agent can produce code that passes all existing tests yet still contains wrong design decisions, unanticipated security holes, or edge-case behavior that was never thought of as something needing a test.

Empirical data confirms this isn’t a theoretical concern: a large 2026 study found more than 110,000 AI-introduced issues persisting in production repositories. Another study found that more than 70% of code produced by a certain model contained highest-severity vulnerabilities in security-sensitive contexts. This isn’t an anomaly — it’s a predictable consequence of the probabilistic nature of systems that generate code from probability distributions, not from deterministic understanding of correctness.

The practical implication: agent output should be treated as the best hypothesis producible from the available information, not as a final answer that just needs deploying. This framing changes how verification is treated — not as a formality performed after “the code is done”, but as a hypothesis-confirmation process that determines whether the code is truly done.

flowchart LR
    A[Spec + Context] --> B[Agent Generates Code]
    B --> C[Hypothesis: Implementation Candidate]
    C --> D{Layered Verification}
    D -- Deterministic: Tests, Linters, Contract Tests --> E{Pass?}
    E -- No --> F[Revise Hypothesis]
    F --> B
    E -- Yes --> G{Probabilistic: Reviewer Agent, Human Review}
    G -- Problems Found --> F
    G -- Confirmed --> H[Hypothesis Accepted as Deliverable]

The motivation for the layered verification from Agentic Part 5 (deterministic then probabilistic) is now clearer: automated tests verify what has been anticipated, but because a probabilistic agent can produce decisions never thought of as something needing a test, the probabilistic layers — an independent reviewer agent and human review for risky areas — cover a gap that deterministic tests alone structurally can’t catch.

This also answers the ongoing debate about “are human gates still needed as models get smarter”. The answer isn’t about how smart the model is — a smarter model is still probabilistic. As long as output is probabilistic, independent verification isn’t an option that can be removed as models improve; it’s part of a design that acknowledges the fundamental nature of the system in use.

Overconfidence in agent output — assuming code that “looks right” is definitely right — is one of the most expensive anti-patterns in daily practice. Passing tests prove the code is correct for the cases that were anticipated. No test can prove there are no problems for cases nobody thought of.

Structured Iteration vs Vibe Coding

With the two sections above clear — iteration exists and output is probabilistic — the right question isn’t “how do we eliminate iteration” but “how do we ensure the iteration that happens converges on the correct result, not spinning without direction”.

The structural difference between vibe coding and spec-driven lies in four things:

AspectVibe CodingSpec-Driven
Iteration sourceOutput looks wrong → re-promptGate fails → fix per already-defined criteria
Stopping criterionWhen it subjectively looks okayWhen acceptance criteria are met and gates pass
Iteration recordLost when the session endsCaptured in spec updates and task history
Convergence directionUnclear — can spinDefined — moves toward agreed criteria

The most important of these four differences is “iteration record”. Every time iteration happens in a spec-driven workflow, it leaves a trace: updated specs, revised tasks, documented decisions. The next session — whether by the same agent or a different one — doesn’t need to rediscover the same context, because that context already exists in written form. This is what makes spec-driven scale over time, while vibe coding tends to degrade in quality as complexity grows because context never accumulates persistently.

When the Spec Turns Out Wrong or an Emergent Requirement Appears

This is the question closest to real experience: what if, after the deliverable is executed, it’s discovered that something was missed in the spec, or even that a new need only became visible after touching concrete implementation?

There are two scenarios with different characters that need different handling.

Scenario one: spec miss — something was overlooked, not an intent change. The agent produced something correct per the existing spec, but the spec itself was incomplete. This isn’t an agent failure — the agent did correctly what was asked. This is a bug in the spec. The handling: fix the spec first, evaluate whether the existing code violates this addition or not, then arrange new tasks based on the found gap. Existing code doesn’t automatically have to be thrown away — if the spec addition doesn’t conflict with the existing implementation, just add a task for the missing part.

Scenario two: emergent requirement — a need only knowable after seeing something concrete. There’s a category of requirements that by nature can’t be fully anticipated up front: business edge cases only visible when real flows run, integration constraints hidden behind requirement abstractions, or UX needs only felt once there’s something tangible to touch and try. This isn’t a process failure — it’s the natural nature of software development, acknowledged even by the strictest methodologies.

What changes with spec-driven isn’t “emergent requirements won’t appear” — they still will. What changes is what happens afterward:

Without spec-driven:
Emergent requirement found
→ directly prompt the agent to add/change
→ the agent improvises on top of existing code
→ inconsistencies pile up silently because no contract is updated
→ code gets harder to understand over time

With spec-driven:
Emergent requirement found
→ update the spec first (intent, constraints, new acceptance criteria)
→ evaluate impact on completed tasks
→ only then execute additions or revisions
→ the spec keeps reflecting the system's actual state

The difference isn’t about speed in handling new requirements — spec-driven may be slower at the first step because there’s a spec-update phase before direct execution. The difference is in technical debt accumulation: the “directly prompt without updating the spec” approach creates documentation debt that bloats over time, because the spec and actual code drift further apart. At some point, the spec itself becomes untrustworthy as a source of truth — and once the spec can’t be trusted, the entire foundation of both these series collapses.

An emergent requirement is valuable information, not a nuisance. It reveals something previously unseen about the problem being solved. Treat it seriously: take time to update the spec before direct execution, even if it feels like a step backward. A spec updated based on real findings is far stronger than a spec written only from initial estimates.

One Big Loop: The Complete Map of Both Series

With all the context above, here’s a diagram uniting the entire journey from both series — not as a simple-looking linear pipeline, but as a system of nested loops with explicit control points:

flowchart TD
    PRD[PRD / Business Requirements] --> SpecTeknis

    subgraph SDD["Series 1: Spec-Driven Development"]
        SpecTeknis[Technical Spec: Intent, Constraints,<br/>Acceptance Criteria, ERD, API Contract]
        Plan[Plan: Task Groups with Clear Dependencies]
        Test[Tests from Acceptance Criteria]
        SpecTeknis --> Plan
        SpecTeknis --> Test
    end

    subgraph Agentic["Series 2: Agentic Development"]
        TaskDesign[Individual Tasks: Right Granularity,<br/>Explicit Definition of Done]
        Context[Context Engineering: Convention Files,<br/>Persistent Memory]
        Eksekusi[Agent Execution: Plan-Act-Observe Loop]
        Guardrail[Guardrails: Automated Gates,<br/>Independent Reviewers]
        Plan --> TaskDesign
        TaskDesign --> Context
        Context --> Eksekusi
        Eksekusi --> Guardrail
    end

    Guardrail --> Review{Review Deliverable}
    Test --> Guardrail

    Review -- Matches Intent --> Done[Deliverable Accepted]
    Review -- Spec Miss --> SpecTeknis
    Review -- Emergent Requirement --> SpecTeknis

    CheckpointManusia[Mandatory Human Review Checkpoint] -.guards.-> SpecTeknis
    CheckpointManusia -.guards.-> Review

Every connection in this diagram references the article that discusses it in depth. The technical spec to SDD Parts 2 and 3; the plan to SDD Part 4; task design to Agentic Part 3; context engineering to Agentic Part 4; agent execution to Agentic Part 2; guardrails to Agentic Part 5; multi-agent orchestration to Agentic Part 6; and the two arrows back from review to the technical spec form the loop explicitly discussed for the first time in this closing article.

Core Principles Recurring Across Both Series

Several principles appear repeatedly across various articles in different contexts. Drawn out explicitly:

Ambiguity costs proportionally to autonomy. This principle appears in SDD Part 1 (why specs matter), Agentic Part 1 (why specs are the prerequisite for autonomy), and Agentic Part 6 (why technical contracts must be agreed before multi-agent execution). The greater the autonomy given, the more expensive the ambiguity gaps left unresolved up front.

Checkpoints are cheaper than fixing at the end. Appears in SDD Part 4 (review mid-execution), Agentic Part 3 (per-task definitions of done), Agentic Part 5 (automated gates before continuing). Finding problems earlier is always cheaper than finding them after lots of work has been built on top.

Shared contracts prevent drift. SDD Part 3 (OpenAPI as an executable contract), Agentic Part 4 (context engineering ensuring all agents reference the same information), Agentic Part 6 (the technical spec as the cross-agent consistency guard). Drift happens when each party fills gaps with its own assumptions; explicit contracts eliminate those gaps.

Probabilistic output requires independent verification. SDD Part 5 (agents passing tests but drifting from intent), Agentic Part 5 (self-verification insufficient due to confirmation bias), this article (output as a hypothesis needing confirmation). The probabilistic nature isn’t a bug that smarter models will fix — it’s a fundamental characteristic that determines how verification must be designed.

Specs are living, not static documents. SDD Part 4 (spec updates as part of the definition of done), Agentic Part 4 (stale context files are more dangerous than none), this article (emergent requirements handled via spec updates, not direct code patches). A spec that isn’t maintained loses its function as a source of truth.

Readiness Checklist: Before Raising Autonomy Levels

Before raising agent autonomy levels (from per-step approval to async execution, or from single agent to multi-agent workflow):

SPEC:
  □ The technical spec is written with all four elements:
    intent, constraints, acceptance criteria, non-goals
  □ Acceptance criteria are written in a testable format
    (verifiable pass/fail, not qualitative statements)
  □ API contracts/data schemas are explicitly defined
    and saved as reference files
  □ Non-goals explicitly state what's out of scope

TASKS:
  □ Tasks are broken down with boundaries aligned to real dependencies,
    not just cut by size
  □ Every task has a definition of done checkable as pass/fail
  □ Dependencies between tasks are explicitly mapped
  □ Per-task scope boundaries are stated (not only at the spec level)

CONTEXT:
  □ A project convention file exists and reflects actual conditions
    (not stale or auto-generated without curation)
  □ Project conventions cover what agents need to know
    without reading the whole codebase from scratch

GUARDRAILS:
  □ Test coverage includes all major acceptance criteria
  □ Contract tests verify conformance to the agreed schemas
  □ Automated gates are installed as mandatory conditions before continuing tasks
  □ Escalation criteria to humans are explicitly defined
  □ The agent execution environment is isolated from production

MULTI-AGENT (additional before building orchestration):
  □ A single agent with well-designed tasks has been tried
    and proven insufficient for this work
  □ The sub-jobs to be parallelized are genuinely independent
  □ A state manager is available to prevent context loss at handoffs
  □ The orchestration pattern is chosen based on need, not the assumption
    that more complex means better

The Most Expensive Cross-Series Anti-Patterns

Not repeating per-article anti-patterns, but the failure combinations that most often appear together and produce the largest impact:

Ambiguous spec + high autonomy + no guardrails. The worst combination from all three series — spec ambiguity gives the agent free interpretation room, high autonomy lets that interpretation execute long before detection, and no guardrail catches the deviation before it spreads. Every article in both series is essentially a mitigation for one corner of this combination.

Agent output trusted as final without independent verification. Treating code that “looks right” or “passes tests” as done without a verification layer independent of the process that produced it — ignoring the probabilistic reality discussed above.

Specs written once, never updated. A spec accurate at the start but never maintained as code changes is a trap that looks good early in a project but delivers growing problems over time.

Multi-agent built before a single agent is seriously tried. Adding multi-agent orchestration complexity as the first step, before evidence that a single agent with well-designed tasks is insufficient — wasting resources on coordination that wasn’t actually needed.

Emergent requirements handled with direct patches without spec updates. Every new requirement handled directly in code without fixing the spec first widens the gap between spec and actual code, until the spec can no longer be trusted as a source of truth.

Context engineering ignored until consistency problems appear. Relying on agents to “know on their own” project conventions from reading existing code, without an explicit context file, produces consistency that depends on how lucky each session’s initial agent exploration happens to be.

Measuring Success: Signals That This Workflow Is Working Well

No complex dashboard needed. A few simple signals indicate this workflow is healthy:

Positive signals:

  • Specs and code stay in sync — reading the spec gives an accurate understanding of code behavior, without needing to read the code itself
  • New sessions can start without re-explaining project context from scratch, because context already exists in convention files and specs
  • When something goes wrong, it can be quickly attributed to an incomplete spec or an implementation deviating from the spec — not “somewhere in there”
  • The iteration that happens converges — each round brings closer to the agreed criteria, not spinning in place

Warning signals:

  • The spec was last updated long before the code was last changed — this gap is a measure of how stale the spec has become
  • The same agent produces the same inconsistency across different sessions, not improving even after being “fixed” — a sign the fix never entered persistent context
  • Deliverable reviews always find the same problems repeatedly — a sign acceptance criteria aren’t specific enough or gates aren’t strict enough
  • Time spent debating “is this per the requirements” exceeds time spent doing the requirement itself — a sign the spec is still too ambiguous

Where to Start: A Gradual Adoption Path

For teams just starting, jumping straight into a multi-agent workflow with full specs and complete guardrails is a strategy likely to fail not because the approach is wrong, but because too many new things must be mastered at once. A more sensible sequence:

Stage 1 — Start with a spec for one small feature. Pick one feature with clear, not-too-large scope. Write a complete spec with the four elements (intent, constraints, acceptance criteria, non-goals) as in SDD Part 2. Do the work the way you’re used to, but use the spec as a reference while executing and verifying. Feel the difference versus working without a spec.

Stage 2 — Add API contracts/data schemas. Once feature specs feel natural, start formalizing API contracts and data schemas as in SDD Part 3. This investment’s impact is immediately felt when working with more than one component.

Stage 3 — Raise agent autonomy gradually, starting from the most well-defined tasks. Identify tasks whose acceptance criteria are clearest and test coverage strongest. Give the agent more autonomy on these tasks, while keeping manual review checkpoints for more complex or risky tasks.

Stage 4 — Build a project convention file. After several features are done, what patterns are often needing re-explanation to agents? Document those in a convention file as in Agentic Part 4. This is usually an investment whose benefit is immediately felt in the next session.

Stage 5 — Formalize guardrails as mandatory gates. Make sure the test suite is strong enough to serve as an automated gate that must pass before a task counts as done. This condition must be met before raising autonomy further.

Stage 6 — Consider multi-agent only when a single agent is proven insufficient. At this point, there’s already a mature spec, maintained context, and reliable guardrails — a foundation that makes multi-agent complexity manageable, rather than adding problems on top of a fragile foundation.

There’s no adoption order that “must” be followed exactly like this. What matters is the principle: build the foundation (mature specs and reliable guardrails) before raising autonomy, not raise autonomy first and hope the foundation forms on its own.

Series Closing

These twelve articles started from one simple premise: ambiguity is the main enemy when AI agents work with growing autonomy. Spec-Driven Development provides the mechanism to resolve that ambiguity up front, in a persistent, repeatedly referenceable form. Agentic Development provides the mechanism to delegate execution in a structured, verified, correctable way when something goes wrong.

But more important than those mechanisms is the shift in mindset: from “I ask AI to produce X” to “I define X precisely, delegate execution with clear control, and verify the result against that definition”. From “try until it looks right” to “iterate until agreed criteria are met”. From trusting agent output as truth to treating it as a hypothesis needing confirmation.

This mindset shift doesn’t happen automatically by installing a certain tool or following a certain template. It happens when, in daily work, there’s a habit of resolving ambiguity in written form before delegating execution — and a habit of evaluating results against pre-existing criteria, not against expectations formed in your head after seeing the result.


Summary

From the Spec-Driven Development series:

  • A spec is the source of truth that precedes code — intent, constraints, acceptance criteria, and non-goals are the four elements always present
  • Testable acceptance criteria (EARS notation) can be directly derived into test cases without additional interpretation
  • API contracts and data schemas (OpenAPI, JSON Schema, Protobuf) are executable specs — automatically validatable, not just documentation
  • Specs are living: updating the spec is part of the definition of done, not a postponable administrative step

From the Agentic Development series:

  • A coding agent = a plan-act-observe loop with tool use; probabilistic in nature, not deterministic
  • Agent output is a hypothesis, not truth — layered verification (deterministic + independent) is how it gets confirmed
  • Context engineering (curated convention files, persistent memory, just-in-time retrieval) determines result consistency more than per-session prompt engineering
  • Effective guardrails work as fences limiting blast radius, not barriers killing autonomy
  • Multi-agent isn’t an automatically better default — only worth it when the work’s complexity genuinely requires it

Principles uniting both:

  • The PRD→deliverable pipeline isn’t an abracadabra machine — iteration remains, but structured and verified, not reactive and directionless
  • Ambiguity costs proportionally to autonomy — resolve it before delegating, not after
  • Emergent requirements are handled via spec updates first, not direct code patches
  • Build mature specs and guardrails before raising autonomy, not the other way around

Portfolio