Your suite boots a new application graph for nearly every test because each declares different overrides. How do you cut that cost?
answer
- the cache is keyed on the wiring
- unique set per test means boot per test
- vary behaviour, not the binding
- shared graph means reset state
- dirty-marking forces a rebuild
basics
~20 sHarnesses cache a booted graph keyed by its configuration, and each distinct override set is a distinct key, so a unique set per test forces a boot per test. Collapse onto a few shared wirings instead.
solid answer
~50 sThe cache key is the wiring, not the test. A harness reuses a booted graph only when the next test asks for the same configuration, including its replacements, so a suite where every test declares its own set produces a cache miss every time and pays full startup on each. The fix is to converge: define a small number of named wirings that whole groups of tests share, and express per-test variation by reprogramming the stand-in that is already bound rather than by binding a different one. Reset that stand-in's state between tests so sharing the graph does not mean sharing leftovers. Keep genuine one-offs — a wiring only two tests need — deliberate and few, and watch what a cache of many live graphs costs in pools, ports and background threads. Marking a graph dirty forces a rebuild, so use it only when a test really did corrupt shared state.
go deeper
Know that starting the application is the expensive part and that tests reuse a started one when they ask for the same setup. Changing what you replace changes the setup.
Explain the cache key: settings, mode and the replacement set together. Show why expressing per-test variation as different behaviour on one bound stand-in keeps tests on a single cached graph.
Measure boots per run and attribute them before restructuring. Talk about grouping tests by wiring, resetting shared stand-ins, using dirty-marking only after real corruption, and the resource cost of holding many graphs open.
Treat the number of distinct wirings as a budget the team spends deliberately. Weigh suite duration against the fidelity lost when everything converges on one convenient graph.
## Why the boots happen at all Starting an application graph is not cheap: registrations are scanned, implementations constructed, pools opened, background components started, routes registered. Test harnesses therefore **cache booted graphs and reuse them**, keyed by the configuration that produced them — the settings, the active mode, and the set of replacements. That key is the whole story. Two tests share a graph when their wiring is identical. A test that declares a replacement no other test declares has a unique key, so the cache cannot serve it and the harness boots again. A suite where each test tweaks one more binding is, in effect, a suite of single-test applications. ## The shape of the fix 1. **Inventory the override sets.** Group the tests by the exact set of replacements they ask for. The usual finding is a long tail of sets that differ by one entry, which is where nearly all the boots are. 2. **Converge on a few named wirings.** Pick a handful — often two or three — that cover most tests, and move the tail onto them, even where a test then boots with a stand-in it does not care about. 3. **Move variation out of the wiring.** Per-test differences almost always concern *behaviour*, not *identity*: this test wants the collaborator to fail, that one wants it slow, the next wants an empty result. Bind the stand-in once in the shared wiring and reprogram it inside each test. 4. **Reset between tests.** A shared graph means a shared stand-in, so clear programmed behaviour and recorded calls in teardown. Without this, step 3 trades boots for order-dependent failures. 5. **Keep one-offs deliberate.** A wiring two tests need is fine; make it a named wiring with an owner rather than an ad-hoc entry, so the count stays visible. ## The tradeoffs to state out loud | choice | buys | costs | |---|---|---| | per-test wiring | exact isolation, nothing shared | a full boot per test, and a cache entry per test | | a few shared wirings | most tests reuse a warm graph | shared mutable stand-ins that must be reset | | one wiring for the whole suite | the fewest boots possible | every test carries every replacement, and the suite drifts furthest from production | The middle row is where most suites should sit. The bottom row looks attractive on the clock and is the one that quietly converts the suite into a test of a fictional application. ## Second-order costs people forget - **Caches hold graphs open.** Each retained graph keeps its pools, threads and any bound ports alive. A suite with dozens of distinct wirings can run out of resources even though each individual boot succeeded; harnesses usually cap the cache and evict, at which point an evicted wiring is re-booted the next time it is asked for and the savings partly evaporate. - **Dirty-marking is a rebuild.** Flagging a graph as no longer trustworthy after a test discards it and forces the next one to boot. It is the right tool for a test that genuinely corrupted shared state, and an expensive habit when applied by default. - **Boot order affects the clock, not just the count.** Interleaving tests from different wirings can thrash a size-limited cache; grouping tests by wiring so each graph is booted once and used for a run of tests is often a larger win than removing a replacement. ## Diagnosing it honestly Before restructuring anything, measure: count boots per run and attribute each to the wiring that caused it. Suites routinely blame "slow integration tests" for time that is actually spent on repeated startups driven by three tests that each added one replacement. The distribution, not the total, tells you what to merge. ## What interviewers listen for They want to hear that you know **what the cache is keyed on**, and that per-test isolation and suite speed are the two ends of one dial rather than separate problems. The strongest answers add the honesty clause: converging wirings means tests share mutable stand-ins, so resetting state between tests is part of the fix, not an optional extra — and that a suite with a single wiring for everything has bought its speed by testing an application that no deployment resembles.
- What do you lose by converging many wirings into a few shared ones?Isolation. Tests now share stand-ins, so leftover programmed behaviour or recorded calls can cross between them, and a test may boot with replacements it never wanted — which can mask a wiring fault it would otherwise have hit. Resetting stand-in state per test and keeping at least one unmodified wiring in the suite are the compensating moves.
- When is a dedicated wiring for a single test still the right call?When the variation is structural rather than behavioural — a different lifetime, a boundary bound to something of another shape, a component that must be absent at startup. Those cannot be expressed by reprogramming a stand-in. Name the wiring, note why it exists, and accept the boot.
- Why can adding more distinct wirings make a suite slower than the extra boots alone suggest?The harness's cache is bounded. Past that bound it evicts, so a wiring that was reused earlier is re-booted later, and interleaved tests can thrash the cache. Retained graphs also hold pools, threads and ports open, which can push the run into resource limits.
saying these in an interview costs you the question
- Believes the harness reuses a graph regardless of which replacements a test declared
- Adds a dedicated wiring per test and blames the framework for the runtime
- Shares one graph everywhere without resetting stand-in state between tests
- Marks the graph dirty by default, forcing a rebuild after every test
- Assumes caching more wirings is free of memory, thread and port cost