skip to content

How would you decide how lenient a service's parameter coercion should be, across trimming, case-insensitive enums and multiple date formats?

level: principalimportance: should knowfreq 34%

answer

  1. leniency is a one-way door
  2. ambiguity is worse than verbosity
  3. strict for machines, forgiving for humans
  4. canonicalise right after conversion
  5. count how often leniency fires

basics

~20 s

Decide it once per surface and write it down: leniency is contract you can add but rarely withdraw. Be forgiving where humans type the value, strict where machines call, and never accept an ambiguous form.

solid answer

~50 s

Leniency — trimming whitespace, accepting several spellings of a boolean, matching enumerated members case-insensitively, parsing more than one date layout — is genuinely useful where values are typed by people or pasted from links. The cost is that every accepted form becomes contract: callers start relying on it, so removing it breaks working requests. The rule I apply is that **ambiguity is not acceptable, while verbosity often is**: a second date layout whose meaning could be read two ways can return the wrong instant with no error, which is far worse than a rejection, while case-insensitive enumerated members cost little more than a note in the documentation. Decide per surface — strict for machine callers, forgiving for browser-facing links — implement it in **shared binder configuration** rather than per route, document the accepted forms, and instrument how often leniency fires so broken callers are visible.

go deeper

for a junior

Notice that a service decides which spellings it accepts — trimmed, upper or lower case, one date layout or several — and that stricter parsing is not the framework being unhelpful.

for a middle

Explain the tradeoff with examples, and why a rule that can return a different value is worse than one that only accepts more spellings of the same value.

for a senior

Bring the operational consequences: split aggregations and cache keys, environments that disagree, and the instrumentation needed before any rule can be withdrawn.

for a principal

Own it as policy across the estate — where strictness is mandatory, where leniency is bought deliberately, how the accepted forms are documented, and how a tightening is rolled out without breaking callers.

## Leniency is a contract decision, not a convenience Every forgiving rule in a binder widens the set of requests a service accepts. That set is contract whether or not anyone wrote it down: callers discover what works, ship it, and depend on it. Adding a rule costs one line; removing it breaks requests that have been succeeding in production, which is why leniency is effectively a one-way door and deserves a decision made deliberately rather than by whoever wrote the first converter. ## Sort the candidates by what failure looks like The useful discriminator is not how forgiving a rule is but **what happens when it guesses**. | Rule | Failure when it misreads | Verdict | |---|---|---| | Trim surrounding whitespace | rare: only where the whitespace was itself significant | usually safe, though it can mask a client that encodes badly | | Case-insensitive enumerated members | none: the member is the same | safe; document the canonical spelling | | Extra boolean spellings | a value read as false that the caller meant as true | acceptable only for a closed, documented vocabulary | | Several date or time layouts | a different instant, silently | dangerous: day and month order is genuinely ambiguous | | Numbers with grouping separators | a different magnitude, silently | dangerous for the same reason | | Ignoring unknown extra parameters | a filter the caller believed was applied | separate decision, and a common source of surprise | The line to hold is that a rule which can quietly produce **a different value** is categorically worse than one which can only produce **more accepted spellings of the same value**. A rejection is a message; a wrong value is a defect that surfaces in someone's report weeks later. ## Decide per surface Not every entry point deserves the same answer: - **Machine-to-machine APIs, internal or partner.** Strict. The caller is code, it can send a canonical form, and a rejection is a build-time-quality signal rather than a user-facing failure. - **Browser-facing query strings and links.** Somewhat forgiving. Values get hand-edited, pasted with trailing characters, and copied out of emails, and a hard failure lands on a person who cannot fix the encoding. - **Administrative and internal tooling.** Forgiving is fine; the audience is small and the blast radius is a form that is easier to use. - **Anything feeding money, dates or access decisions.** Strict, regardless of surface, because a silently reinterpreted value is unacceptable there at any level of convenience. ## Second-order costs people miss Two spellings of the same value do not stay confined to the binder: - **Aggregation splits.** Metrics, audit rows and analytic groupings keyed on the raw value now carry two keys for one thing. - **Cache keys split.** Two requests that mean the same thing miss each other's cached entries, quietly lowering hit rate. - **Deduplication and idempotency weaken.** Comparisons that decide whether two requests are the same can disagree with the binder's view of them. - **Documentation and generated clients drift.** Published schemas describe the canonical form; leniency is an undocumented superset that only exists in the running service. The mitigation is **canonicalise immediately after conversion** — one normalised value flows onward, whatever spelling arrived — so leniency stays a property of the edge rather than leaking into storage and metrics. ## Make it policy, not a per-route accident 1. Write the accepted forms down per type — one document, one list — and treat additions as contract changes. 2. Implement them in **shared binder configuration** applied across services, not in individual converters where they drift apart. 3. Keep the rules **identical across environments**; a test environment more forgiving than production ships bugs to callers, and the reverse blocks releases for no reason. 4. **Count it.** Emit a metric when a lenient path fires — a trimmed value, a non-canonical spelling — with the route and the caller. That turns leniency from a permanent blindfold into a list of clients to fix. 5. Pair any planned tightening with that metric: announce, watch the count fall, then remove. ## Where I end up Default to strict, and buy leniency deliberately in the places where a human is typing. Never accept a form whose interpretation is ambiguous, at any level of seniority of the argument for it. And treat every forgiving rule as something the service now owes its callers until it can prove, with numbers, that nobody is using it.

  • Why are multiple accepted date layouts singled out as dangerous?
    Because two layouts can match the same text and mean different days, so the binder chooses one and the request succeeds with the wrong instant. No error is raised, no caller is told, and the damage appears later in a report or a billing period. A rejected request would have been strictly better.
  • What does canonicalising immediately after conversion buy you?
    It confines leniency to the edge. One normalised value flows into logs, metrics, cache keys and storage regardless of the spelling that arrived, so aggregations do not split and comparisons stay meaningful. Without it, every accepted variant propagates through the system and becomes far harder to retire.
  • How would you actually remove a lenient rule that has already shipped?
    Instrument it first: count how often the non-canonical form arrives, broken down by caller and route. Announce the change with a date, work the list down with the callers still on it, keep the metric visible, and only then tighten. Removing it blind is indistinguishable from an outage for whoever was relying on it.

saying these in an interview costs you the question

  • Treating leniency as free because it only adds accepted inputs
  • Accepting ambiguous date layouts and calling it caller convenience
  • Letting each converter decide its own strictness independently
  • Running a test environment more forgiving than production
  • Removing an accepted form with no measurement of who still sends it