Your platform builds stand-ins for many teams' services at run time: do you mandate a declared contract, or generate subtypes of their concrete classes?
answer
- applicability, not performance
- constant cost against variable risk
- the properties live in others' files
- fail at wiring, not at the call
- a hybrid with a stated rule
basics
~20 sMandating a contract makes every stand-in buildable by one mechanism and keeps the platform out of other teams' class shapes. Generating subtypes avoids that tax but makes applicability depend on code you do not own and cannot keep stable.
solid answer
~40 sThis is an applicability decision, not a performance one. A contract mandate means every intercepted service publishes a declared contract and its callers depend on it; in exchange the platform builds every stand-in the same way and imposes nothing on how a class is written. Generating subtypes asks nothing of teams up front, but each stand-in is then conditional on that class permitting extension, on the intercepted members being overridable, and on construction being safe to repeat - properties another team can change without knowing you depend on them. The deciding question is when a failure is discovered. Whichever policy you pick, check applicability where the stand-in is built and refuse loudly, naming the member, rather than shipping interception that quietly does nothing.
go deeper
Recall that a platform offering interception has to require something of the code it intercepts - either a declared contract, or a class shaped so it can be extended and overridden.
Explain what each policy demands: a contract mandate needs declarations and re-pointed call sites, subtype generation needs extensible classes with overridable members.
Argue from failure timing. A missing contract is caught once, in one place; a class that stopped being extensible is caught late, or silently not at all.
Own the trade: a constant, visible cost paid by every team against a variable risk the platform carries in code it does not review, plus the rule that makes a hybrid defensible.
## Stating the decision precisely The platform offers one capability: given a service object, hand back a stand-in that routes calls through a handler. The two policies differ in **what the platform requires of the teams it serves**. - **Contract mandate.** Every service that wants interception publishes a declared contract, and its callers depend on that contract. The platform always synthesizes from declarations. - **Subtype generation.** Teams keep writing whatever they write. The platform extends each concrete class and overrides the members to intercept. Neither is free, and the costs land on different people, which is why this is a lead's call rather than an implementation detail. ## What each policy costs | Dimension | Contract mandate | Subtype generation | |---|---|---| | Cost to teams | A declaration to maintain, call sites re-pointed once | Nothing visible up front | | Cost to the platform | Migration help, and a rule to enforce | A dependency on how others write classes | | Applicability | Uniform: if the contract exists, it works | Conditional on each class and member | | Failure timing | At the boundary, when the contract is missing | Whenever someone marks a class non-extensible | | Blast radius of a refusal | One service that has not declared yet | Any service whose shape drifted | | Coverage | Exactly the declared members | Only what could be overridden | The contract mandate front-loads a **constant** cost per service and buys a property that does not degrade. Subtype generation defers the cost and converts it into a **variable** risk carried by the platform, spread across code the platform does not review. ## The part that actually decides it: when you find out The difference that matters in operations is not what the two policies can do, it is when they tell you they cannot. 1. A missing contract is visible at the moment a team asks for interception, in one place, with one obvious remedy. 2. A class that has become non-extensible, or a member that is no longer overridable, is visible only when the stand-in is built - and if the machinery is forgiving, not even then: the stand-in is created and the intercepted behaviour silently does not apply. That second case is the one that ruins a platform's credibility, because behaviour that quietly does nothing is indistinguishable from behaviour that is turned off. Whichever policy you adopt, make the check explicit **where the stand-in is built**: confirm the class permits extension, confirm each member the team asked to intercept is overridable, and refuse with the member named. A loud failure at wiring is cheap; a silent one is discovered by an auditor. ## A defensible middle Most platforms end up with a hybrid, and it is defensible when the rule is stated rather than emergent: - **Require a contract for anything that crosses a team boundary**, where the declaration is useful on its own merits and the callers were going to be re-pointed anyway. - **Allow subtype generation inside a team**, where the same people own the class, the constructor and the decision to seal it. - **Publish the requirements as a checklist** - extensible class, overridable members, constructor free of side effects - so a team can see whether their code qualifies before they depend on it. - **Re-check at build or start-up**, not at review time, because the properties you depend on live in someone else's file and drift without a conversation. ## What tips the balance Three things push toward the contract mandate: - **Scale of the estate.** The more teams, the more the variable risk of subtype generation is realised somewhere every quarter. - **Environments that forbid creating types after start-up.** Where run-time synthesis is unavailable at all, the stand-in has to exist before the program starts, and a policy built on run-time construction needs that path planned rather than discovered. That is the neighbouring mechanism, not this one, but it constrains this decision. - **The value of the declaration itself.** A published contract is a testing seam and a documentation surface, not only a hook for interception, so part of the cost buys something a second time. And one pushes the other way: **you cannot always change the callers**. Where call sites are compiled elsewhere or too numerous to touch, the mandate is not a policy, it is a wish, and subtype generation plus an explicit applicability check is the honest answer.
- You pick the contract mandate - what makes it expensive for teams who were not writing contracts?Every intercepted service now publishes a second declaration that must stay in step with its class, and existing call sites must be re-pointed at it. The new code is trivial; the migration of call sites dominates. Teams also lose the ability to intercept anything they forgot to declare, which is a real narrowing of coverage.
- How do you stop the subtype policy from failing silently?Check applicability when the stand-in is built, not when a call arrives: confirm the class permits extension and that each member the team asked to intercept is overridable, then refuse loudly with the member named. Silence is the whole danger, because interception that quietly does nothing looks exactly like interception nobody switched on.
- What happens to either policy where the runtime forbids defining new types after start-up?Run-time synthesis is unavailable, so the stand-in must exist before the program starts and the work moves into the build. That is a different mechanism with different constraints. A policy that assumed run-time construction discovers this at the worst moment, so the path belongs in the decision rather than after it.
saying these in an interview costs you the question
- Treats the choice as a performance question rather than an applicability one
- Assumes every team's class is extensible with overridable members
- Mandates contracts without costing the migration of existing call sites
- Lets a stand-in that cannot intercept a member start up silently
- Believes one policy removes the need to check applicability at all