A recursive count of comments in a thread returns 0 for every thread — what base-case mistake explains it?
answer
- it returns, so termination is not the bug
- what should a childless comment count as
- read the function's contract back
- who is supposed to count the node itself
- the guard may not be needed at all
basics
~20 sThe base case returns 0 for a childless comment, so no call ever counts the node it was handed. A childless comment is one comment — the base must return 1, matching the contract the recursive branch relies on.
solid answer
~50 sThis is not a termination bug — the function returns, promptly, with the wrong number. The stated contract is "count this comment and everything beneath it", and the base case answers 0 for a childless comment, which breaks that contract at the smallest input. Since every descent bottoms out in leaves contributing 0 and no branch ever adds the node itself, the total collapses to 0 for every thread. Fixing only the base to return 1 leaves it still wrong: an internal node would sum its children without counting itself. Both branches have to honour the same contract. Cleanest is to drop the special case entirely — set `total = 1`, loop over the children, return the total — because a loop over an empty child list already does the right thing, so the degenerate case falls out of the general case instead of being encoded twice.
code
pseudocode · 7 linesCOUNT-THREAD(node): // comments in this subtree, node included
if is-empty(children(node)):
return 0
total = 0
for each c in children(node):
total = total + COUNT-THREAD(c)
return totalgo deeper
Know that a base case has to return the right value, not just stop the recursion. Practise saying what a smallest input should answer before you look at what the code does.
Explain the contract framing: the base case is the same promise at the smallest input, and both branches must agree. Be able to show why repairing only the base still leaves internal nodes undercounted.
Bring the review technique. Substitute the smallest input into the contract, check both branches agree, and argue for deleting a special case that the general case already handles rather than keeping two copies of the rule.
Make it a convention. Recursions whose base duplicates the general case are where the two drift after later edits, so a house style of basing on the truly degenerate input keeps a whole class of silent miscounts out of the codebase.
## Termination and correctness are separate obligations The first thing to say out loud is what the symptom rules out. The function returns, so nothing is wrong with termination: there is a base case, the subtree shrinks on every descent, and the recursion unwinds. What is wrong is the *value* the base case returns. Reviewers who have been trained to check that a base case exists often stop the moment they see one, and this is the family of bug that slips past them. ## The base case is the smallest instance of the same promise A recursive function has a contract — here, "return the number of comments in this subtree, including the node I was given". The recursive branch is written against that contract: it trusts each child call to return the child's whole subtree count. The base case must satisfy **the same** contract at its smallest input. A comment with no replies is a subtree of exactly one comment, so the correct answer at the base is 1, not 0. Returning 0 says "this subtree is empty", which is a different sentence about a different structure. Because every leaf contributes 0 and no internal node adds itself, the error does not merely accumulate — it annihilates. The total is 0 for every input, including a thread with a thousand replies. A bug that produces a plausible-but-low number is arguably worse; this one at least announces itself immediately. ## Half-fixes Changing the base to return 1 and stopping there leaves the function wrong in a quieter way. Consider a root with two childless replies: the leaves now return 1 each, the root sums them to 2, and the true answer is 3. The root never counted itself. Both branches encode the contract, so both branches have to be repaired. This is exactly why the interesting review question is not "is there a base case" but "do the base case and the recursive case say the same thing about the same contract". ## Prefer the degenerate base to the one-step-early base The deeper design point is that the `is-empty(children(node))` guard is unnecessary. Write the general case as "one for this node, plus the sum over children", and a node with no children falls straight out of it: the loop body never executes, and the function returns 1. The contract then lives in exactly one place. A special case that duplicates what the general case already handles is a standing invitation for the two to drift apart — someone later changes the counting rule in the loop and forgets the guard above it. The rule of thumb is to base on the truly degenerate input (nothing left to process) rather than on the input that is one step away from degenerate (no children, one element left, one node remaining), and to add an earlier base case only when the general case genuinely cannot express it. There is one case that still needs its own answer: an entirely absent thread, where there is no root node at all. That is a different input shape — an empty structure rather than a smallest non-empty one — and it needs an explicit answer of 0 at whatever boundary can produce it. ## How to check a base case's value in review A mechanical check that costs seconds: take the function's stated contract, substitute the smallest input, and read off what the contract demands. Then look at what the base case returns. If they differ, the recursion is wrong no matter how elegantly the general case is written. Run the same substitution on the general case with a one-level input and check that the two agree. Most base-case value bugs — returning 0 where 1 is meant, returning an empty accumulator where the identity element is meant, returning the item where a collection containing the item is meant — surface immediately under that test.
- How would you rewrite it so the childless case needs no branch of its own?Start the total at 1 for the node itself, loop over the children adding each subtree count, and return the total. A loop over an empty child list runs zero times, so a childless comment returns 1 through the general case. The contract then lives in one place instead of two, which removes the chance of the branches drifting apart later.
- What is the mechanical check for whether a base case returns the right value?Substitute the smallest input into the function's stated contract, read off what the contract demands, and compare with what the base case actually returns. Then do the same for a one-level input through the general case and confirm the two agree. Termination and correctness are separate obligations, and this check tests the second one.
- Would fixing only the base case to return 1 make the function correct?No. A node with two childless replies would then sum 1 and 1 and return 2, while the true answer is 3, because no internal node counts itself. Both branches encode the same contract, so both have to be repaired — which is a good argument for having only one branch express it.
A base case is the smallest instance of the same promise, not a different promise. If the function promises "this comment and everything under it", a comment with no replies must answer one, not zero.
saying these in an interview costs you the question
- Calls it a termination bug when the function returns normally
- Assumes a base case only needs to stop the recursion
- Fixes the base case and leaves the recursive branch inconsistent
- Adds a guard for the empty child list the general case already handles
- Cannot state what the function promises to return