A suite that doubles at vendor seams everywhere has thin integration coverage. How do you shift the substitution boundary without pausing delivery?
answer
- Accumulated default, not one bad decision
- Inventory the seams, rank by incidents
- Narrow policy a reviewer can apply
- Buy the evidence before spending it
- Measure escaped defects, not doubles removed
basics
~20 sInventory the seams, rank them by where defects actually escaped rather than by count, write a narrow enforceable policy, add adapter tests against the real dependencies before converting anything, then migrate one dependency per release cycle, new code complying immediately.
solid answer
~50 sTreat it as an accumulated default rather than one bad decision, which rules out both instinctive answers: a big-bang conversion removes the safety net while it runs, and an unenforced policy changes nothing. Start with a cheap inventory, since exposure is usually concentrated in a handful of dependencies. Rank by the incident record, not by how many files mention a vendor, and accept that a stable dependency with no history of defects can be left alone. State the policy as rules a reviewer can apply: substitute at owned interfaces, the process edge, or nondeterministic sources; one owned adapter per dependency, with a named owner and a small set of tests against a real or sandbox instance. Buy that evidence before spending it, then convert one dependency per delivery cycle. Measure escaped defects by seam and adapters lacking a real-dependency test, not stand-ins removed.
go deeper
You will not lead this, but know the shape: teams sometimes replace third-party types everywhere, which leaves nothing exercising the real dependency. Recognise the smell so you can raise it rather than copy the surrounding tests.
Be ready to describe the conversion of a single dependency end to end: introduce the owned interface, move callers, rewrite the tests that touched the vendor type, and add a handful of adapter tests against a real instance.
Show the sequencing judgement: evidence before conversion, one dependency at a time, new code compliant immediately and old tests converted when touched. Be specific about which tests hit real dependencies and what they cost per commit.
Own the portfolio view and the honest tradeoffs: which dependencies are worth converting, who owns each adapter, how the slow tests are budgeted, why a uniform mandate backfires, and which metric you would defend to leadership over a test-count target.
### Read the problem correctly first The presenting complaint — doubles at vendor seams, thin coverage where components meet — is not one bad decision but an accumulated default. That matters, because it rules out the two instinctive answers. A **big-bang conversion** removes the team's only safety net for the duration of the rewrite and stops feature work. A **written policy with no enforcement or owner** changes nothing: the next test will be written the way the surrounding tests were. The lead's job is to convert a habit into a policy, buy evidence before spending it, and let the change ride existing delivery rather than compete with it. ### 1. Inventory and classify, cheaply Count what exists before deciding anything: how many test files construct a stand-in for a vendor type, which vendor types, and how concentrated they are. A representative shape from this kind of audit: about 1,140 test files, 37 distinct vendor types substituted, and 6 of those accounting for roughly 71% of the occurrences. That distribution is the plan — most of the exposure lives behind a handful of dependencies, and you never have to touch the long tail. ### 2. Rank by risk, not by count Order the work by **where escaped defects have actually come from**, using the incident record rather than intuition. In the **tax-filing wizard**, the class that hurt was a **stale-cache read** in the rules dependency: the vendor client served a snapshot from a previous tax year, no substituted test could have modelled it, and the wrong figures rode a **3-week release train** before anyone noticed. That single class of defect justifies converting the rules dependency first regardless of how many test files mention some other vendor. A dependency that is stable, rarely upgraded and has never produced an incident is a legitimate "leave it alone" — the policy exists to buy risk reduction, not tidiness. ### 3. State the policy narrowly Written as rules a reviewer can apply without a meeting: - Substitution happens at interfaces the team owns, or at the process edge, or for nondeterministic sources. Value objects and in-process logic the team owns stay real. - Each external dependency has exactly one adapter, with a named owner, and that adapter has a small set of tests exercised against a real or sandbox instance. - New code follows the policy from the day it lands; a reviewer may decline a stand-in for a vendor type in new code. Narrow beats comprehensive. A policy that mandates a wrapper around every third-party call produces pass-through interfaces that repeat the vendor's shape and buy no evidence at all. ### 4. Buy the evidence before you spend it The counter-intuitive sequencing point, and the one that separates a lead's answer from an engineer's: **add the adapter-level tests against the real dependency first, while the old doubles are still in place.** Converting a suite means rewriting the very tests you are relying on to notice mistakes; doing it with nothing verifying the adapters is a rewrite without a net. A handful of tests per adapter — often under ten, running in tens of seconds — is enough to make the conversion safe. ### 5. Migrate on the delivery cadence Take one dependency per release train. Within a train: introduce the owned interface, move callers, convert the tests that touch it, delete the vendor-shaped scaffolding. Old tests elsewhere get converted when the surrounding code is touched, not on a sweep. Publish a short scoreboard — dependencies converted, adapters lacking a real-dependency test — so the work stays visible without becoming a separate project that competes for capacity. ### 6. Measure the thing you actually wanted - **Escaped defects classified by seam**: how many lived in a collaboration the suite never exercised. This is the metric the whole effort exists to move. - **Adapters with no test against the real dependency**: the gap list, which should trend to zero. - **Suite run time and per-commit cost**: the budget the slow tests spend. Guard against the vanity version. "Stand-ins removed" and raw executed-line percentages both improve under changes that make the suite worse, and a fixed unit-to-integration ratio is a slogan rather than a finding — the published evidence for any specific ratio is thin, and it is more honest in an interview to say so than to quote a number. ### Tradeoffs to own out loud Every owned interface is code to maintain and can rot into a pass-through. Tests against real dependencies are slower, occasionally unavailable, and consume sandbox quota, so they are rationed and belong to whoever owns the adapter. Some conversions will not pay for themselves and should be declined. And the policy only holds if review enforces it — which makes the durable deliverable a reviewer's checklist and a named owner per adapter, not the document.
- One vendor dependency is stable, rarely upgraded and has never produced an incident. Does the policy still apply to it?Not urgently. The policy exists to reduce risk, not to make the codebase uniform, and converting a dependency that has never hurt you spends capacity for no measurable return. Leave it, record the decision so the next reviewer does not relitigate it, and revisit if the vendor starts shipping breaking changes or the dependency moves onto a critical path. A policy that admits exceptions with reasons survives; one that demands uniformity gets ignored wholesale.
- How do you keep the tests that hit real dependencies affordable once every adapter has some?Keep them few and contract-shaped rather than scenario-shaped: they exist to pin the dependency's semantics, not to cover your domain logic, which the fast tests already do. Give each adapter an owner responsible for its budget, run them on the commit path only where quota and latency allow, and move the rest to a scheduled run with a clear alerting owner. If they become a flaky tax nobody trusts, they will be deleted and you will be back where you started.
- Your director asks for a target ratio of fast tests to integration tests. What do you say?That any specific ratio is a slogan, not a finding, and the published evidence for a universal number is thin. Offer a measurable target instead: escaped defects that lived in a collaboration no test exercised, and the count of adapters with no test against the real dependency. Those move when the work actually helps, whereas a ratio can be hit by writing tests that prove nothing.
saying these in an interview costs you the question
- Proposes converting the whole suite in one release
- Writes a policy with no owner and no review enforcement
- Counts stand-ins removed as the measure of success
- Mandates an owned wrapper around every third-party call
- Quotes a fixed test-ratio target as if it were established evidence
- Sequences the conversion before any real-dependency tests exist