skip to content

Two engineers write different alert conditions over the same signals and claim they mean the same thing - how does a truth table settle it?

level: middleimportance: should knowfreq 45%

answer

  1. compare, do not argue
  2. same inputs, same column
  3. one differing row settles it
  4. rows double per distinct proposition
  5. the biconditional must be a tautology

basics

~20 s

Enumerate every assignment of the distinct propositions the two conditions share - 2^n rows for n propositions - and evaluate both on each. They are equivalent exactly when the two columns agree in every row; the first disagreeing row is a concrete counterexample.

solid answer

~40 s

List the **distinct atomic propositions** appearing in either condition - say `budget exhausted`, `dependency timed out`, `payload rejected` - and build one table with a row per assignment of truth values to them, `2^n` rows for `n` propositions. Evaluate both conditions on every row and compare the two result columns. If they agree everywhere, the conditions are logically equivalent and either may replace the other. If any row differs, that row *is* the answer to the argument: it names a concrete combination of signals where one alert fires and the other does not, which is far more persuasive than trading rewrites. Equivalently, the two are the same claim exactly when the biconditional joining them is a tautology - true on every row.

code

pseudocode · 7 lines
pseudocode
function first_difference(condA, condB, props):
    for each assignment in all_assignments(props):   // 2^n of them
        if evaluate(condA, assignment) != evaluate(condB, assignment):
            return assignment                        // the counterexample row
    return NONE                                      // every row agreed

props = [budget_exhausted, dependency_timeout, payload_rejected]  // 8 rows

go deeper

for a junior

Put both conditions in one table over every combination of the shared signals. Equivalent means the two result columns match in every single row, not most of them.

for a middle

Explain why enumeration decides it: the value depends only on the atoms, and 2^n rows exhaust them. Be able to produce the differing row rather than just a verdict.

for a senior

Bring the counterexample as a scenario, and check the one-way conditionals too - the real finding is usually 'yours fires in strictly more cases', with the extra rows named.

for a principal

Watch the atoms, not the algebra. When two teams name the same signal but derive it differently, an equivalence argument answers the wrong question and the disagreement belongs upstream in the definitions.

Two conditions are **logically equivalent** when they have the same truth value under every assignment of their inputs. That is a statement about a finite table, which is why the argument can be settled rather than debated: for propositional conditions, enumeration decides it. ## The procedure 1. **Collect the distinct atomic propositions.** Not the operators and not the sub-expressions - the indivisible signals. If both conditions mention 'budget exhausted', that is one proposition appearing twice, not two. 2. **Build one table over all of them together.** With `n` distinct propositions there are `2^n` assignments: 2 propositions give 4 rows, 3 give 8, 4 give 16. Both conditions must be evaluated over the *same* rows, which is why the table is joint rather than two separate tables. 3. **Evaluate each condition per row**, using intermediate columns for sub-expressions so a mistake is visible. 4. **Compare the two result columns.** Agreement on every row means equivalence. Any disagreement means they are different conditions, and that row is the counterexample. ## Why the result is a decision and not a sample Each condition's value depends only on the truth values of its atomic propositions, so the `2^n` assignments exhaust every situation the conditions can distinguish. There is nothing outside the table to check. This is the difference between this and testing: a test suite samples inputs, whereas the table covers the whole input space of the propositional abstraction. The usual caveat is that the abstraction may be wrong rather than the table. If one condition's 'budget exhausted' is computed from a different signal than the other's, they are not the same proposition, and treating them as one column answers the wrong question. Naming the propositions carefully is where this goes wrong in practice. ## Equivalence stated as a tautology A statement that is true under every assignment is a **tautology**. Joining the two conditions with a biconditional gives a single statement, and: - the two conditions are equivalent **exactly when** that biconditional is a tautology; - the two conditions are **never** simultaneously true exactly when their conjunction is a contradiction - true under no assignment; - one condition is **stronger** than the other - it fires in a subset of the cases - exactly when the conditional from it to the other is a tautology, while the reverse conditional is not. That last one is the useful refinement in a review, because the honest outcome of the argument is often not 'equivalent' but 'yours fires in strictly more cases than mine', and the table shows exactly which extra rows those are. ## Cost, and what to do when the table is too big The row count doubles with each additional distinct proposition, so a hand table is practical to roughly four or five propositions - 16 or 32 rows - and unpleasant beyond that. Two things keep it usable: - **You rarely need the whole table.** To *refute* equivalence you only need one differing row, and a targeted guess usually finds it: set the propositions that appear in one condition and not the other, and look at what each side does. - **Group the propositions that cannot vary independently.** If two signals are mutually exclusive by construction, the rows where both are true are unreachable and can be struck, which shrinks the table and often changes the verdict. ## The output that ends the argument When equivalence fails, present the differing row as a scenario rather than as a bit vector: *'the dependency timed out, the budget was intact and the payload was valid - your condition fires here and mine does not'*. That reframing turns a disagreement about notation into a question with an operational answer: which behaviour do we actually want on that row? Frequently neither engineer intended it, and the table's real value is that it surfaced a case nobody had considered.

  • The table shows the two conditions differ on exactly two rows - what is the next question to ask?
    Which behaviour is wanted on those rows. Describe each differing row as a concrete combination of signals and ask whether the alert should fire there. Often neither author had considered the case, so the disagreement resolves into a new requirement rather than a winner between the two expressions.
  • How do you show that one condition is strictly stronger than the other rather than equivalent?
    Check the conditional in each direction. If the conditional from the first to the second is true on every row while the reverse fails on at least one, the first fires only where the second does and not conversely - it is strictly stronger, and the failing rows are exactly the extra cases the weaker one covers.
  • What breaks the method even when the table is built correctly?
    Mislabelled propositions. If the two conditions compute a similarly named signal from different sources, they are distinct propositions and must occupy separate columns; collapsing them into one asks whether the expressions match, not whether the alerts do. The table is only as sound as the atoms chosen for it.

saying these in an interview costs you the question

  • Builds a separate table per condition and compares totals
  • Counts matching rows and calls a majority equivalent
  • Treats passing tests as proof that two conditions agree
  • Forgets a proposition that appears in only one condition
  • Collapses two differently computed signals into one column