# Spec-to-Code Pipeline

The controlled path from specification through tasks and tests to implementation, with agents doing generation and humans gating transitions.

**Rests on:** Product Requirements Documents, Task Cards

**Leads to:** Review Gates, Spec Drift

*Software Engineering · Spec-Driven Development*


## Why learn this

This is the foundation for everything you'll do with AI coding agents. Mastering the spec-to-code pipeline unlocks structured project management: breaking a vague idea into a clear written plan (the specification), listing the specific jobs needed to build it (task cards), and defining exactly what success looks like before any code is written (acceptance criteria). You'll be able to have an AI agent write code that actually matches your plan, and catch mistakes before they become costly bugs. Without this discipline, you risk 'vibe coding' — guessing at the outcome based on feel rather than facts. This approach lets you work fast at first, but prevents you from drowning in uncontrolled changes as your project grows.


## The intuition

Think of building software as baking a complex cake. If you just say 'make me a nice cake' to an AI, you might get something edible, but it won't be what you wanted. The spec-to-code pipeline is like writing a detailed recipe first, listing every ingredient and step, and then having a sous-chef follow that recipe exactly. Each step produces something you can check: the recipe (spec), the list of tasks, the test to see if it's right, and finally the cake itself. The AI is the fast worker; you are the quality control. The pipeline ensures the AI doesn't skip steps or add its own creative flair unless you approve it. It's the difference between chaos and control.


## An analogy

Imagine you're the architect for a new house, and you hire a brilliant but very literal contractor (the AI). If you just say 'build me a nice house,' you'll get something, but it might have a bathroom in the kitchen and no windows. The spec is your detailed blueprint. The pipeline is the process: first, you turn your vague idea into a clear blueprint (the spec). Then you break the blueprint into specific jobs for the contractor (task cards — 'lay the foundation,' 'install the plumbing'). You also write the inspection tests ('does the door open and close?' 'is there a sink in the bathroom?'). The contractor works through the tasks, and after each one, you inspect it against the test before letting them move on. The blueprint, the task list, and the test results are all kept in a log (version control), so if the contractor did something you didn't ask for, you can point to the line in the blueprint. This stops the contractor from secretly building a pool when you asked for a garden. The analogy breaks down because the AI doesn't get tired or have opinions, but it will happily do the wrong thing if you're not precise. You can't just say 'build it nicely' — you must say 'place the door on the north wall, two metres from the corner.'


## Definition

The spec-to-code pipeline is the controlled path that takes a written specification and converts it into working code through a series of reviewable stages — specification, task breakdown, test definition, and implementation — where an AI agent does the generation and a human reviews and approves each transition.


## How it connects

You haven't met any of the other topics yet, but you already have the core idea. This pipeline is the engine that makes the whole approach work. The Engineering Intent Document (a high-level summary of why we are building this) is the seed, the Product Requirements Document (the detailed list of what the software must do) is the full blueprint, and the pipeline is how you get from there to code. The Spec Drift (when the final product stops matching the original plan) you'll learn about is the disease, and the Review Gates (checks that stop work until quality standards are met) and Traceability (the ability to link every piece of code back to a specific requirement) are the medicine. Task Cards are what the pipeline hands to the agent. Acceptance Criteria are the tests you write. Right now, it's enough to see the whole process as one connected machine.


## The mathematics

The mathematics here isn't about numbers — it's about structure and relationships. The branch is **graph theory**, which studies networks of connected things. We're using it to model the dependencies between stages. The key idea is that a series of steps where each step's output becomes the next step's input is a 'path.' The pipeline is a path from 'spec' to 'code.' Each step 'points to' the next, like a chain. The maths helps us define what it means for the path to be 'complete' — meaning every requirement is linked to a task, that task to a test, and that test to code. It's like a flow chart, but with the strict rule that nothing moves forward until every connection is checked.

A full trace is: for every requirement r, there exists a task t, a test tst, and code c such that: r → t → tst → c. The '→' means 'is implemented by'. The pipeline is complete only if this holds for all r in the set of requirements.


## A worked example

```
You have 3 requirements (R1, R2, R3). The pipeline has 4 stages: Spec -> Tasks -> Tests -> Code.

Step 1: Requirement R1 maps to Task 1 and Task 2. (R1 -> T1, T2)   # Each requirement must be broken into one or more tasks.
Step 2: Task 1 maps to Test A. Task 2 maps to Test B. (T1 -> A, T2 -> B)   # Every task needs a test to prove it's done.
Step 3: Test A maps to Code File 1. Test B maps to Code File 2. (A -> C1, B -> C2)   # Each test is verified against a piece of code.
Step 4: All 3 requirements are traced to code. (R1->C1, R2->C2, R3->C3). The path is complete.   # The path from every requirement to code is unbroken.

Result: The pipeline is 'complete' for all 3 requirements — every one has code that can be tested against it. That's a 100% traceability score.
```


## From scratch, in Python

```python
first_name = "Priya"
last_name = "Sharma"

# 1. The specification: a list of requirements (strings)
spec = [
    "The system shall greet the user by name.",
    "The system shall say goodbye when the user leaves."
]

# 2. A dictionary to hold traceability links.
#    Keys are requirement IDs, values are task IDs.
trace_spec_to_tasks = {}

# 3. Break the spec into task cards (each is a small, self-contained job)
tasks = {
    "T1": {"requirement": "The system shall greet the user by name.", "description": "Write a function that takes a name and prints a greeting."},
    "T2": {"requirement": "The system shall say goodbye.", "description": "Write a function that prints a goodbye message."}
}

# Link each requirement to its tasks.
trace_spec_to_tasks["R1"] = ["T1"]  # R1 is the first requirement
trace_spec_to_tasks["R2"] = ["T2"]

# 4. Write tests for each task.  Each test is a function that returns True/False.
def test_greeting():
    # 'captured_output' is a string we build by calling our future function.
    # We'll simulate it for now.
    captured = "Hello, Priya!"
    return captured == "Hello, Priya!"

def test_goodbye():
    captured = "Goodbye, Priya!"
    return captured == "Goodbye, Priya!"

tasks["T1"]["test"] = test_greeting
tasks["T2"]["test"] = test_goodbye

# 5. Implement the code (as simple functions)
def greet(name):
    return f"Hello, {name}!"

def goodbye(name):
    return f"Goodbye, {name}!"

# 6. Run the pipeline: run each task's test against the implementation
#    and record the result in a dictionary.
results = {}
results["T1"] = tasks["T1"]["test"]()
results["T2"] = tasks["T2"]["test"]()

# 7. Print a traceability report
print("Traceability Report")
for req, task_ids in trace_spec_to_tasks.items():
    for tid in task_ids:
        passed = results.get(tid, False)
        print(f"{req} -> {tid}: {'PASS' if passed else 'FAIL'}")
```


## What the code does

- **Line 1-2**: We create two variables (`first_name`, `last_name`) – these are containers for text.
- **Line 4-7**: `spec` is a **list** – an ordered collection of strings.  We use square brackets `[]` and separate items with commas.
- **Line 9-11**: `trace_spec_to_tasks` is a **dictionary** – it maps keys (like `'R1'`) to values (like `['T1']`).  We use curly braces `{}`.  We'll fill it later.
- **Line 14-17**: `tasks` is a dictionary where each value is itself a **dictionary** – this is a **nested** structure.  Keys are task IDs (`'T1'`), values have `requirement`, `description`, and later `test`.
- **Line 20-21**: We add entries to our trace dictionary.  `trace_spec_to_tasks['R1'] = ['T1']` links requirement R1 to task T1.
- **Line 24-29**: `def test_greeting():` defines a **function** – a reusable block of code.  Functions start with `def`, have a name, parentheses, and a colon, and the body is indented.
- **Line 25-27**: We simulate what the greeting function would output.  `==` means 'is equal to'.  The function returns `True` if the condition is met.
- **Line 32-33**: We attach the test function to the task dictionary using `tasks['T1']['test'] = test_greeting`.
- **Line 38-43**: Define the actual implementation functions `greet` and `goodbye`.  `f"Hello, {name}!"` is an f-string – it inserts the value of `name` into the text.
- **Line 46-48**: We call each test and store the result.  `tasks['T1']['test']()` retrieves the test function from the dictionary and calls it with `()`.
- **Line 51-55**: We create a report.  We loop over the trace dictionary using a `for` loop.  `for req, task_ids in ...` gives us each key-value pair.  `results.get(tid, False)` gets the result from our results dictionary, or `False` if not found.  We format it with an f-string.


## Common misconceptions

**"The AI does all the work, I just review."** — The AI agent is so smart, I don't need to write a spec. I can just describe it and it'll work.

Without a spec, the agent will make its own assumptions about what 'good' looks like. It will pick the first solution that compiles, not the one that meets your needs. You'll get code that does something, but it might not be the right something. In fact: The quality of the AI's output is entirely dependent on the quality of your specification. The AI is brilliant at following a good spec and terrible at reading your mind. Writing a clear spec *is* the main job.

**"The pipeline is for big enterprise projects."** — This is overkill for my small script. It's just a few lines of code.

Spec drift happens just as quickly in small projects. A one-line change can silently break a feature. The pipeline is a habit, not a weight. Even a single function benefits from a test that says 'given this input, it should return that output.' In fact: The pipeline scales down. For a small task, the spec is one sentence, the task is 'write function', and the test is one assertion. The principle is the same — know what 'done' looks like before you start.

**"Once the code is written, the pipeline is done."** — The spec, tests and code are final as soon as it's working.

A working feature today is a specification for tomorrow's changes. The code will evolve, and the spec must evolve with it, or it becomes stale and misleading — worse than no spec at all. In fact: The pipeline is a living thing. When you change the code, you should update the test, and if the test changes, you must check it still matches the spec. That's the traceability link — it keeps the whole system honest.

**"Traceability is just a document."** — I have a matrix on paper, so I have traceability.

A document that isn't used is not traceability. Traceability is a living link that is checked at every review gate. If you don't actually *use* it to decide whether to proceed, it's just a formality, and you'll miss the drift. In fact: Traceability is a process. At each gate, you ask 'is this requirement still linked to a task that has a passing test?' If the answer is no, you stop and fix it. The matrix is the tool, not the goal.

**"Tests are for later."** — I'll write the code first, then maybe write some tests if I have time.

If you write the tests after the code, you're inevitably biased by what the code does. You'll write tests that pass, not tests that verify the spec. The whole point is to test against the *intended* behaviour, not the *actual* behaviour. In fact: Write the acceptance criteria *before* you write a line of code. The test is the definition of done. It's the first artifact in the pipeline, not the last.


## In short

The spec-to-code pipeline is a controlled path from a written specification to working code. Each stage — spec, tasks, tests, code — produces a reviewable artifact. An AI agent does the generation, but a human reviewer approves each transition. The key is traceability: every requirement is linked to a task, that task has a test, and that test passes against the code. This makes spec drift visible early and ensures you are always building what you intended. Remember: the AI is the worker, your specification is the work order, and the tests are the inspection — but your review is the only approval that matters.


## Where this is used

How is this used in the real world?

* **Modern AI coding assistants (like GitHub Copilot or Cursor):** When you give a natural language prompt, these tools often internally generate a plan (a mini-spec), break it into steps, and write code.  The pipeline concept is what separates a 'vibe-coder' (who lets the tool do its thing) from a 'spec-driven engineer' (who explicitly writes the spec first).  A professional developer using these tools will write a `requirements.md` file, then a `TASKS.md` file, and then ask the agent to 'implement task T1 only.'  They'll run the tests (`pytest`) before allowing the agent to move to T2.  If it goes wrong, they can see exactly which task's test failed.

* **Large Language Models (LLMs):** The training and evaluation of LLMs often uses a spec-to-code pipeline internally.  Data scientists write a detailed spec of an evaluation task (e.g., 'answer questions about a given document'), create test questions, and then run the model against them.  The pipeline ensures the model is tested against 'task cards' and 'acceptance criteria' — it's the same logic, just used for benchmarking, not production code.

* **Small Language Models (on your phone):** On a phone, every kilobyte of memory is precious.  A developer can't afford to run a huge model.  They use the pipeline to specify a *tiny* model's behaviour.  For example, a spec might say 'detect the wake word \'hey bro\''.  The task breakdown might be: 'create a 10,000-parameter model' and the acceptance test is 'recognises the wake word with 95% accuracy on a noisy bus'.  The developer writes the spec, generates the task, and the agent optimises the tiny model — and the reviewer checks it against the test.

* **Retrieval-Augmented Generation (RAG):** In RAG, you build a system that first *retrieves* relevant documents and then *generates* an answer.  A spec-to-code pipeline is crucial here.  The spec says 'only answer from the provided documents. If the docs don't contain the answer, say \'I don\'t know\''.  The task breakdown includes 'write a retriever', 'write a generator', and the tests verify that the system *doesn't* hallucinate.  A developer might write a test that feeds in a query and a document, and checks the output is a subset of the document's text.  Without this, the agent would happily invent facts.

* **Agentic systems (AI agents that plan and use tools):** This is where the pipeline shines.  Each step the agent takes is like a task card.  The system prompt acts as the spec.  A developer designs an agent to book a flight.  The spec defines the constraints: 'must check the user's budget, must confirm before booking'.  The agent then produces a *plan* (a list of tasks), calls a 'search_flights' tool, and the developer (as reviewer) can intervene if the agent's next step violates the spec.  The pipeline lets you control the agent's autonomy.

* **Everyday ML (recommendations, fraud detection):** In a bank, a fraud detection model isn't just 'trained'.  A data scientist writes a spec: 'The model must flag transactions above ₹50,000 that occur from a new device'.  The tasks include 'feature engineering', 'model training', and the test is 'does it flag a simulated fraud case?'.  The model is developed, tested, and deployed following the same gate-controlled path.


---

Read this on the web: https://basavapurushottam.com/learn-ai/spec-to-code-pipeline
