skip to content

How can two changes merge with no conflict and still leave the system broken?

level: seniorimportance: should knowfreq 45%

answer

  1. Clean merge is a claim about text only
  2. Two changes, different places, one contradiction
  3. A contract changes; a dependent change ships too
  4. No algorithm can decide program meaning
  5. Test the merged result, not each side

basics

~20 s

Textual merging only guarantees that no two changes touched the same region. Changes in different places can still contradict each other: one side alters what a value means, the other adds a caller assuming the old meaning. Clean merge, broken result.

solid answer

~40 s

This is a **semantic conflict**: two changes that are textually independent and logically incompatible. One side renames a concept while the other adds a use of the old name; one side changes a unit or a default while the other writes code depending on the old one; one side removes a caller that the other side's new invariant relied on. Nothing overlaps, so nothing is reported. No merge algorithm can close this gap, because deciding it means understanding what the merged program means, not what its text looks like. What catches it is building and exercising the **merged** result — each side passing its own tests says nothing about the combination — plus integrating often enough that the two changes are small and recent when they meet.

go deeper

for a junior

Learn the core fact: a clean merge only means no two changes touched the same region. It does not mean the result builds or behaves. When you merge, run the code afterwards rather than trusting the absence of a reported conflict.

for a middle

Be able to invent an example on the spot — a renamed concept plus a new caller, or a changed unit plus a caller using the old one — and explain why the two never overlap textually and therefore never surface as a question.

for a senior

Interviewers expect the diagnosis and the countermeasure. Say why text-level analysis cannot decide program meaning, then name what does work: verifying the merged content itself, integrating often, and announcing contract changes to the people about to depend on them.

for a principal

Own the systemic answer: make verification of merged content automatic and unskippable, keep divergence windows short, and be explicit that organising work to avoid textual conflicts does not touch this class and may hide it.

## What a clean merge actually guarantees A clean merge guarantees exactly one thing: no region of any file was changed differently on both sides. That is a statement about text. It is not a statement about behaviour, about types, about invariants, or about whether the result even builds. A **semantic conflict** is the gap between those two things — two changes that are textually independent and logically incompatible. Because they never touch the same region, there is nothing for the merge to report, and the first sign of trouble arrives later: a failed build, a failed test, or, worst of all, output that is quietly wrong. ## The shapes it takes | One side does this | The other side does this | Why it merges clean | |---|---|---| | renames a concept everywhere it exists | adds a new use of the old name | the new use is in a file the rename never saw | | changes the unit or scale of a value | writes a caller passing the old unit | different files, no overlapping region | | tightens an invariant on entry to a routine | adds a path that enters without satisfying it | the new path is new text, not changed text | | deletes a routine believed unused | adds the first real caller of it | one deletion, one addition, no shared region | | changes a shared configuration default | adds code relying on the previous default | configuration and code live apart | The common structure is worth naming out loud in an interview: **a semantic conflict is a contradiction between a change to a contract and a change that depends on that contract, located in different places.** Splitting work by file or by module makes textual conflicts rarer and does nothing at all to this class — arguably it makes it more likely, because the two authors never see each other's text. ## Why no merge algorithm can catch it To report this, the algorithm would have to know what the merged program *means*: which values flow where, what a unit is, which invariants hold. That is program analysis, not text reconciliation, and it is undecidable in the general case for anything expressive enough to be useful. A merge implementation deliberately stays in the text world, where its guarantees are exact and cheap. Expecting a better merge tool to solve this is the most common wrong answer to this question. Type checking narrows one slice of it — a renamed symbol with a stale caller usually fails to build — which is precisely why the failures that survive are the ones where both versions are perfectly well-formed and only the meaning changed. ## What actually catches it 1. **Build and test the merged content itself.** Neither side's own passing run says anything about the combination; the merged result is code that has never been executed by anyone. 2. **Run that check automatically, on the merged content, before it reaches the shared line.** A pipeline that tests each side in isolation and then records the merge is testing two things nobody is going to ship. 3. **Integrate frequently in both directions.** The window in which contradictory changes can be made is exactly the time the two lines are apart; halving that time halves the exposure. 4. **Say contract changes out loud.** A message announcing that a meaning, unit or default has changed reaches the person about to depend on the old one; a merge algorithm never will. 5. **Review the merged result, not just the two diffs.** Both diffs can be individually impeccable. ## A worked case A university admissions system ranks applicants from a scoring routine. A second team, working in another timezone, changes that routine to emit a normalised score between 0 and 1 instead of raw points, and updates all 14 call sites they can see. Overnight, the home team adds a new eligibility path that reads the score and compares it against a raw threshold of 62 — code written against the meaning that held when their work began. The two changes touch 31 files between them and share none. The merge is clean, both sides' own test runs were green, and the merged build compiles: the score is a number either way. Nothing fails until 4,180 applicants are ranked with a threshold that now excludes essentially everyone, and the defect is found by a person, not a tool. What would have caught it: a run of the full test suite against the merged content, where any test asserting an eligibility outcome fails immediately; or a short message from the team changing the unit, timed so the other timezone reads it before writing the new path. What would not have caught it: a better merge algorithm, a more careful reading of either diff alone, or a rule about who edits which file.

  • Both sides ran their full test suites and passed before merging. What does that guarantee about the merged result?
    Nothing. Each run exercised a version that will not be shipped. The merged content is a combination no one has executed, and the contradiction lives precisely in the interaction between the two changes, so it can only appear once both are present. The check has to run against the merged content itself.
  • Who is accountable for catching a semantic conflict before it reaches the shared line?
    Whoever performs the merge owns verifying the merged result, and the team owns making that verification automatic rather than a matter of diligence. Treating it as the reviewer's job fails, because both diffs can be individually correct; treating it as the tool's job fails, because no text-level tool can decide it.
  • Does splitting work so that two teams never edit the same files remove this risk?
    No, and it can increase it. File separation removes textual conflicts, which are the cheap ones that announce themselves. Semantic conflicts live between a contract and its dependents, which are usually in different files by design, so separation removes the warnings without removing the contradiction.

Two carpenters work on the same house without ever touching the same board: one narrows the door frame, the other builds a wider door. Nothing overlaps, both jobs are correct alone, and the door does not fit.

saying these in an interview costs you the question

  • Believes a clean merge means a correct merge
  • Thinks a smarter merge algorithm would catch it
  • Tests each side but never the merged content
  • Blames the version-control system for the breakage
  • Assumes reviewing both diffs separately is enough
  • Says file-level ownership removes the risk