Why do small, individually buildable commits make git bisect far more useful?
answer
- the answer is one commit
- resolution equals commit size
- each candidate must build
- skips break the clean halving
- squashed weeks tell you little
basics
~20 sBisect's answer resolution is one commit. Small commits mean the first bad commit is a diff you can read; a huge squashed commit only tells you the bug is somewhere in a week of work, and unbuildable commits force skips.
solid answer
~40 s`git bisect` narrows history down to a single commit — no finer. So the value of the answer equals the size of that commit. If commits are focused, the reported first bad commit is a small readable diff and diagnosis is nearly done. If a week of work was collapsed into one commit, bisect still succeeds but hands you thousands of changed lines and you start debugging from scratch. Buildability matters just as much: every commit bisect lands on must compile and be testable, otherwise you spend steps on `git bisect skip` and can end with a set of possible culprits instead of one. Merge-heavy history has the same problem, since commits inside a topic branch were often never expected to build alone.
go deeper
Be able to say that bisect narrows down to one commit, so the smaller and more focused each commit is, the more useful its answer will be.
Explain both requirements: small commits give a readable boundary diff, and individually buildable commits keep the search halving cleanly instead of burning steps on skips.
Connect hygiene to incident response — argue for atomic, green commits and a fast reproducer because they are what make automated bisect a routine first step rather than a last resort.
Own the tradeoff: per-commit buildability costs CI capacity and review discipline, and you should justify it by the diagnosis time it buys, including when a coarser first-parent search is good enough.
## Resolution is one commit Bisect performs a binary search over the commit graph and stops at a boundary: the first commit, in the searched range, where the symptom is present. That is the finest answer it can give. How useful the answer is therefore comes down entirely to what one commit contains in your repository. If your commits are small and single-purpose, `<sha> is the first bad commit` is close to a complete diagnosis: a diff of a few dozen lines with a message explaining intent. If your history consists of one commit per feature branch containing a week of work across forty files, bisect has done its job perfectly and told you almost nothing you did not already know. The search cost is logarithmic; the payoff is proportional to commit size. ## Every candidate must be judgeable The second requirement is that an arbitrary commit in the range can be built and tested. Bisect does not walk your history in the order you wrote it — it jumps to whichever commit sits near the middle of the remaining candidate set. That commit might be a mid-refactor state, a commit that renames a file before the callers are updated, or a work-in-progress step inside a topic branch. Every such commit costs you: you answer `git bisect skip` (or, in `git bisect run`, the script exits `125`), Git picks a neighbour instead of halving cleanly, and the search takes more steps. If enough commits are unjudgeable, the search can end without a single answer, reporting a set of commits the first bad one could be. A history where any commit builds is what keeps bisect logarithmic and unambiguous. ## Automation raises the bar further `git bisect run` only pays off when one command can decide the question at any commit. That requires two things from history: the build works at each candidate, and the test that reproduces the symptom exists — or can be supplied from outside the repository — across the range. Teams that keep commits atomic and green get unattended searches; teams that do not end up hand-testing every step. ## The merge-topology angle A history full of merged branches contains many commits that were only ever meant to be valid as part of a completed branch. Recent Git offers `git bisect start --first-parent`, which follows only first parents so the search stays on the mainline and names the merge that brought the regression in. That is a coarser answer — a whole branch rather than a commit — but a reliable one, and it is the natural fallback when intermediate commits are not individually buildable. ## The practical hygiene rules What this implies for day-to-day work: - Keep each commit to one logical change, so the boundary commit is readable. - Keep each commit buildable and testable on its own, so no step is wasted on a skip. - Write messages that say why, so the reported commit explains itself without archaeology. - Keep unrelated formatting or bulk renames in separate commits, so the guilty diff is not buried in noise. ## Limiting the search another way When commits are large for historical reasons you can still narrow the field: `git bisect start -- <path>` restricts candidates to commits touching the given paths, so the search visits fewer, more relevant commits. It reduces steps, but it cannot make an individual commit smaller — only commit hygiene does that. ## Why interviewers ask it The question connects two things candidates often treat separately: how they write commits, and how fast their team diagnoses regressions. Answering it well means saying explicitly that commit granularity is the resolution limit of your debugging tools, not a matter of taste.
- If history is already full of large commits, what can you still do to make a bisect useful?Limit the candidate set with git bisect start -- <path> so only commits touching the relevant files are tested, and choose the narrowest reproducer available. Once bisect names the large commit, fall back to reading its diff file by file rather than expecting Git to localise the change further.
- Does bisect work on a merge-heavy history?Yes, but it may land on commits inside topic branches that were never expected to build, costing skips. Recent Git's git bisect start --first-parent stays on the mainline and identifies the merge that introduced the regression instead, which is coarser but reliable when intermediate commits are not individually valid.
saying these in an interview costs you the question
- Says bisect can pinpoint the guilty line, not just a commit
- Treats commit size as pure style with no debugging cost
- Assumes every commit in history builds without checking
- Thinks squashing everything makes history easier to bisect
- Ignores that skips lengthen and blur the search