Build a Traceability Matrix — Requirements Engineering, Part 4
- Shawn West
- Jul 28
- 6 min read
Updated: Aug 17
Requirements Engineering · Part 4
Six weeks into the refund-queue build, someone asks: "If we change the split-tender ordering rule, what do we have to retest?" The room goes quiet and an engineer starts grepping for "gift card." That silence is what a traceability matrix exists to remove.
This tutorial builds one in about 90 minutes. For why traceability is an ability rather than a document, read the practical guide alongside it.
Illustrative composite continuing the support-tooling example from Parts 1–3. Figures are illustrative, not measured.
Before You Start
Four inputs, all of which exist if you have worked Parts 1–3:
A numbered requirements source — for us, the Process a Refund Request use case from Part 2 including alternate flow A3 (split-tender), plus the stories from Part 3.
Read access to the repository, enough to name files.
The test suite's layout, or someone who can answer questions about it in one sitting.
A spreadsheet or a markdown file in the repo. The tool is the last decision, not the first.
What You'll Build
One table for a single release: every requirement with a stable ID, its source, an honest status, the code implementing it and the tests verifying it — plus three coverage queries and a threshold.
Step 1: Fix the Scope (10 min)
Write the scope line first: "Refund queue, release 1. Requirements from UC-01 and its alternate flows." Everything outside it stays out today.
Working heuristic, not a measured figure: roughly 20–50 requirements is what a team maintains by hand without strain, and by the low hundreds it reliably fails. Calibrate that against your own team rather than taking it as measured. The mechanism matters more than the range: a manual matrix decays at the rate people forget, and that rate does not improve with size.
Check: your scope line names a release and a source document, and you can point to one requirement you deliberately excluded today.
Step 2: Extract Requirements and Give Them IDs (15 min)
Pull every statement of system behaviour from the use case and stories. "Agent sees only refunds assigned to their team" is a requirement; "add a team filter to the queue endpoint" is a task.
Assign IDs sequentially and never reuse one. The ID is the asset — the handle Part 9 uses when a change request lands. If REQ-004 is deleted, REQ-004 stays deleted.
Check: no two rows share an ID, and every row is a behaviour someone could disagree with. Read three at random; any describing how rather than what belong in the backlog.
Step 3: Build the Matrix (15 min)
Six columns: the left pair points backward at where the requirement came from, the right pair forward at what satisfies it.
ID | Requirement | Source | Status | Code | Tests |
REQ-001 | Agent sees only refunds assigned to their team | UC-01 main flow, step 2 | Verified | queue/scope.ts | queue-scope.spec.ts |
REQ-004 | Split-tender refunds exhaust gift-card balance before the card | UC-01 alt flow A3 | In Progress | tender/split.ts | — |
REQ-005 | Refunds over $500 need supervisor approval | Part 1 interview | Done | approval/threshold.ts | approval.spec.ts |
REQ-006 | At-risk refunds sort first on queue load | US-12 (Part 3) | Done | queue/sort.ts | queue-sort.spec.ts |
REQ-007 | At-risk rows show hours remaining | US-12 (Part 3) | In Progress | queue/RiskBadge.tsx | — |
REQ-008 | Refund actions write an audit entry | Part 1 interview | Not Started | — | — |
Status takes five values: Not Started, In Progress, Done, Verified, Deprecated. "Done" means the code exists; "Verified" means a passing test asserts it. Collapsing those two is how a matrix starts lying.
Check: open the file on a phone. Every column reads and no cell needs a paragraph — a requirement that won't fit in one is two requirements.
Step 4: Fill the Source Column First (15 min)
Do the left side first: Source is what answers "what breaks if this changes?" — and filling it is a discovery instrument.
REQ-005's threshold traced back to one sentence in the Part 1 interview and nowhere else: no use-case step, no story, no policy document. The $500 figure was one person's recollection of a rule, and writing "Part 1 interview" in that cell is what made its thinness visible. It had an owner in finance by the end of the day.
Check: no Source cell is blank, and none says "the team" or "requirements doc." Each names an artifact and a location inside it. A requirement sourced to a memory is a finding.
Step 5: Link Forward to Code and Tests (20 min)
Two rules. Point at the narrowest true thing — tender/split.ts is useful, src/ is not; if you cannot name a file, the requirement is not implemented, whatever the board says. Name the test, not the suite — queue-sort.spec.ts is checkable, "covered by regression" is not.
Blanks are the point of this step. REQ-004's empty Tests cell is the reason this tutorial opened with a silent room; do not fill a cell to make the table look finished.
Check: every non-blank Code and Tests cell resolves to a path that exists right now.
Step 6: Reconcile Status Against the Evidence (5 min)
Read each row and ask whether its status matches the two cells beside it. Verified with an empty Tests cell is a belief that a behaviour is proven when nothing proves it. Correct the status, never the evidence.
Check: no row is Done or Verified with an empty Code cell, and none is Verified with an empty Tests cell. Count the rows you downgraded — that is the gap between what the team thought was finished and what is.
Step 7: Run the Three Coverage Queries (10 min)
This is the validation step. Filter the table three times:
Query | What it means | What to do |
Requirement with no linked test | Behaviour nobody checks — a test gap | Write the test, or downgrade the status and plan it |
Requirement with no linked code | Not built, whatever the board shows | Confirm it is outstanding, or link the code |
Test with no linked requirement | Unapproved scope, or a test outliving its behaviour | Trace it to a requirement, or delete it with a note |
The threshold. Coverage is not a percentage to improve; it is three conditions that are met or not.
Every requirement at Done or Verified has a linked test and a code location. Target zero violations, not a low number. Rows at Not Started or In Progress may have blanks — that is what status is for.
Every requirement has a non-blank Source. No exceptions; an origin-less requirement cannot be impact-assessed later.
Orphaned tests are allowed, each carrying a one-line label ("exploratory regression, no requirement"). Unexplained orphans should be zero.
Ours failed condition one on REQ-004 and REQ-007 — legal, since both are In Progress, but both were assumed shippable in the release plan. That moved two test-writing tasks into the current sprint rather than the retrospective. Requirements you cannot verify are also the ones you cannot safely rank, which is what Part 5 needs next.
Check: you can state, without opening the repo, how many rows violate each condition. If any number is "I'd have to look," the query was not run.
Step 8: Make It Survive the Sprint (5 min)
A matrix decays the moment updating it depends on remembering to. Add a line to the pull-request template — "Requirement(s) touched, and matrix updated? REQ-___ / N/A" — and keep the file in the repo beside the code, so it moves through the same review. That is what turns a mid-sprint requirements change into an impact list rather than a negotiation.
Check: your next open pull request names a REQ ID or says N/A, and the matrix sits in the same repository tree as the code.
You're Done When
Every requirement has an ID and a non-blank Source — pick any row and name the artifact it came from without asking anyone.
No row is Done or Verified with an empty Code or Tests cell, and you know how many you downgraded to get there.
You can answer "what breaks if REQ-004 changes?" in under two minutes from the matrix alone, naming files, tests and a source document rather than a person.
If This Goes Wrong
Three hours in and it isn't finished. Scope too wide, or decomposed too far. Cut to one release and merge anything untestable on its own into its parent.
Half the Code cells say "not sure." Either it was never built or nobody knows where it lives — both are findings worth more than the table. Write unknown and log an open question.
Nobody updates it after week two. Move it into the repo and add the PR prompt. If it still decays, ask what decision it has served since — a matrix nobody queries is maintenance theatre.
Statuses drift back into optimism. Tie status to evidence and re-run Step 6 monthly. If the same rows keep failing, the problem is your definition of finished — see Definition of Done vs acceptance criteria.
Common Failure Modes
Building it right-to-left. Code and test links are the easy half and the less valuable one. Without Source, the matrix tells you what exists, not what breaks.
Filling blanks to look complete. Every invented link is a gap you have hidden from yourself, and it surfaces in an incident rather than a review.
Over-decomposition. Four hundred micro-requirements is not traceability; it is a spreadsheet nobody reads.
Sources
ISO/IEC/IEEE 29148:2018, Systems and software engineering — Life cycle processes — Requirements engineering — lists traceable among the characteristics of an individual requirement: upward to a stakeholder need or higher-tier requirement, downward to lower-tier requirements. iso.org/standard/72089.html
Continue the Requirements Engineering path
Part of the Requirements Engineering learning path.


