My favorite debugging technique: Binary elimination

Just a guy who loves to write code and watch anime.
Search for a command to run...

Just a guy who loves to write code and watch anime.
No comments yet. Be the first to comment.
Qualities that make outstanding builders.

Bipedal vs quadrupedal: how many legs This is about the number of legs the creature walks on. Bipedal. Two legs. Humans, ostriches, T-rex, kangaroos, most fantasy humanoids. Quadrupedal. Four legs. Do

Intro You hear "lerp" and "smoothstep" everywhere in game dev. They sound like math jargon. They're not. Both are small tools that do the same job: smoothly move from one value to another. The problem

What is Kinematics Kinematics is the math field. It's the study of how things move without worrying about forces (which would be dynamics). FK and IK are the two branches: forward kinematics and inver

Intro Textures are usually the biggest cost in a 3D scene. Memory, bandwidth, and load time all get eaten by them. Resizing them is the obvious lever. There's more. This post is about the less obvious

Binary elimination is a debugging technique where you systematically comment out portions of code to identify which section contains a bug. It's like a binary search, but for code problems.
When faced with a large codebase or complex bug, trying to reason through every line is inefficient. Binary elimination lets you quickly narrow down the problematic area without understanding all the code first.
Comment out roughly half your suspect code
Run the program to see if the error still occurs
If the error disappears, the problem is in the commented-out code
If the error remains, the problem is in the active code
Repeat the process with the problematic half, until you isolate the exact issue
Let's say you have a TypeScript function that parses CSV data through multiple steps but produces incorrect output:
function parseFinancialData(csvString: string): FinancialReport {
// Step 1: Split CSV into rows and columns
const rows = csvString.split("\n").filter((row) => row.trim().length > 0);
const headers = rows[0].split(",");
const dataRows = rows.slice(1);
// Step 2: Convert rows to objects
const transactions = dataRows.map((row) => {
const values = row.split(",");
const transaction: Record<string, any> = {};
headers.forEach((header, index) => {
transaction[header.trim()] = values[index]?.trim() || "";
});
return transaction;
});
// Step 3: Parse numeric values
const parsedTransactions = transactions.map((transaction) => ({
...transaction,
amount: parseFloat(transaction.amount),
date: new Date(transaction.date),
}));
// Step 4: Group by category
const byCategory: Record<string, any[]> = {};
parsedTransactions.forEach((transaction) => {
const category = transaction.category;
if (!byCategory[category]) {
byCategory[category] = [];
}
byCategory[category].push(transaction);
});
// Step 5: Calculate summaries
const summary = Object.entries(byCategory).map(([category, transactions]) => {
const total = transactions.reduce((sum, t) => sum + t.amount, 0);
return {
category,
count: transactions.length,
total,
average: total / transactions.length,
};
});
return { transactions: parsedTransactions, byCategory, summary };
}
When your function returns incorrect summaries, you're not sure which step has the bug.
Comment out the later steps and check intermediate results:
function parseFinancialData(csvString: string): any {
// Step 1: Split CSV into rows and columns
const rows = csvString.split("\n").filter((row) => row.trim().length > 0);
const headers = rows[0].split(",");
const dataRows = rows.slice(1);
// Step 2: Convert rows to objects
const transactions = dataRows.map((row) => {
const values = row.split(",");
const transaction: Record<string, any> = {};
headers.forEach((header, index) => {
transaction[header.trim()] = values[index]?.trim() || "";
});
return transaction;
});
/*
// Step 3, 4, and 5 commented out
*/
return { transactions }; // Return early for testing
}
After inspection, the transaction objects look correct, so the problem is in steps 3-5.
Re-enable step 3 and check again:
function parseFinancialData(csvString: string): any {
// Steps 1-2 here...
// Step 3: Parse numeric values
const parsedTransactions = transactions.map((transaction) => ({
...transaction,
amount: parseFloat(transaction.amount),
date: new Date(transaction.date),
}));
console.log("Sample parsed transaction:", parsedTransactions[0]);
/*
// Steps 4-5 still commented out
*/
return { transactions: parsedTransactions };
}
Looking at the logged transaction, you notice the amount is NaN. The bug is in step 3.
Debug step 3 in detail:
// Step 3: Parse numeric values
const parsedTransactions = transactions.map((transaction) => {
console.log("Raw amount:", transaction.amount, typeof transaction.amount);
const amount = parseFloat(transaction.amount.replace("$", ""));
console.log("Parsed amount:", amount);
return {
...transaction,
amount,
date: new Date(transaction.date),
};
});
Now you see the problem: the amount fields include dollar signs that need to be removed before parsing.
Start with large chunks before drilling down
Keep track of what you've commented out and tested
Comment out entire logical sections, not random lines
If removing any section fixes the issue, that section contains the bug
For complex interactions, you may need to restore some code to maintain basic functionality
Works when you don't fully understand the codebase
Faster than stepping through code or adding print statements everywhere
Useful when you don't know what's causing the problem