Spec-Driven Development
Spec-Driven Development β€Ί Module 2: Authoring Machine-Readable Specs
Module 2: Authoring Machine-Readable Specs

Chapter 5: The Anatomy of a Production-Grade `.spec.md`

By Muhammad Riaz

Moving Beyond Vague User Stories

In traditional Agile environments, user stories are deliberately kept lightweight. A ticket on a Jira board might say:

"As a user, I want to filter my order history by date range so that I can track my spending."

For human software engineers, this brief summary works because humans possess implicit domain context. A human developer instinctively knows that a date range filter requires validating that the start date precedes the end date, handling timezone offsets between the client browser and database server, and gracefully rendering a "No orders found" empty state.

An AI model possesses no implicit domain context about your specific business rules or repository invariants.

When fed a lightweight story like the one above, an AI coding agent will make probabilistic guesses for every unspecified decision. It might parse dates as local server time instead of UTC, write a non-indexed SQL query that locks the database table, or omit pagination altogether.

A production-grade .spec.md file is a machine-readable document designed to eliminate guesswork. It acts as a precise functional boundary that translates human business intent into unambiguous constraints for LLM consumption.

The 7 Core Elements of an Executable Spec

To achieve deterministic output from AI coding assistants, every .spec.md file must contain seven structured sections:

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                       ANATOMY OF A PRODUCTION `.spec.md`                    β”‚
β”‚                                                                             β”‚
β”‚  1. METADATA HEADER          ──> Versioning, ownership, status              β”‚
β”‚  2. CONTEXT & PROBLEM        ──> High-level rationale & business value     β”‚
β”‚  3. USER JOURNEY             ──> Step-by-step actor interactions            β”‚
β”‚  4. FUNCTIONAL REQUIREMENTS  ──> Explicit, numbered behavior constraints    β”‚
β”‚  5. SYSTEM INVARIANTS        ──> Non-negotiable technical/business rules    β”‚
β”‚  6. SCOPE EXCLUSIONS         ──> "What NOT to build" (Hard boundaries)      β”‚
β”‚  7. ACCEPTANCE CRITERIA      ──> Verifiable, testable success definitions   β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Let us examine each element in detail.

1. Metadata Header

The metadata header anchors the specification within your version control system. It provides immediate context for human code reviewers and multi-agent parsers.

---
id: SPEC-2026-042
title: Dynamic Order History Date Filtering
status: DRAFT | APPROVED | IN_PROGRESS | COMPLETED
author: Jane Doe (@janedoe)
created: 2026-10-18
updated: 2026-10-18
target_release: v2.14.0
---

2. Context & Problem Statement

Briefly explain why this feature exists. This section prevents "context rot" by aligning the LLM on the broader business objective before diving into technical details.

## Context & Problem Statement
Currently, users with large order histories (100+ transactions) experience slow page load times because the system fetches all historical records at once. Users cannot narrow down orders by custom timeframes, resulting in high customer support ticket volume regarding tax preparation and expense tracking.

3. User Journeys (Primary & Edge Paths)

Map out exact step-by-step interactions. Explicitly defining edge paths prevents the AI from generating code that handles only the "happy path."

## User Journeys

### Primary Path: Custom Date Range Filter
1. User navigates to `/dashboard/orders`.
2. User selects a "From" date and a "To" date using the datepicker input.
3. User clicks "Apply Filter".
4. System updates the URL query parameters (`?startDate=YYYY-MM-DD&endDate=YYYY-MM-DD`).
5. System displays matching orders paginated in sets of 20.

### Edge Path 1: Invalid Date Range

- **Trigger:** User selects a "From" date that is later than the "To" date.

- **System Behavior:** Disable "Apply Filter" button; display inline error: *"Start date must be before end date."* No API request should be dispatched.

### Edge Path 2: Zero Results Found

- **Trigger:** Selected date range yields zero matching records in database.

- **System Behavior:** Display custom empty state component (`<EmptyOrdersState />`) with button to "Reset Filters".

4. Functional Requirements (The "MUST" List)

Write functional requirements using standard RFC 2119 keywords (MUST, MUST NOT, SHOULD, MAY). Number each requirement so AI agents can reference them directly in commit messages or test files.

## Functional Requirements

- **FR-1:** The backend API endpoint `GET /api/v1/orders` MUST accept optional `startDate` and `endDate` query parameters formatted in ISO 8601 (`YYYY-MM-DD`).

- **FR-2:** All date range comparisons in the database query MUST evaluate against UTC timestamps stored in the `created_at` column.

- **FR-3:** The API response MUST include pagination metadata containing `totalCount`, `totalPages`, `currentPage`, and `pageSize`.

- **FR-4:** If no query parameters are provided, the API MUST default to returning orders from the last 30 days.

5. System Invariants & Business Rules

Invariants are rules that must hold true under all circumstances. This is where you protect your application's integrity against common AI assumptions.

## System Invariants & Business Rules

- **INV-1 (Tenant Isolation):** The query MUST ALWAYS restrict results to the authenticated user's `tenant_id` (`WHERE tenant_id = auth.tenant_id`). Cross-tenant leakage is a critical severity failure.

- **INV-2 (Performance Boundary):** Database queries executed by this endpoint MUST use the existing composite index `idx_orders_tenant_created` (`tenant_id`, `created_at`). Full table scans are strictly forbidden.

- **INV-3 (Soft Delete Enforcement):** Records where `deleted_at IS NOT NULL` MUST NEVER be returned in the result set.

6. Explicit Scope Exclusions (The "MUST NOT" List)

Telling the AI what not to build is just as important as telling it what to build. This section directly eliminates scope explosion and dependency creep.

## Explicit Scope Exclusions (What NOT to Build)

- **EX-1:** DO NOT build CSV/PDF export functionality in this specification.

- **EX-2:** DO NOT create new UI charting or spending visualization components.

- **EX-3:** DO NOT install external date-parsing libraries (e.g., `moment.js` or `date-fns`). Use native JavaScript `Date` and Browser Internationalization APIs (`Intl`).

- **EX-4:** DO NOT alter existing database schema or write new migrations.

7. Verifiable Acceptance Criteria

Acceptance criteria must be written as discrete, verifiable checkboxes. AI test-runner agents will use these criteria as acceptance gates.

## Acceptance Criteria

- [ ] `GET /api/v1/orders?startDate=2026-01-01&endDate=2026-01-31` returns HTTP 200 with matching records.

- [ ] Passing a `startDate` greater than `endDate` returns HTTP 400 Bad Request with a structured validation error JSON payload.

- [ ] Requests missing authentication credentials return HTTP 401.

- [ ] Database query execution plan confirms utilization of `idx_orders_tenant_created` index.

- [ ] Unit tests achieve >= 90% branch coverage on the new date-parsing service module.

Real-World Before & After: The Impact of a Production Spec

To see how a complete .spec.md file changes AI agent execution, compare these two real-world runs using an automated CLI coding assistant:

Without Production Spec (Ad-Hoc Prompt)

  • Prompt: "Add date range filtering to the orders table."

  • AI Behavior:

  1. Installs moment.js (adding 70KB to the frontend bundle).

  2. Queries the database using server local time zones.

  3. Omits soft-deletion checks (deleted_at).

  4. Returns all matching records without pagination, crashing dev server on large datasets.

  • Developer Remediation Time: 3.5 hours refactoring, stripping packages, and writing missing tests.

With Production .spec.md (SDD Workflow)

  • Prompt: "Execute feature build according to .specs/SPEC-2026-042.spec.md ."

  • AI Behavior:

  1. Uses native JavaScript date handling (zero new dependencies).

  2. Converts dates to UTC microsecond bounds before querying.

  3. Appends deleted_at IS NULL and tenant_id filters to ORM query.

  4. Implements standard 20-item page limit.

  5. Generates 6 unit tests covering edge cases defined in the spec.

  • Developer Remediation Time: 4 minutes reviewing passing test suite and verifying PR.

Practical Exercises

  1. Convert a Jira Ticket: Take a vague user story from your team's current backlog and rewrite it using the 7 core sections of a production
   .spec.md.
  1. Set Up a Spec Template: Create a .github/spec_template.md file in your main project repository so your team can generate structured specs instantly using a standardized layout.

  2. Audit for Exclusions: Review a recent feature you built with an AI assistant. List three things the AI generated that you had to manually delete or refactor. Write those three items down as Explicit Scope Exclusions to see how they would look in a formal spec.

Next Steps

Now that you know how to structure the full anatomy of a specification file, we must dive deeper into one of its most powerful anti-drift mechanisms: Hard Boundaries & Scope Exclusion.

In Chapter 6, we will master Setting Hard Boundaries: The Art of Explicit Scope Exclusion β€”learning how to write anti-prompts, prevent dependency sprawl, and lock AI models into strict architectural sandboxes.