Six frontend apps in your organisation each maintain their own fixtures for the same internal API. How would you decide whether to invest in a single schema-derived fixture package, and what would you watch for after adopting one?
answer
- measure the harm before centralising
- share types before sharing data
- generated, never hand-edited
- factories absorb per-team variation
- N drifts become one correlated drift
basics
~20 sDecide from evidence of harm: how often duplicated fixtures caused incidents, and how much rework each API change costs across six repos. A shared package pays off only if it is generated from the schema and ships factories, not frozen payloads — otherwise it becomes a seventh, more confident copy.
solid answer
~50 sI would first measure the actual damage: how many production incidents traced back to a stale fixture, and how many repos need touching per API change. If duplication is cheap in practice, generating types centrally and leaving fixtures local captures most of the value for a fraction of the cost. If it is expensive, the package has to be generated from the published schema and expose factories with overrides rather than frozen blobs, or teams will fork it the first time they need a variation. Ownership matters more than the artifact — a package published by the API team alongside its schema is strongest, because the mock becomes a released output of the service, but that is work someone must staff. After adoption I watch for version skew, where an app pins an old release and quietly tests against last quarter's contract, and for the package acquiring hand-maintained values that make it a second definition of the API.
go deeper
Know that duplicated fixtures across projects drift independently, and that generated types or a shared package are the usual responses. You are not expected to run this decision.
Be able to explain the mechanics of a shared fixture package: factories over constants, versioning tied to the schema, and why a hand-edited package is worse than local copies.
Show that you would justify the investment with evidence — incidents traced to stale stubs and the cost per API change — and that you would try shared types before shared data.
Own the tradeoff explicitly: sharing converts many independent drifts into one correlated drift with a wide blast radius, so it is worth it only with a named owner, a regeneration trigger, and visibility into which app is pinned to which contract version.
## Name the cost before proposing the cure Six copies of a fixture set is not automatically a problem. It becomes one when it produces measurable harm, and there are only a few places to look: - **Incidents traced to a stale stub** — how many times did a green suite ship a break, and in how many of the six apps? - **Change amplification** — when the API changes, how many repositories must be touched, by whom, and how long does the last one lag? - **Divergence** — do the six copies already disagree about the same endpoint? Two apps holding different beliefs about one response is a sign the copies are being maintained by memory. If the answers are small, the honest recommendation is to do the cheap thing and stop. If they are large, they justify the coordination cost of a shared artifact and give you the number to revisit later. ## The cheaper eighty percent Before centralising fixtures, centralise *types*. Publishing generated types from the schema is far less coupling than publishing data: no app has to change how it writes tests, each keeps its local fixtures, and every one of them gains a build-time break when the contract moves. In many organisations that alone removes most of the incident class, and it is the option I would rule out explicitly before proposing anything heavier. ## What a shared fixture package must be If you do build one, three properties decide whether it survives. 1. **Generated, not hand-maintained.** The moment a human edits values in the package, it becomes another belief about the API — only now six teams share the belief and trust it more. Defaults should be derived from the schema, with a recorded sample as the source of realistic values. 2. **Factories, not frozen payloads.** Teams need per-test variation. A package that exports constant objects will be spread, mutated and forked within a month; one that exports `makeUser(overrides)` absorbs the variation by design. 3. **Versioned with the schema.** The package version must map to a schema version, so "which contract is this app testing against" has an answer you can read off a lockfile. ## Who should own it - **A platform or frontend-infrastructure team.** Realistic and easy to staff, but the owners are one step removed from the API and will learn about changes at the same time everyone else does. - **The API team, published alongside the schema.** The strongest form, because the mock becomes a released artifact of the service and moves when the service moves. It is also real work the API team must be funded to do, and they must want to do it; an unowned mandate produces a package that ages faster than the copies it replaced. - **Nobody in particular.** The common outcome and the worst one: a shared package everyone depends on and no one regenerates. Whichever you choose, the decision to record is *who regenerates it and on what trigger*, not which repository it lives in. ## What to watch for after adoption - **Version skew.** App four pins release 2.1 for eight months. Its tests are green against a contract that no longer exists, and centralisation has made that invisible rather than obvious. Guard it with a floor on the accepted version range and a dashboard of which app is on which schema version. - **Over-permissive defaults.** One sloppy default is now wrong in six apps simultaneously, and each team assumes the shared package is authoritative. Blast radius grows with sharing; that is the price. - **Local escapes.** Casts, deep spreads and locally re-declared payloads appear whenever the package cannot express something a team needs. They are a design signal, not misbehaviour — treat a rising count as a request for a missing factory. - **A shadow definition.** Watch for the package drifting from the schema it claims to derive from. A conformance job that validates every exported factory against the current schema on every release keeps it honest. ## The judgment to demonstrate The answer an interviewer is listening for is not "centralise". It is that shared test infrastructure trades N independent drifts for one correlated one, that this trade is only worth making when the duplication is demonstrably causing harm, and that whatever you build must be generated and owned, because an unowned shared mock is a more confident lie than six local ones.
- Why is publishing generated types a lighter-weight move than publishing fixtures?Types couple only the shape, not the data. Each app keeps writing its own fixtures in its own style and still gets a build-time break when the contract moves. Publishing fixtures additionally dictates values, defaults and factory ergonomics across six teams, which is where the friction and the forking come from.
- How would you detect version skew across apps consuming a shared fixture package?Map each package release to a schema version, then report which version each app has locked. A simple dashboard or a CI rule that fails when an app is more than one release behind turns skew into a visible, actionable number instead of a silent divergence hidden by a green suite.
- What would make you recommend leaving the six copies alone?Little evidence of harm: few or no incidents traced to stale fixtures, API changes that touch one or two repos, and copies that have not visibly diverged. Then I would ship generated types, set a threshold on incidents per quarter, and revisit centralisation if that threshold is crossed.
saying these in an interview costs you the question
- Centralising before duplication has caused any measurable harm
- Shipping frozen fixture blobs instead of factories
- A shared package hand-maintained rather than generated
- Ignoring version skew because the suites are green
- Assuming one shared package eliminates drift risk entirely