Chapter 06 · Reps

The Technical Specification — Reps

← Back to Chapter 6

Chapter 6 — Reps

These reps are not warmups around this week’s milestone. Each one produces a section of the document you are turning in. Do all twelve in order and docs/architecture.md writes itself.

Ground rules

  • Work on YOUR project. PantryPilot and TraceLens are examples in the chapter. Every rep here operates on the system you are actually going to build and demo in Week 16.
  • Every rep lands in the repository. Prose goes into docs/architecture.md; diagram sources and rendered images go into docs/diagrams/. Nothing counts until it is committed.
  • Decisions, not descriptions. After each rep, ask the §6.9 question: how many decisions did I just make? If the answer is zero, you wrote a paragraph, not a specification.
  • Verify everything an assistant tells you. Versions, endpoints, header names, config keys, limits, prices — checked against the vendor’s own documentation, with the date recorded. Log substantive AI use in docs/ai-usage.md.
  • Log your hours in docs/hours-log.csv as you go, honestly, including the rep that took three times as long as you expected. That rep is next week’s most useful estimating data.
  • Write the reflection. A rep is not finished until you have written the two to four sentences it asks for. Keep them in docs/architecture.md where they belong, or in your working notes — but write them.

Templates and the worked example live in this chapter’s code/ directory: code/tech-spec-template.md, code/pantrypilot-spec-excerpt.md, code/sample-requirements.md, and the checker code/spec-check.py. More good-versus-bad pairs are in Appendix B.


Boundaries and structure

Rep 1 — The decision inventory

Open docs/requirements.md. Read every Must-priority functional requirement and every non-functional requirement, and write down — one line each — every decision that must be made before that requirement can be built. Do not solve them yet. Just find them.

| # | Requirement | Decision that must be made first        | Section it belongs in |
|---|-------------|------------------------------------------|-----------------------|
| 1 | FR-03       | Where does the barcode lookup happen?    | Components            |
| 2 | FR-03       | What if the lookup fails?                | Errors                |
| 3 | FR-07       | How many days is "about to expire"?      | Data / behavior       |

Aim for at least twenty rows. If you find fewer than fifteen, you are not looking hard enough — go back to the non-functional requirements, which hide the expensive ones.

Reflect: Which requirement generated the most decisions? That requirement is where your design risk lives, and it is almost certainly the one you should build first in Week 9.


Rep 2 — Context, then containers

Draw both, in a tool whose source you can commit (see Appendix A).

  1. Level 1 — Context. One box for your system. Every human role and every external system around it. No internals.
  2. Level 2 — Containers. Every separately runnable thing, each labeled with a technology and a one-line responsibility. Every arrow labeled with payload and protocol. Draw the trust boundary.

Then run this checklist against your own drawing and fix what fails:

[ ] Every box has a responsibility AND a technology
[ ] Every arrow has a direction, a payload, and a protocol
[ ] The trust boundary is drawn; no secret crosses it
[ ] There is a legend, a version number, and a date
[ ] Every box is something I will actually build, run, and demo

Reflect: How many containers did you draw, and how many of them did the requirements demand versus how many you added because they felt professional? Delete the ones that fail that test and say what you deleted.


Rep 3 — The level-3 zoom, exactly once

Pick the one container where the hard part of your project lives. Open it and draw the components inside: three to eight modules, each with a name and a job. Do this for one container only.

Reflect: Why did you pick that container? Name the specific thing a reviewer would otherwise have had to guess at. If you cannot name it, you picked the wrong container — or you did not need a level 3 at all, which is also a legitimate answer to write down.


Rep 4 — The responsibility table, and the audit that follows it

Build the component responsibility table from §6.3 for every component in your system.

| Component | Responsibility (one sentence, starts with a verb) | Owns | Depends on | Serves |
|-----------|--------------------------------------------------|------|------------|--------|

Then audit it, and record the result:

  • Verb test — read every responsibility out loud. Any that sound like a job title (“manages the data layer”) get rewritten as verbs.
  • Single-owner test — for every table, file, cache, and piece of in-memory state, exactly one component owns it. List any state with two owners and fix it.
  • Cycle test — draw the dependency graph from the “Depends on” column and walk it. Any cycle gets broken, and you write down how.
  • Traceability test — every component serves at least one requirement, and every Must requirement is served by at least one component. Both directions.

Reflect: Which test failed first? Almost everyone fails the single-owner test on their first draft. What state did you find with two owners, and what would that have cost you in Week 12?


Contracts and data

Rep 5 — One complete interface contract, for the one you understand least

Choose the interface you are least confident about. Not the easy one. Write it out completely with all eight facts from §6.4: name and purpose (plus the requirement it serves), authorization, inputs with types and validation, success output with an example, every error, idempotency, side effects, and limits.

If your project has no HTTP surface, do this for a CLI command (flags, exit codes, stdout/stderr contract), a queue message, or a library entry point. The eight facts do not change.

Reflect: What did you have to decide while writing this that you had been quietly leaving open? Name it. That decision is the value of the rep.


Rep 6 — One error envelope, one status-code policy

Decide, once, for the whole system, and write it into the specification:

Error envelope:  { "error": { "code": ..., "field": ..., "message": ... } }
Codes I will use and what each means in MY system:
  400 ____________________   401 ____________________
  403 ____________________   404 ____________________
  409 ____________________   429 ____________________
  500 ____________________   (CLI: exit 0 / 1 / 2 ____________________)
What a user is shown for each, and what gets logged: ____________________

Reflect: Where were you about to use two different error shapes in the same system, and why did that feel reasonable at the time?


Rep 7 — The data model, with invariants

For every entity: purpose, primary key, every attribute with a type and a nullability rule whose meaning you can state in words, invariants, relationships with cardinality, and a rough volume estimate. Then apply the six decisions from §6.5 — surrogate keys, UTC timestamps with one conversion point, integer minor units for money, constrained enumerations, a per-entity deletion policy, and one meaning per nullable column.

Reflect: Which column did you almost make free text that should be constrained? Which nullable column’s meaning did you struggle to state in words? That struggle means two concepts are sharing one column — say what they are.


Rep 8 — Three sequence flows, and the branch that matters

Sequence the money path, the risky path, and the failure path (the risky path with its boundary broken). For each, number the steps, name the participant at each step, and say what data moves. Then build the failure table:

| Step | What can go wrong | System behavior | User sees |
|------|-------------------|-----------------|-----------|

Reflect: What did the failure branch change about your interface contract from Rep 5 or your data model from Rep 7? If the answer is “nothing,” you drew the flow after deciding instead of to decide — do it again, honestly.


Rep 9 — The migration decision

Answer three questions in docs/architecture.md, in writing:

1. Migration mechanism: ____________________  (tool, or numbered SQL files
   applied in order and tracked in a schema_migrations table)
2. Forward-only or reversible: ____________________  (forward-only is fine —
   what is not fine is not knowing)
3. Path and runner: migrations/____________ , applied by ______________
   (the same script the Week 14 clean-machine test will run)

Then write migration 0001 for the schema from Rep 7 and commit it.

Reflect: What is your plan for the first schema change after you have data you care about? Write the two sentences now, while it is hypothetical and therefore easy to be honest about.


Failures, dependencies, and judgment

Rep 10 — Error policy and the edge-case register

Two artifacts. First, the failure-category policy table from §6.7, filled for your system. Second, the edge-case register — at least twelve entries, each concrete:

| # | Edge case                          | Expected behavior | Becomes test |
|---|------------------------------------|-------------------|--------------|
| 1 | Empty state: zero items, first run |                   | Week 11      |
| 2 | Exactly one of something           |                   | Week 11      |
| 3 | Ten thousand of something          |                   | Week 11      |

Then, for every call that leaves your process, write the timeout in seconds, the retry policy with its cap, and whether the user finds out.

Reflect: Which external call did you discover had no timeout at all in your plan? Look up what your client library’s default actually is and write the number down — some defaults are “forever.”


Rep 11 — Rewrite the vague specification

Here is a real-shaped bad specification. Rewrite it into a buildable one using Version B from §6.9 as your model.

FR-12 — Search

The system shall let users search their data. Search should be fast and
return relevant results. It will use the database's search feature.
Results should be paginated. Handle errors appropriately.

Produce: a definition of what is searched and what is matched; the interface contract; the ranking or ordering rule; pagination parameters and their defaults and maxima; the behavior on zero results, on one result, and on ten thousand; the behavior on a query of one character, of 500 characters, and of emoji; what “fast” means as a number tied to a non-functional requirement; the error cases; and at least three acceptance criteria. Mark anything you genuinely cannot decide yet as an OPEN QUESTION with a blocker, an owner, and a date.

Reflect: Count the decisions you added. Then find the equivalent section in your own draft specification — the one where you wrote “handle errors appropriately” in your own words — and fix it.


Rep 12 — Specify the dependency that can betray you

If your project calls a model, write the full prompt contract from §6.8: purpose, inputs, privacy rule, prompt template path in the repo, model identifier with the date you verified it, parameters, output schema and validator, behavior on invalid output, token and latency and cost budget with a hard cap, the non-AI fallback, and the logging and retention rule.

If your project has no AI component, do exactly the same for your riskiest third-party dependency: what you call, what it costs, what its limits are, what you do when it is down, and what your system degrades to.

Reflect: Describe your fallback in one sentence, then answer honestly: could you demo your project in Week 16 with that dependency switched off entirely? If not, the fallback is not real yet.


Done? One Last Thing.

Assemble it, then hand it to a stranger.

  1. Assemble. Copy code/tech-spec-template.md into docs/architecture.md and fill it with Reps 1–12. Version it v0.1, date it, and list the open questions in one place at the end.

  2. Run the checker. Copy code/spec-check.py into your repository as tools/spec-check.py, then from the repository root:

    python3 tools/spec-check.py docs/architecture.md --requirements docs/requirements.md

    It reports missing sections, vague words, unresolved markers, and any requirement identifier that appears in your requirements but never in your specification. It exits 0 when clean and 1 when it finds something, so it will drop straight into the continuous integration you build in Week 9. Run it first against the supplied pair — code/pantrypilot-spec-excerpt.md and code/sample-requirements.md — so you know what its output looks like before you point it at your own draft.

  3. Red-team it. Give an assistant your container diagram, responsibility table, and interface list, and ask: “You are the senior engineer who will inherit this project. Name the five places this design will break first, and for each, the requirement it violates and the cheapest change that would prevent it. Do not compliment the design.” Write down which of the five were fair. Verify any specific fact it produced. Log the session in docs/ai-usage.md.

  4. Run the stranger test for real. Hand the document to a classmate who is not on your project. Point at one Must requirement and ask them to describe how they would build it. Every question they have to ask you is a hole. Write the questions down; they are your Week 8 review agenda.

  5. Commit with a message that names the artifact: docs: technical specification v0.1 (Milestone 6).

If a stranger can read that document and start building without calling you, you are ready to be reviewed.

Up next: Milestone 6