Programmes rarely fail certification on the engineering. They run out of schedule assembling the argument that the engineering was done, which is a different problem with a different fix.
The pack is collected, not written
Every artefact in a certification pack already exists somewhere before the pack is assembled. The requirements are in a tool. The design is in documents and models. The code is in version control. The test results are in whatever the test rig produced.
What is missing at the end is not content. It is the connective tissue: which requirement this test covers, which design element this requirement drove, which version of the code this result was produced against. Those links were knowable at the moment each artefact was created and expensive to reconstruct three years later, which is exactly when teams sit down to reconstruct them.
Treat that as a data capture problem during development rather than a documentation task at the end and most of the pain goes away. Treat it as documentation and the last six months of the programme belong to it.
The links that have to hold, in both directions
Reviewers work in two directions and both have to be answerable.
Forwards, from a requirement: what design implements this, what code realises that design, what test demonstrates the code. A requirement with no test underneath it is an open item, and a requirement with a test that does not actually exercise the stated behaviour is worse, because it looks closed.
Backwards, from the code: which requirement asked for this. Code with no requirement above it is either dead, or it is behaviour that was added without being specified. On a real programme it is usually the second, and it is usually defensive handling that somebody added after a bad day on the rig.
That reverse trace is the one teams find late and the one that generates the most work, because closing it means writing requirements retrospectively for behaviour that is already flying.
Derived requirements are where it breaks
Not every requirement traces to a customer statement. Choosing a filter order, setting a watchdog period, picking a message rate: these come from the design, and they are real requirements with real safety consequences.
Standards require that derived requirements be fed back to the safety assessment, because the system safety analysis was done against the original set and cannot account for a constraint the designers invented afterwards. In practice this is the trace that is least often maintained. The decision gets made in a design review, recorded in minutes, implemented, and never enters the requirements set at all.
A useful habit: when a number appears in the code that was not in the requirements, it is a derived requirement, and it needs a home before the commit lands.
Tool qualification is a scoping decision
Any tool whose output you rely on without independently checking it may need qualifying, and qualification is expensive. The decision that controls the cost is not which tool to use. It is what you claim the tool does for you.
A code generator whose output is reviewed line by line and tested against the requirements needs a far weaker argument than one whose output is trusted. A static analyser used to find problems needs almost nothing; the same analyser used to eliminate a verification activity needs a full case.
Decide that early and write it down, because the scope of every tool claim propagates into the plans, and revising it after the plans are agreed is the kind of change that costs a review cycle.
Structural coverage measures the tests, not the code
Coverage analysis is widely misread as a code quality metric. It is not. It is a check on whether the requirements-based tests were thorough, and the interesting output is the gap, not the percentage.
Code reached by no test means one of three things, and each has a different response. Either a requirement is missing a test, or the code implements behaviour no requirement asked for, or the code is unreachable and should be removed. The number alone distinguishes none of these, which is why a coverage target as an objective produces tests written to hit lines rather than tests written to demonstrate behaviour.
What we would build on day one
Three things, none of them large.
A stable identifier on every requirement that survives edits, so a trace does not break when somebody rewords a sentence. Identifiers that encode the section number are identifiers that change during reorganisation, and every link to them dies quietly.
A machine-readable link from each test to the requirements it covers, held with the test rather than in a separate matrix. A matrix maintained by hand goes stale between the moment it is written and the next commit.
A build that emits the trace as an artefact on every run, and fails, or at least reports loudly, when a requirement loses its coverage. That turns an end-of-programme reconstruction into a report that has been correct continuously since the first week.
None of this is specific to any one standard. It is what makes the argument assemblable at all.