skip to content

A departure placeholder in a shared library has grown from two requirements to a proposed third - how do you decide between accepting it, splitting the routine, or naming a concept?

level: principalimportance: should knowfreq 27%

answer

  1. count branches, not entries
  2. a published list is a contract
  3. adding narrows, removing widens
  4. capability can arrive as a parameter
  5. split before you name a concept

basics

~20 s

Decide from the body and the callers, not the list length. Split when only one branch needs the third capability, name a concept when the three always travel together and you own the candidate types, and otherwise take the capability as a parameter rather than demanding it from the type.

solid answer

~50 s

Start by asking which part of the body needs the third capability. If it is one branch, the routine is doing two jobs and splitting it gives each half a narrower requirement list and more admissible callers. If every path needs all three and the same trio appears across many signatures, that is a concept worth naming - but only if you own the candidate types, since a named carrier type they must be declared under is worthless when they belong to other teams. The third option is to stop demanding the capability from the type at all and take it as a parameter the caller supplies. Weigh the compatibility direction too: adding a requirement narrows the admissible set and breaks callers whose type argument lacks it, while removing one widens the set and leaves existing calls compiling.

go deeper

for a junior

Recall the baseline rule: a routine should require only what its body calls, so a capability used by nothing is removed rather than kept for symmetry.

for a middle

Explain the split option concretely: when one branch needs the extra capability, two routines with narrower lists admit more callers than one routine with a longer list.

for a senior

Show the compatibility reasoning on a published signature: which existing type arguments stop qualifying, and what you offer those callers in the same change.

for a principal

Own the standard: when a recurring combination earns a name, who pays for the redeclaration, and how requirement-list changes are versioned and announced across teams.

## Why a third requirement is worth a conversation A requirement list is a public contract, and every entry is a demand on everyone who calls the routine. Two entries usually describe a routine that does one job needing two capabilities - order these, then render them. A third entry is often the moment the routine stopped doing one job: it now orders, renders, and does something else that only some inputs support. Nothing about the number three is magic. The signal is that each added entry multiplies the reasons a would-be caller is excluded, while the body frequently uses the new capability on only one of its paths. So the decision is made by reading the body and the call sites, not by counting entries. ## Four honest responses | Response | When it is right | What it costs | |---|---|---| | Accept the third requirement | every path in the body uses it, and callers already have it | the admissible set narrows for everyone, including future callers | | Split the routine | only one branch needs it | two entry points to document, and callers must pick | | Name a combined concept | the same trio recurs across many signatures and you own the types | every candidate type must be redeclared under the new name | | Take the capability as a parameter | the type has no natural claim to the capability | one more argument at every call site | The fourth is the one candidates most often miss. If the new demand is that a departure can be formatted for a second display, that is not necessarily a property the departure type owns - a formatting function supplied by the caller does the same work, leaves the requirement list at two, and lets one departure type be formatted differently in different places. Demand a capability from the type when it is genuinely intrinsic to it; pass it as a parameter when it is a policy the caller chooses. ## The compatibility direction Changing a published requirement list is not symmetric, and the direction is easy to state backwards: - **Adding** a requirement **narrows** the set of admissible type arguments. Every existing caller whose type argument lacks the new capability stops compiling. This is a breaking change, and the breakage lands on people who did nothing. - **Removing** a requirement **widens** that set. Every existing call still compiles, because a type argument that satisfied more still satisfies less. The risk lands on the body instead: it may no longer call what the removed entry declared. So a third requirement on an already-published routine is a version-scale decision, while the same third requirement on a routine nobody calls yet is a five-minute design choice. Judge the two differently. ## How to decide, in order 1. List the paths in the body and mark which ones touch the new capability. One path means split. 2. Count the candidate types that satisfy all three today, and how many of them you control. If the answer is few and you control none, no bound edit is available to you - the capability has to arrive as a parameter. 3. Look for the trio elsewhere. A combination appearing once is a local list; appearing across many signatures is a concept, and a concept you own is worth naming. 4. Check the implementation slot. If the new requirement carries state and one of the existing entries already does, the list is unsatisfiable under single implementation inheritance and the decomposition must change regardless of preference. 5. If you still add it, publish the change as breaking, name the type arguments that stop qualifying, and give the affected callers the parameter-passing alternative in the same note. ## What to standardise for a team - Say in review terms what a requirement list is for: exactly the capabilities the body calls, and nothing added because it felt related. - Set the threshold for naming a combined type on recurrence and ownership, not on list length; a name nobody can make their types wear is worse than a long list. - Treat any edit to a published list as an API change with a compatibility direction stated explicitly, so the person reviewing it does not have to work out which way it cuts. - Keep the escape hatch visible: a capability the type does not naturally own belongs in a parameter, and a team that remembers this writes shorter requirement lists without losing anything.

  • The same three requirements travel together across ten signatures. What does that argue for?
    A named combined type, if you own the candidate types. A trio repeated that often is a concept the domain already has, and naming it shortens ten signatures. If the candidate types belong to other teams, keep the list: a name you cannot make them wear buys nothing.
  • Which direction of change to a published requirement list is safe for existing callers?
    Removing an entry. It widens the admissible set, so every type argument that qualified before still qualifies. Adding an entry narrows it and breaks callers whose type argument lacks the new capability, which is why that direction needs a version and a migration note.

saying these in an interview costs you the question

  • Treats any added requirement as a harmless refinement of the signature
  • Thinks narrowing a published requirement list keeps existing callers compiling
  • Names a combined concept for a trio that appears in one routine
  • Never considers taking the capability as a parameter instead
  • Counts entries in the list rather than the branches that use them
  • Ignores that candidate types owned elsewhere cannot be redeclared