skip to content

A type satisfies every operation your shape-based requirement names yet means something entirely different — what risk has the requirement failed to catch?

level: seniorimportance: nice to knowfreq 33%

answer

  1. members match, meaning does not
  2. nobody declared an intention
  3. common verbs, common parameter types
  4. green build, wrong output later
  5. widen the shape, publish the result type

basics

~20 s

Accidental conformance: a shape checks member names and signatures, never meaning, so a type that never intended to be an add-in can match and be accepted. The mismatch compiles clean and surfaces as wrong behaviour at run time.

solid answer

~50 s

The requirement matched a shape, and a shape carries no semantics. Two unrelated types can both expose an operation of the same name and signature while meaning opposite things — one that discards pending work and one that completes it, one that returns a discount and one that returns a penalty. Because no declaration of intent exists, the host accepts the impostor, the build is green, and the defect appears as wrong output inside the add-in. The narrower and more ordinary the shape, the more likely this is: one common verb over common parameter types matches a large part of any codebase. You narrow the gap by widening the shape, by having an operation traffic in a result type you publish, or by requiring a named supertype for the part of the contract where behaviour actually matters.

code

pseudocode · 16 lines
pseudocode
requires T has { apply(amount: Number) -> Number }

type LoyaltyDiscount {
    function apply(amount: Number) -> Number {
        return amount - (amount * 0.10)   // reduces
    }
}

type LatePenalty {
    function apply(amount: Number) -> Number {
        return amount + (amount * 0.10)   // increases
    }
}

// both satisfy the requirement; the host cannot tell them apart
function totalFor(rule: T, amount: Number) { return rule.apply(amount) }

go deeper

for a junior

Recall that matching by shape compares operation names and signatures only, so a type can satisfy a requirement without ever meaning to.

for a middle

Explain why a narrow shape over common types is easy to match by chance, and name mitigations such as widening the shape or requiring a result type the host publishes.

for a senior

Trace where the defect actually surfaces — checker accepts, build green, host tests pass, wrong output in production — and choose mitigations for a real extension point rather than reciting them.

for a principal

Decide where the behavioural core of your contract lives and what you require there, knowing that intent is buyable with a declaration while conduct is only ever buyable with tests.

## The hazard a shape cannot see A structural requirement is satisfied when the members line up: names, arity, parameter types, result types. Nothing in that comparison examines what the operation *does*, and nothing in the candidate ever said it wanted to participate. When a type matches by coincidence, the type system has done exactly what it was asked and still admitted the wrong thing. This is **accidental conformance**, and it is the standing cost of matching by shape. It is not a theoretical cost. Consider a plugin host whose requirement is a single operation taking a number and returning a number, called `apply`. In any large codebase there are several such operations, and at least one of them means the opposite of what the host assumes. ## Why the risk is real, and when it is worst - **Common verbs.** `run`, `apply`, `close`, `reset`, `handle`, `next` are the names every codebase reaches for. - **Common parameter types.** A shape over primitive-ish types matches far more candidates than one over domain types. - **Narrow shapes.** One operation is easy to hit by chance; six related operations with consistent naming are not. - **Similar shapes with opposite polarity.** The dangerous case is not an unrelated type but a *neighbouring* one — the same operation on the same data with the sign, the unit or the ordering reversed. ## Where the failure surfaces 1. The checker accepts the type argument, because the members are there. 2. The build is green and review sees nothing unusual, because there is no conformance declaration to read; the only evidence is the use site. 3. The host's own tests pass, because they exercise conformers the host wrote. 4. The defect appears at run time, inside the add-in, as wrong output rather than as a crash — and the exception you would have wanted is never thrown, because every call is well typed. That ordering is the whole reason the risk is worth naming in an interview: every gate the mistake passes is a gate that was working correctly. ## Narrowing the gap None of these makes meaning checkable; each makes an accident less likely. 1. **Widen the shape.** Require the whole cluster of operations the protocol actually involves, not the one the body happens to call. Coincidence scales badly with width. 2. **Traffic in a type you publish.** If one required operation returns a result type that is yours, an unrelated type cannot match it without referencing your artifact — which reintroduces intent through the back door, and reintroduces the dependency edge with it. 3. **Require a marker the candidate must supply deliberately** — an operation returning a protocol version or capability descriptor that has no reason to exist by accident. 4. **Split the requirement.** Use a shape for the parts that are pure data access and a named supertype for the part that carries behaviour and ordering guarantees. 5. **Publish conformance tests.** The host can ship an executable contract that a candidate must pass, moving the check from the type system to the build. This catches meaning the way no requirement form can. ## What a named requirement does and does not buy here A named supertype excludes accidental conformance *by construction*: a type is only a conformer if its author said so, and nobody writes that line by chance. That is a real guarantee and it is worth paying for on an extension point that matters. It is not, however, a guarantee of correct behaviour. A declared implementer can still return the penalty when you meant the discount; it has agreed to the contract and broken it. The difference is accountability rather than safety: | | Requiring a shape | Requiring a named supertype | |---|---|---| | Match by coincidence | possible | excluded | | Behaviour guaranteed | no | no | | Someone to hold to the contract | nobody declared anything | the declaring author | | Where review can see intent | the use site only | the candidate's declaration | ## What an interviewer is listening for - The name of the failure and the reason for it: the check is over members, not meaning. - That you can say **where** it surfaces — clean build, green host tests, wrong output in production — rather than just calling it unsafe. - That your mitigations reduce probability honestly and you say so, instead of claiming a shape can be made to check semantics. - That you do not overcorrect: naming a supertype removes the accident, not the possibility of a conformer behaving wrongly.

  • Why does requiring an operation whose result type you publish make accidental conformance far less likely?
    Because an unrelated type cannot produce that result without referencing your artifact, and referencing it is a deliberate act. The match stops being a coincidence of names over shared primitive types. Note what you have bought it with: the dependency edge that matching by shape existed to avoid now reappears, on that one operation.
  • Does requiring a named supertype guarantee the add-in behaves as the contract says?
    No. It guarantees intent, not conduct. A declaring author has agreed to the contract and can still violate it — returning the wrong sign, mutating a value it promised to leave alone, blocking where the contract said it would not. What the declaration buys is an accountable party and a visible place for review, documentation and versioning to attach.
  • Which mitigation catches a wrong meaning that no requirement form can?
    An executable conformance suite the host publishes, which the candidate's build runs against its own type. Requirements settle members; tests settle behaviour. The cost is that add-in authors must wire the suite into their pipeline, which is a process commitment rather than a type-system one, and it moves the failure from their compile to their test run.

Two departments can use forms with the same three boxes — name, date, amount — one a refund request and the other a payroll entry. Matching the boxes does not tell you which form you were handed.

saying these in an interview costs you the question

  • Says a shape check verifies behaviour as well as members.
  • Claims accidental conformance is only a textbook worry.
  • Believes declaring a published supertype makes the add-in behave correctly.
  • Expects the mistake to show up as a type error or a crash.
  • Thinks narrowing a requirement to one operation makes matching safer.