skip to content

What standard would you set for a shared library on whether routines declared over an unknown type may inspect it?

level: principalimportance: should knowfreq 28%

answer

  1. pick a default, then name the exceptions
  2. the declaration carries the review
  3. inspection makes correctness a per-type property
  4. boundaries where values leave the type system
  5. could the caller pass it instead

basics

~20 s

Default to no inspection, and make each exception a named, documented, tested contract. A declaration that proves its own behaviour costs reviewers nothing; one that permits inspection turns every future type argument into a path someone has to verify by hand.

solid answer

~50 s

The default is uniform: a routine declared over a type it does not name does not ask what that type is. The reason is economic rather than aesthetic — the declaration then carries the review for every team and every type argument that will ever be supplied, including ones nobody has written. Exceptions are real, chiefly at boundaries where values leave the type system altogether, so the standard should name the categories that qualify rather than pretend there are none. For each exception: the behaviour is documented per type, there is a test per supported type, the routine is named so callers can see it is type-directed, and the fall-through case is specified. The first question in review stays the same, though — could this decision be an argument the caller supplies instead of a guess the body makes?

go deeper

for a junior

Take the working habit: prefer routines that do not ask what type they were handed, because anyone reading the declaration then knows what they do.

for a middle

Be able to argue the default from the mechanism — a body that cannot inspect has behaviour bounded by its declaration, which is what makes one test enough for every type argument.

for a senior

Show where you would allow exceptions and what you would demand with them: per-type documentation, a test per supported type, a specified fall-through, and a name that advertises the routine is type-directed.

for a principal

Own the trade being made. Name the two currencies — reviewer attention across teams versus convenience at the call site — say which is scarce here, and state what evidence would make you revise the default.

## The decision being made A library used by many teams has to choose a default for one question: may a routine declared over a type it does not name find out what that type is and behave differently? The answer is a standard, not a preference, because whichever way it goes it is applied by people who will never meet each other. The case for **uniform by default** is about who pays for review. When a body cannot inspect its type argument, the declaration bounds what the body can do, and that bound holds for every caller, every type argument, and every future change that keeps the declaration. Review scales, because reading the declaration is the review. When inspection is permitted, correctness becomes a per-type property, and someone has to establish it for each type and re-establish it whenever a new one appears. ## What the rule buys and what it costs | | Uniform by default | Inspection permitted freely | |---|---|---| | Reviewing a call site | the declaration is enough | read the body with this type in mind | | A type argument nobody anticipated | provably no new path | a new, unexercised branch | | Tests needed | correct once, correct for all | one per supported type | | Rewrites across the routine | licensed by the declaration | re-established by hand each time | | Cost at the call site | operations must be passed explicitly | the body guesses, callers write less | | Cost to the library author | more arguments, more ceremony | branches that accumulate quietly | The last two rows are the honest price of the rule. Uniform code pushes work outward: if a routine needs to know how to compare, format or encode something, the caller has to hand that over instead of the body inferring it. That is more typing and, for a while, more friction. What you get back is that nobody ever has to ask what the routine does for a type they just invented. ## Exceptions worth carving out deliberately A blanket ban with no exceptions is the version of this standard that gets quietly ignored, so name the categories up front: - **Boundaries where values leave the type system** — encoding for storage or transport, and the decoding that comes back. Behaviour genuinely has to depend on what the value is, and banning it there only pushes the same decision into every caller, where it is harder to see and duplicated. - **Layers whose whole job is to choose an implementation by type.** If that is the routine's purpose, it is not uniform code with a shortcut in it; it is dispatch, and it should be declared and named as such. - **A measured, isolated performance decision**, kept behind a declaration that is otherwise uniform and accompanied by evidence that the difference mattered. Everything else — a missing argument guessed from the type, a nicer error message, a convenience for one caller — should go back and be passed in. ## Making the standard hold A standard nobody can apply is a document, not a rule. Four things make this one applicable: 1. **A default in writing, with the reason attached.** "Uniform unless listed" beats "avoid run-time type tests", because the second invites a case-by-case argument every time. 2. **A naming and documentation convention** so that a type-directed routine is visibly type-directed, with its behaviour per type and its fall-through case written down. 3. **A test expectation** — a case per supported type argument for anything that inspects, and explicitly no such matrix for anything that does not. 4. **One review question**, asked every time: could this branch be an argument the caller supplies? Most exceptions dissolve at that question, and the ones that survive are the real ones. ## Knowing whether the standard is right Set out in advance what would change your mind, because a standard that cannot be wrong is not being tested: - **If the exception list keeps growing**, the problem is probably the abstraction, not the rule — the library is being asked to do something its declarations cannot express, and the fix is to change what callers pass rather than to loosen the default. - **If defects cluster in the inspecting routines**, the rule is paying for itself and is worth tightening. - **If teams route around the library** because the ceremony is too high, the cost side of the table is winning and the default should be revisited for the surfaces where that is happening. The judgement being asked for here is not "inspection is bad". It is knowing which of these two currencies — reviewer attention across many teams, or convenience at the call site — is scarce in your organisation, and setting the default for the scarce one.

  • When is a blanket ban on inspection the wrong call?
    At the edge where values stop being typed at all — encoding for storage or transport. Behaviour there genuinely depends on what the value is, and forbidding it merely scatters the same decision across every caller, where it is duplicated and less visible than in one documented place.
  • How would you tell whether the standard is working a year in?
    Look at two counts. If defects cluster in the routines that inspect, the default is paying for itself. If the exception list keeps growing, the abstraction is wrong rather than the rule, and the answer is to change what callers pass rather than to loosen the default.
  • What is the first question to ask about a proposed exception?
    Whether the branch could be an argument instead. Most requests to inspect are a missing parameter in disguise: the body needs one piece of information and guesses it from the type. Passing it makes the declaration honest again and puts the choice where someone can see it.

saying these in an interview costs you the question

  • Bans inspection with no exceptions and no migration path
  • Treats it as style rather than a cost to reviewers
  • Cannot name what the uniform declaration actually buys
  • Allows exceptions without documenting per-type behaviour
  • Assumes the ceremony at call sites costs nothing
  • Sets a standard with no signal that would revise it