skip to content

An ambiguous condition grammar leaves two evaluators disagreeing about thousands of saved rules — how do you get to one meaning?

level: principalimportance: should knowfreq 33%

answer

  1. one meaning, chosen on purpose
  2. the defect is the specification
  3. measure how many rules actually differ
  4. canonicalise with owner sign-off
  5. version the language, then migrate

basics

~20 s

Choose the single intended reading deliberately, then make it unforgeable: pin it in the grammar, re-parse every stored rule to find the subset whose two trees differ, review that subset with its owners, and version the language rather than rewriting meanings silently.

solid answer

~50 s

Treat it as an unowned specification gap, not a bug in one evaluator — both are faithful to a grammar that never chose. The work has three parts. **Decide the meaning**: pick the reading users evidently expect, usually the conventional one, and write it into the grammar by layering the rules, not into a settings file. **Find the blast radius**: parse every stored rule under both readings and compare the trees; typically only a small fraction differ, and that subset is the only material needing human review. **Land it without silent reinterpretation**: either canonicalise the affected rules into an explicitly bracketed form with owner sign-off, or version the rule language and migrate, so nobody's rule quietly changes meaning on deploy. Then close the door — one parser shipped as a shared component plus a conformance corpus, so the next reimplementation cannot re-open the choice.

go deeper

for a junior

Take away the principle: when two components disagree about the same text, ask what the specification says before assuming one of them is broken.

for a middle

Be able to explain why the grammar, not the parser, is the thing to change, and how you would compare the two readings across stored rules to size the problem.

for a senior

Own the migration: measure the affected subset, get owner sign-off on reinterpretations, canonicalise into explicit form, and add the regression corpus that keeps it settled.

for a principal

Frame it as a specification-ownership question — who decides the meaning of stored text, how that decision is versioned, and what prevents a future implementation from re-opening it.

## Name the failure correctly first Neither evaluator is wrong. Both implement the published grammar; the grammar permits two trees, and each implementation picked one. Framing this as "evaluator B has a bug" sends someone to patch an implementation and leaves the actual defect — an under-specified language — in place, where the next reader of the grammar will hit it again. The unit of repair is the specification. ## Decide the meaning before touching anything Ambiguity means the grammar abdicated a choice, so someone must now make it, and "whichever is easier to implement" is the wrong basis. Useful inputs: - **What did users believe they wrote?** Sample the affected rules and read them as prose. The reading that matches ordinary expectation — conventional precedence, nearest binding — is almost always the right target, because it is what the authors meant. - **Which reading is already load-bearing?** If the request path has used one reading for two years, that reading is the de facto meaning of the stored data, whatever the batch job computes. - **Which is cheaper to express afterwards?** Whichever you do not choose must remain writable via brackets; check it actually is. ## Three fixes, and what each costs | Fix | Removes divergence for | Cost | |---|---|---| | Publish the convention; leave both parsers | Nobody, reliably — it is guidance a future implementer may not read | Cheapest, weakest; the grammar still permits both trees | | Ship one parser as a shared component both services call | Today's two implementations | Real, but the grammar stays ambiguous, so a third reader — a validator, an exporter, a UI preview — reintroduces it | | Rewrite the grammar so each rule has exactly one tree | Every present and future reader | Highest effort; must be paired with a migration story for stored text | These are layers, not alternatives: the durable answer is the grammar rewrite, with the shared parser as the mechanism that guarantees everyone runs the new grammar, and the written convention as documentation of a decision rather than a substitute for one. ## Migrating text that already exists The stored rules are the hard part, because a grammar change can silently reinterpret them. Work it in order: 1. **Parse every stored rule under both readings** and compare the resulting trees. Rules whose trees coincide are unaffected — expect most of them, since the ambiguity needs a specific shape to bite. 2. **Partition the differing set** by whether evaluation actually diverges. Two different trees can still agree in value when the operator is associative, which shrinks the review pile again. 3. **Attribute the remainder to owners** and ask which reading they meant. This is the only step that cannot be automated, and it is why step 1 matters: reviewing a hundred rules is possible, reviewing thousands is not. 4. **Rewrite the survivors into an explicitly bracketed canonical form**, so their meaning no longer depends on any convention and the change is visible in a diff. 5. **Freeze it**: the new grammar plus a corpus of rules with their expected trees, checked on every build. A silent mass rewrite that assumes one reading is the failure mode to avoid — it is indistinguishable, from an owner's point of view, from the system changing its behaviour for no reason. ## Closing the door for good - **One implementation of the parser**, consumed by everything that reads rules, so "which tree" has exactly one answer in the organisation. - **A conformance corpus** of rule text and expected trees. This is what makes a future reimplementation — in a different service, a different runtime, a different team — provably equivalent instead of hopefully equivalent. - **A version on the language**, so a future disambiguation is an explicit migration rather than a redeploy that changes what old text means. - **A guard on the grammar itself**: keep it in a form your parser construction accepts, so that a future edit reintroducing ambiguity fails the build instead of shipping. ## What an interviewer is listening for Three moves, in this order: locating the defect in the specification rather than in an implementation; sizing the blast radius with data before proposing a migration; and separating the decision (which meaning) from the mechanism (how it is enforced) and the migration (what happens to text already written). A candidate who jumps straight to "we standardise on one parser" has solved the incident and left the cause; a candidate who proposes rewriting thousands of stored rules without first measuring how many actually differ has invented a migration that did not need to exist.

  • How do you work out which stored rules are actually affected?
    Parse each rule under both readings and compare trees; where the trees match, nothing changes. Then narrow again by whether evaluation differs, since two trees can agree in value. What remains is a small reviewable set rather than the whole corpus.
  • Why is shipping one shared parser not a sufficient fix on its own?
    It makes today's readers agree while leaving the grammar under-specified. The next component that reads rules — a validator, a preview, an export — is written from the grammar and can pick the other tree, so the divergence returns. The specification has to pin the tree.
  • What do you do about rules whose owners have left and cannot be asked?
    Fall back to the reading the production path has been applying, since that is the behaviour the business has actually been getting, canonicalise them into bracketed form so the choice is recorded, and flag them so the first failure after deploy is traceable to a known list rather than a mystery.

saying these in an interview costs you the question

  • Says just document a convention and leave both parsers alone
  • Assumes every stored rule changes meaning and must be rewritten
  • Calls it a bug in one evaluator rather than an under-specified grammar
  • Proposes rewriting stored rules silently with no owner review
  • Thinks a suite of example rules substitutes for pinning the grammar