Beyond the Hype: Practical Spec-Driven Development with AI Agents for Traceable Code Delivery A developer outlined a Spec-Driven Development (SDD) methodology for using AI agents to generate production-grade code, replacing ad-hoc "vibe coding" with machine-readable JSON specifications that serve as the single source of truth. The approach derives test scenarios directly from spec inputs and expected outputs, wraps the non-deterministic agent loop in deterministic guardrails, and requires human approval before any code reaches production. The reference implementation uses Python and a tool-using LLM operating in a sandboxed environment. Originally published on tamiz.pro https://tamiz.pro/insights/spec-driven-development-ai-agents-practical-guide . The era of "vibe coding"—where developers prompt an LLM, review the output, and push it to production without a structured rationale—is colliding with enterprise realities. Systems are too complex, security audits are too rigorous, and the cost of silent hallucinations in code generation is too high. To move from experimental AI assistance to reliable, production-grade software delivery, engineering teams must shift from an output-first mindset to an intent-first methodology: Spec-Driven Development SDD . This deep-dive explores how to implement SDD using AI agents, transforming natural language requirements into executable, machine-readable specifications. We will dissect the architecture, the data contracts, and the deterministic validation loops required to build an evidence-backed pipeline that guarantees traceability from initial intent to the final deployed artifact. In traditional AI-assisted coding, the prompt is ephemeral; the context is limited to the model's memory window or the immediate conversation state. SDD replaces this fragility with a structured contract. Instead of asking an agent "write a function to handle user authentication," the system defines a JSON schema that explicitly dictates the input boundaries, error handling, and expected state mutations. The architecture rests on three core pillars: LLMs are non-deterministic. To achieve traceable code delivery, the agent loop must be wrapped in deterministic guardrails. The process flows as follows: The quality of SDD is entirely dependent on the quality of the specification. Vague specs produce vague code. We must use structured data formats that are easy for humans to review and precise enough for machines to enforce. Consider a payment processing feature. A natural language prompt might say: "Process a credit card payment and handle failures." A spec-driven contract for this feature must explicitly define the data flow. Below is a simplified example of a FeatureSpec object. This JSON serves as the single source of truth for both the human developer and the AI agent. { "specId": "PAY-2023-001", "version": "1.0.0", "intent": "Process a credit card payment via Stripe", "inputs": { "type": "object", "properties": { "amount": { "type": "integer", "minimum": 1, "description": "Amount in cents" }, "currency": { "type": "string", "pattern": "^ a-zA-Z {3}$" }, "cardToken": { "type": "string" } }, "required": "amount", "currency", "cardToken" }, "constraints": "Amount must not exceed the user's verified limit." , "expectedOutputs": { "type": "object", "properties": { "status": { "enum": "success", "declined", "pending" }, "transactionId": { "type": "string", "format": "uuid" } } }, "errorContract": { "failureModes": "INSUFFICIENT FUNDS", "CARD EXPIRED", "NETWORK TIMEOUT" } } In SDD, tests are not written after the code; they are derived from the specification. Before any code is generated, the system parses the inputs and expectedOutputs to generate a matrix of test scenarios. minimum and maximum values. amount: -5 , currency: "USD" . errorContract failure modes. By treating the spec as a generator for tests, we ensure that the AI agent's success criteria are mathematically defined, removing human bias from the code review process. The agent is not just a text generator; it is an orchestrator of tools. We will outline a reference implementation using Python and an LLM capable of tool use. The agent operates in a sandboxed environment where it can read files, execute tests, and query the database schema, but cannot push code to production without human approval. The agent must maintain a "Working Memory" that contains the spec, the current state of the code, and the history of previous failures. This prevents the agent from entering a loop where it repeatedly tries the same failing code modification. python class SpecDrivenAgent: def init self, llm client, spec path, repo context : self.llm = llm client self.spec = load json spec path self.repo = repo context self.history = Keeps track of past attempts and errors def execute self : 1. Generate Test Cases based on Spec test suite = self.derive tests from spec self.spec 2. Initial Plan Generation plan = self.llm.generate code plan self.spec, self.repo 3. Static Validation of Plan if not self.validate plan against constraints plan : raise SpecViolationError "Proposed plan violates business constraints." 4. Code Generation & Execution Loop max retries = 3 for attempt in range max retries : code = self.llm.generate code plan, test suite, self.history self.repo.apply code code test results = self.repo.run tests test suite if test results.passed: self.generate evidence log test results, plan return "SUCCESS" else: Append the specific failure context to history This forces the LLM to look at the exact failing assertion self.history.append { "code attempted": code, "failure log": test results.stderr, "attempt": attempt } raise AgentExhaustedError "Failed to satisfy spec constraints." The validate plan against constraints function is the critical safety net. Before the LLM writes code, it must prove that its plan aligns with the architectural rules. This validation is deterministic and does not rely on the LLM. doNotTouch array. If the LLM plans to modify core/database.py when the spec dictates changes to services/payment.py , the static validator rejects the plan. The core value proposition of SDD for enterprise engineering is traceability. When an audit asks, "Why was this database query written with a LIMIT 100 instead of LIMIT 50 ?", the system must be able to provide the exact chain of evidence. Every time the agent generates code, it outputs an EvidenceLog . This is an immutable record that links the spec ID to the code commit. { "specId": "PAY-2023-001", "commitHash": "a1b2c3d", "generationTimestamp": "2023-10-27T10:00:00Z", "modelVersion": "claude-3-opus", "testMatrix": { "testName": "test payment success", "status": "passed", "duration": "0.4s" }, { "testName": "test card declined", "status": "passed", "duration": "0.1s" } , "reasoning trace": "Step 1: Analyzed spec. Payment amount must be 0.", "Step 2: Generated initial implementation.", "Step 3: Test 'test insufficient funds' failed due to missing status code 402.", "Step 4: Modified exception handler to return 402 as per spec.errorContract." } By storing the reasoning trace , you create a human-readable audit trail that mirrors the LLM's decision-making process. For deeper dives into how to manage LLM observability and audit logs at scale, see tamiz.pro https://tamiz.pro . In continuous integration CI , the Evidence Log is attached to the Pull Request. If the tests pass, the PR is automatically tagged with the spec IDs that it satisfies. This creates a bidirectional link: While SDD is powerful for discrete, well-defined features, it faces challenges in complex, stateful systems. AI agents frequently fail when they need to understand the current state of a database. In SDD, the spec must include a ContextState section. Before code generation, the agent is provided with a read-only snapshot of the database schema via migration files or DBML and sample data. If the spec requires altering the database schema, the evidence log must include the exact migration file generated by the agent. This migration is then reviewed by a human DBA before execution, maintaining the human-in-the-loop requirement for data integrity. For microservices, the spec must define the event contracts. The expectedOutputs in the spec should not just be HTTP responses, but also emitted domain events. PaymentProcessed event to the payments topic." Implementing SDD requires strict security boundaries. Agents must never run with production credentials. Each agent execution should happen in an ephemeral CI runner e.g., GitHub Actions ephemeral container, or a disposable AWS Fargate task . environment configuration. The spec file is a critical data asset. It contains business rules that define the system's behavior. Therefore, spec files must be treated with the same security clearance as source code. Use version control Git with branch protection. Require human code review for spec changes, even if the spec is generated by another agent. Spec-Driven Development is not a one-time migration. It is an evolution of how we interact with software. As discussed in Tamiz's Insights https://tamiz.pro/insights , the transition to agentic workflows requires a fundamental shift in developer identity. We are no longer writing code; we are defining the constraints within which code is generated. The engineers who master this shift will define the next decade of software architecture. TDD dictates that you write the test before the code. SDD elevates this: you write the specification intent, inputs, constraints, expected outputs before the test. The test is an artifact generated from the spec. TDD is a practice; SDD is a comprehensive architectural workflow that includes test generation, code generation, and evidence logging. Agents can handle service implementations, but the interface and domain model must be highly refined in the spec. Attempting to generate a complex, novel domain model with an LLM without a detailed, pre-approved spec often leads to architectural drift. SDD works best when the domain logic is stable and the data contracts are clearly defined. Hallucinations are mitigated by the deterministic validation loop. The LLM might hallucinate a code structure, but the static validator and the execution of the spec-derived test matrix will catch the hallucination immediately. The evidence log then provides the exact point of failure, forcing the agent to correct itself within a bounded number of retries.