You are reusing a component that has run without incident for years in another product. What does your test strategy have to say about it?
A clean track record is evidence about the old system's operating envelope, not about yours, so the strategy must surface the assumptions the component inherits, test it against the new envelope rather than trusting its history, and state which inherited assumptions are going untested.
What the interviewer is scoring
- Does the candidate treat the component's history as evidence about its original context rather than about the new one
- Whether the new system's operating envelope is compared to the old one explicitly
- That reused code is scheduled for testing in the new context instead of counted as pre-verified
- Whether the strategy names which inherited assumptions are deliberately going untested and who accepted that
- Does the candidate separate a defect in the component from a defect in the decision to reuse it
Answer
A clean history is evidence about the old system
The argument for skipping the reused component is always the same, and it is not stupid: it has run in production for four years, it has never caused an incident, and there are twenty new components competing for the same testing effort. Spending time on proven code while unproven code goes untested looks like poor prioritisation.
The flaw is in what "proven" means. Four years without incident is strong evidence that the component behaves correctly for the inputs it received, under the loads it saw, in the environment it ran in, with the callers it had. It is no evidence at all about inputs outside that range. And the entire reason you are reusing it in a new system is that the new system is different — different scale, different data, different callers, different reason for existing — which is precisely the set of ways its history stops applying.
So the strategy's job is not to re-verify the component's logic. Its job is to identify where the new context leaves the range the history covers, and to test that boundary.
Ariane 5 Flight 501 and the cost of an inherited envelope
The 1996 loss of Ariane 5 Flight 501 is the case to reach for, because it is the clean version of this failure and not a story about sloppy engineering. Inertial reference software carried over from Ariane 4 converted a 64-bit floating-point value into a 16-bit integer, the value overflowed on a trajectory the earlier vehicle had never flown, and the resulting diagnostic output was read by the control system as flight data.
What makes it the right anchor for a test-strategy answer is that the code was not defective by the standards of the system it was written for. On Ariane 4 the value could not reach the range that overflowed, so the conversion was safe, and no amount of testing against Ariane 4's flight profiles would have revealed anything. The gap was one of scope rather than of coverage. The component's assumption about its operating envelope travelled with it into a vehicle whose envelope was different, and nothing in the reuse decision required anybody to write that assumption down and check it.
Every reuse decision has this shape, usually with lower stakes. A rate limiter sized for a hundred requests per second, a parser assuming a file will fit in memory because the old system's files always did, an identifier field wide enough for the previous volume of records, a currency routine that only ever saw two decimal places, a timeout tuned to a network that was always in one region.
Surface the assumptions before choosing tests
The first piece of work is therefore an inventory of what the component assumes, because you cannot test an assumption you have not articulated. It comes from several places, none of which is a specification.
Read the code for its implicit bounds: fixed-width types and casts, buffer and array sizes, hard-coded limits, timeouts, precision of numeric types, character-set handling, anything that would behave differently at a thousand times the volume. Read its existing tests, which are a record of what its authors believed the input range was, and note which dimensions they never varied. Read its configuration defaults, since a default is a statement about the expected environment. And where they are reachable, ask the people who ran it what its worst day looked like, because the operational limits are usually known informally and written down nowhere.
Then describe the new envelope on the same axes: the volume and rate of data, the ranges and precision of values, the concurrency, the character sets and locales, the error rates of its dependencies, the platform, and who calls it and how often. The comparison is mechanical once both sides exist, and every axis where the new envelope exceeds or simply differs from the old one is a candidate test with a real justification attached.
What the strategy commits to
For the axes where the new envelope is genuinely wider, the tests are ordinary and specific: boundary values at the new limits and just beyond, volume at the new scale, the value ranges the old system could not produce. This is where the reuse decision earns its testing budget, and the case for the effort is now concrete rather than a general appeal to prudence.
Two further commitments matter as much. The first is behaviour at the boundary rather than merely within it, because the Ariane case turns on what happened when the assumption was violated: the failure was silent in the sense that a value was produced rather than an error raised, and the caller had no way to distinguish diagnostic output from data. So test what the component does when it is pushed outside its range, and if the answer is that it returns something plausible rather than failing loudly, that is a finding to report on its own, independent of any specific input.
The second is the seam. A reused component in a new system has new callers making new assumptions about it, and defects at that boundary are invisible to tests on either side: each half is correct on its own terms and the pair disagrees about units, nullability, ordering, error semantics or who validates what. Contract tests at the interface catch the whole class rather than one instance.
When nothing is written down
The common real situation is a component with no documentation, no tests and no surviving authors. The route in is characterisation tests: exercise the component with a broad range of inputs and record what it currently does, without judging whether that is correct. The result is not a correctness suite, because you have codified the behaviour including any bugs, and it must be labelled as such so nobody later mistakes it for a specification. What it gives you is a description of the current behaviour, which is the thing you need in order to notice when the new context changes it, and a safety net for the modifications the integration will require.
Where the component processes data, replaying real historical inputs through it is worth more than any hand-written case, because production data contains the malformed, legacy and edge-case records that nobody would think to invent.
The paragraph most strategies omit
Finish by stating what is not being tested and why. A strategy that lists coverage implies uniform confidence, and the inherited assumptions you decided not to verify are exactly the risk that later reads as an oversight. Write down each assumption you identified, whether the new system stays inside it, and where you chose to accept it untested — because the cost of testing was disproportionate, because the condition cannot be reproduced pre-release, or because a detection mechanism covers it instead.
That paragraph converts an inherited assumption into an accepted risk with a name attached, which is the only form in which a reuse decision can be reviewed later. It also makes the distinction an interviewer is listening for: whether a subsequent failure was a defect in the component, or a defect in the decision to reuse it without checking what it assumed.
Likely follow-ups
- The component has no tests of its own and everyone who wrote it has left. Where do you start?
- How do you test for a failure mode that is silent rather than a crash?
- The component is a third-party library rather than internal code. What changes about your approach?
- What would you write down so the next team to reuse it does not repeat this?
Related questions
- How do you turn discovery into a proposed architecture and a written proposal that survives procurement?hardAlso on assumptions7 min
- Where do you draw the line between a unit test and an integration test, and how do you keep a few thousand of them under ten minutes?hardAlso on test-strategy5 min
- What is the test pyramid, and is it still the right model for your system?mediumAlso on test-strategy4 min
- In the room, the customer tells you a competitor has committed to something you cannot match. How do you respond?hardAlso on assumptions5 min
- How do you decide what to automate and what to leave manual?mediumAlso on test-strategy6 min
- One managed service in your primary region is having a bad day and your product is down with it. What can you do during the outage, and what should you have designed before it?hardSame kind of round: design5 min
- You are moving a bank's existing accounts onto a new core platform. How do you cut over so there is never a moment when a customer's true balance is unknown?hardSame kind of round: design6 min
- No environment in the estate contains the whole stack, so nobody can test an order end to end before release. How do you get assurance anyway?hardSame kind of round: design6 min