skip to content

What makes an extension point in an automated suite different from shared code everyone edits?

level: middleimportance: should knowfreq 46%

answer

  1. Ask who calls whom at that seam
  2. A contract, not a convention
  3. Registered, never edited in place
  4. The framework owns when it calls

basics

~20 s

An extension point is a declared contract the framework itself calls: a named operation, a registration step, and a stated moment in the run. Shared code everyone edits has none of those, so every change reaches every case.

solid answer

~40 s

Three properties together make a seam an extension point. First, a **declared contract**: a named operation with a fixed shape of inputs and outputs, publishable without exposing internals. Second, a **registration mechanism**: a suite says which implementation it wants without editing shared source. Third, a **defined moment**: the framework states when it calls, how often, in what order, and what a thrown error does. The quick field test is the direction of the call — at an extension point the framework calls your code, whereas with a shared helper your code calls it. A helper everyone edits has no contract, so its shape drifts; no registration, so the only way to differ is a conditional per suite; and no way to enumerate dependants, so each edit silently changes behaviour for cases nobody reviewed.

code

pseudocode · 12 lines
pseudocode
# 1. the contract the framework publishes
contract CaseObserver:
    on_case_start(case_info)
    on_case_end(case_info, outcome)

# 2. a suite registers its own - shared source untouched
registry.add_observer(CaptureOnFailure())
registry.add_observer(TimingRecorder())

# 3. the framework owns the moment and the ordering
for observer in registry.in_registration_order():
    isolate(lambda: observer.on_case_end(case_info, outcome))

go deeper

for a junior

Be ready to define an extension point in one sentence: a place the framework calls out to code you supply, through a shape it publishes. Knowing the term and the direction of the call is enough at this level.

for a middle

Explain the three things a seam needs before it counts — a declared contract, a way to register an implementation, and a stated moment the framework calls it — and give a concrete example of each in an automated suite.

for a senior

An interviewer expects judgement about whether a seam should exist at all. Weigh the frozen surface against real dependants, and be able to diagnose a suite whose behaviour changed because someone edited a shared routine rather than plugging into a contract.

for a principal

Own the policy: which seams the organisation publishes, what each one promises, and who reviews a new one. The tradeoff to articulate is extension freedom for many teams against the permanent surface every published seam adds.

## What a seam has to have before it counts An **extension point** is a place where a framework calls out to code it does not own, through a shape it publishes and keeps. That last clause carries the whole definition. A place in shared code that people happen to edit has none of it, and the difference shows up the first time two suites want different behaviour at the same moment. Three properties have to be present together: 1. **A declared contract.** A named operation with a fixed shape of inputs and outputs. Somebody can implement it without reading the framework's source, because the shape says what will arrive and what may be handed back. 2. **A registration mechanism.** A way for a suite to say *use mine* without editing framework code. It may be a list the suite writes, a configuration entry, or a marked implementation the framework finds; what matters is that the shared code is untouched. 3. **A defined moment.** The framework states when it calls, how many times, in what order relative to other registered implementations, and what happens if the call throws. Without this, extensions work by folklore. The quickest field test is the **direction of the call**. In an ordinary utility relationship your code calls shared code. At an extension point the framework calls yours: you hand it an implementation and it decides when to invoke it. That inversion is precisely what lets one suite add behaviour without any edit to code every other suite shares. Reuse through a shared ancestor type is a different mechanism with different tradeoffs; it is not what makes a seam an extension point. ## The three shapes you meet in practice | Seam | Who calls whom | What the contract fixes | Symptom when it is not a real seam | | --- | --- | --- | --- | | Callbacks around a case | the runner calls your code | the moment, the data passed, the error policy | teams paste their setup into one shared routine | | Pluggable observers of the run | the run pushes outcomes to you | the events, their ordering, isolation on failure | outcome handling is wired into the runner itself | | Swappable adapters | your case calls a contract, the suite picks the implementation | the operations, the error shape, who selects | a conditional in shared code, one branch per target | - **Callbacks around a case** let a suite take a screenshot on failure, stamp a correlation identifier, or reset a data seam, without owning the runner. - **Pluggable observers** let one suite publish outcomes somewhere the framework never heard of. The framework promises the events; it does not promise the destination. - **Swappable adapters** let a case express *what* it wants while a suite decides *how* it reaches the system under test. ## Why editing the shared helper is a different thing Every property above has a matching failure when it is missing: - **No contract.** The shape drifts with each caller. Two suites add a parameter, a third reads a field the first never sets, and nobody can say what the helper accepts any more. - **No registration.** There is exactly one implementation for everybody, so the only way to differ is a conditional keyed on which suite is running. The number of branches grows with the number of teams. - **No stated moment.** Whether your code runs at all depends on which path happened to reach the helper, so behaviour becomes a function of call history rather than of the contract. - **No visibility.** Nobody can enumerate the dependants, so nobody can predict what an edit affects. Every change to shared code is a change to every suite, discovered by whoever's run goes red first. That last point is the practical one. A seam concentrates change: an edit to your implementation affects your suite. A shared helper distributes it: an edit affects everyone, including cases nobody in the review had in mind. ## The cost side: do not publish one you cannot keep A published seam is not free. It freezes a shape you now owe to whoever implements it, it adds a layer a reader must step through when tracing behaviour, and it moves some of the run's behaviour out of the code you can read into whatever was registered. Two habits keep that cost honest: - **Wait for a second real dependant.** A contract guessed from one implementation is speculative: you pay the frozen surface and buy no flexibility. With two implementations in hand, extract the contract from what they actually share. - **Keep the seam enumerable and shallow.** You should be able to ask a run what is registered and get a list back. And the extension should receive data, not the framework's private internals — the moment an implementation reaches into internals, those internals have quietly become part of what you promised. A seam that satisfies all of this can be added to by a suite that never touched the framework's source, and removed by that suite just as cleanly. That is the property worth interviewing for.

  • When is publishing an extension point premature?
    When there is exactly one implementation and no second caller in sight. You pay the full cost of a seam — a frozen shape, a documented moment, an extra layer to step through when tracing behaviour — and buy no flexibility. Wait for the second real dependant, then extract the contract from the two implementations you already have rather than guessing what a future one will need.
  • What should happen when a registered extension throws?
    Decide it once, publish it, and apply it uniformly. The usual choice is isolation: catch, record the failure against that extension by name, and let the run continue, because an add-on that reports outcomes should not be able to fail cases. Where the extension supplies something the case cannot proceed without, failing loudly is right. Either way the framework decides the policy, not each implementation.

A published extension point is a wall socket: the shape is fixed, anything that fits may be plugged in, and nobody opens the wall. Shared code everyone edits is splicing into the cable.

saying these in an interview costs you the question

  • Calls any shared helper an extension point
  • Publishes a seam with a single implementation
  • Cannot say when the framework calls the extension
  • Requires extensions to reach into framework internals
  • Adds new behaviour by editing the shared setup routine