The debugger I actually use
After fifteen years and a dozen languages, I keep coming back to four primitives. Here they are.
There are a hundred articles about debuggers. Most of them are wrong about how I actually debug. Not because the authors are wrong — because they're describing the tools, and I'm telling you about the moves.
After fifteen years, four moves. That's the post.
1. Print, but better
Yes, console.log. No, not the way you're doing it.
function processOrder(order: Order) {
const tax = computeTax(order);
const shipping = computeShipping(order);
const total = order.subtotal + tax + shipping;
return { ...order, tax, shipping, total };
}
When this is wrong, the temptation is to log total. Don't. Log the inputs at the boundary, log the outputs as you go, and label them so you can grep:
function processOrder(order: Order) {
console.log("[order:in]", { id: order.id, subtotal: order.subtotal });
const tax = computeTax(order);
console.log("[order:tax]", tax);
const shipping = computeShipping(order);
console.log("[order:ship]", shipping);
const total = order.subtotal + tax + shipping;
console.log("[order:out]", { id: order.id, total });
return { ...order, tax, shipping, total };
}
Now your terminal reads like a story. When the story doesn't make sense, the bug is on the line that doesn't make sense.
2. Bisect by deletion
If a function is wrong and you can't figure out why, delete half of it and replace the deleted half with a constant. Run the test. If it's still wrong, delete the other half. If it's now right, the bug is in the half you replaced.
This is a binary search through code. It costs five minutes and finds bugs that staring at the screen for an hour won't.
3. The reproduction comes first
If you don't have a way to make the bug happen on demand, you don't have a bug. You have a story about a bug. Stories about bugs are how bugs survive sprint after sprint. The reproduction is what kills them.
Write the failing test before you fix anything. If you can't write the failing test, write the smallest script that demonstrates the failure. If you can't do that, the bug is bigger than the bug — there's a part of the system you don't understand.
“Everyone knows that debugging is twice as hard as writing a program in the first place. So if you're as clever as you can be when you write it, how will you ever debug it?
”
4. Step away
The fourth move is the hardest one. When you've been at it for an hour and nothing's working, stop. Go for a walk. Do the dishes. Don't think about it.
I have lost count of the bugs I've fixed in the shower. The brain is a strange machine. It doesn't always work when you're staring at it.
That's the toolkit
Print better, bisect by deletion, reproduce first, step away. Four moves. They have served me through Python, TypeScript, Go, Rust, and a handful of languages I'd rather not list. The tooling changes every five years. The moves don't.
Keep reading
3 postsA walk through pattern
A small photo essay on the textures and rhythms hiding in plain sight along a city walk.
Welcome to Sirasoft — a quiet place to write about software
What this blog is, who it's for, and how a single MDX file becomes a published essay.
Designing for the second glance
Most of UX is the first impression. The work that lasts is what people notice on the second visit.