# Spec-Driven Development: Let the Spec Drive the Code (With a Real Example)

> Source: <https://dev.to/sergiocolqueponce/spec-driven-development-let-the-spec-drive-the-code-with-a-real-example-589n>
> Published: 2026-06-17 18:26:46+00:00

By

Sergio Colque Ponce— Software Engineering, Universidad Privada de Tacna.

Full source code:[github.com/srg-cp/spec-driven-development]

If you have used an AI coding agent — Copilot, Claude Code, Gemini CLI — you have probably lived this moment: you describe a feature, the agent produces code that compiles and *looks* right, and then it quietly does the wrong thing. The agent is not weak; the **input** was ambiguous. We have been treating coding agents like search engines when they behave more like very literal pair programmers.

**Spec-Driven Development (SDD)** is the answer to that problem: instead of jumping straight to code, you write down *what* you want and *why*, refine it, and only then let the implementation follow. The specification — not the code — becomes the center of the project.

The idea is old (anyone who has written a Product Requirements Document will recognize it), but it has become practical again thanks to tools like GitHub's open-source [Spec Kit](https://github.com/github/spec-kit). Spec Kit organizes the work into a small set of Markdown artifacts, each feeding the next:

The workflow is usually summarized as **Spec → Plan → Tasks → Implement**, and the same process is meant to work regardless of language, framework, or which of the 30+ supported agents you use.

The real shift is not "more documents." It is this: when requirements change, you **update the spec, regenerate the plan, and let the implementation follow** — instead of patching code and hoping the intent survives. The spec is a *living* artifact, not a dusty Word file nobody opens.

You do not need Spec Kit to practice SDD. The underlying principle is simple and tool-agnostic:

The specification is the single source of truth. The code and the tests are derived from it — never the reverse.

The most powerful version of this is an **executable specification**: a machine-readable contract that your tests and CI can check the implementation against automatically. If the code drifts away from the spec, the build fails. That is what turns a spec from documentation into a guardrail.

Let me show exactly that with a small, real project you can run yourself.

We want a `/shorten`

endpoint: send a long URL, get back a short code. The SDD move is to **write the contract first**, before any logic. Here it is as an OpenAPI document (`spec/openapi.yaml`

):

```
components:
  schemas:
    ShortenRequest:
      type: object
      required: [url]
      additionalProperties: false
      properties:
        url:
          type: string
          format: uri
          minLength: 1
    ShortenResponse:
      type: object
      required: [original_url, short_code]
      additionalProperties: false
      properties:
        original_url:
          type: string
        short_code:
          type: string
          description: Exactly 7 lowercase hex characters.
          pattern: '^[0-9a-f]{7}$'
```

Notice we already decided the *shape* of success: a response must contain `original_url`

and a `short_code`

of exactly seven hex characters. No code exists yet, but the rules are unambiguous.

Now the code simply honors the contract. Its job is to satisfy `ShortenResponse`

, not to invent its own format:

``` python
import hashlib

class InvalidURLError(ValueError):
    """Raised when the input violates the ShortenRequest schema."""

def shorten(url: str) -> dict:
    if not isinstance(url, str) or not url:
        raise InvalidURLError("url is required and must be a non-empty string")
    short_code = hashlib.sha256(url.encode("utf-8")).hexdigest()[:7]
    return {"original_url": url, "short_code": short_code}
```

This is where SDD stops being a slogan. The tests **load the spec** and validate the implementation against it. The assertions are not hand-copied from the code — they come straight from the contract:

``` python
import yaml, jsonschema
from openapi_spec_validator import validate as validate_openapi
from shortener import shorten, InvalidURLError

SPEC = yaml.safe_load(open("spec/openapi.yaml").read())

def schema(name):
    return SPEC["components"]["schemas"][name]

def test_spec_is_a_valid_openapi_document():
    validate_openapi(SPEC)              # the contract itself must be valid

def test_response_conforms_to_the_spec():
    result = shorten("https://upt.edu.pe/admision/2026")
    jsonschema.validate(instance=result, schema=schema("ShortenResponse"))
```

If someone later changes `shorten()`

to return, say, an 8-character code, the `pattern`

in the spec no longer matches and `test_response_conforms_to_the_spec`

fails immediately. The spec is now *enforced*.

A small GitHub Actions workflow validates the contract and runs the tests on every push. The spec is checked by a machine, on every change, forever:

```
name: CI
on: [push, pull_request]
jobs:
  spec-and-tests:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with:
          python-version: '3.12'
      - run: pip install -r requirements.txt
      - run: python -c "import yaml; from openapi_spec_validator import validate; validate(yaml.safe_load(open('spec/openapi.yaml')))"
      - run: pytest -v
```

Running the suite locally is just as honest:

```
tests/test_contract.py::test_spec_is_a_valid_openapi_document PASSED
tests/test_contract.py::test_response_conforms_to_the_spec PASSED
tests/test_contract.py::test_short_code_matches_the_specified_pattern PASSED
tests/test_contract.py::test_invalid_request_is_rejected PASSED
```

Spec-Driven Development shines on **greenfield** work (starting from zero, where it is tempting to just start typing), on features with **real consequences** where "looks right" is not good enough, and on any project handed to an AI agent that needs unambiguous intent rather than a vague prompt. It is overkill for a throwaway script — but for anything you will maintain, the spec pays for itself the first time a requirement changes.

One honest caveat: keep a **human in the loop**. SDD with AI does not mean blind trust. You still review the spec, the plan, and the generated code at the critical steps.

Spec-Driven Development is less about a specific tool and more about reversing the usual order: decide *what* is correct, write it down where machines can check it, and let the code and tests follow. Whether you adopt the full Spec → Plan → Tasks → Implement workflow with Spec Kit, or just keep an OpenAPI contract that your CI enforces, the payoff is the same — your software does what you actually meant, and it keeps doing it after the next change.

The complete, runnable project is here: ** github.com/srg-cp/spec-driven-development**. Clone it, change the

`short_code`

pattern in the spec, run the tests, and watch the contract catch you.Thanks for reading!
