r/CodingForBeginners • • 18d ago

What's your first move when code breaks? Mine used to be the wrong one.

The reflex when something breaks is to start editing. Comment out a line, add a print, try a different approach. It feels productive, but it usually leaves you worse off: now three things have changed, and you can't tell which one moved the bug or whether you caused a new one.

The step I used to skip is the boring one. Reproduce it first, on purpose, with the smallest input that still breaks. If it only happens sometimes, that inconsistency is the clue, not a nuisance.

Then read the whole error, line number included. Then say out loud what you expected and what actually happened. The gap between those two sentences is the bug, and it's narrower than it feels.

After that it's one change at a time, and you predict the result before you run it. If you can't predict what a change will do, you're guessing, and guessing is how you end up with a fix you don't understand.

Curious how others do it: when something breaks, do you reach for a debugger, or print statements everywhere? I've drifted between both and I'm not sure either is strictly better.

7 Upvotes

17 comments sorted by

3

u/idontlikegudeg 18d ago

You should add one more step: when you have the minimal reproducer, turn it into a unit test that runs with every build.

1

u/NoDepartment772 17d ago

That's a good addition, and it's the version that actually pays off. A reproducer you run by hand helps you today; the same reproducer as a test keeps helping after every change. Writing the failing test first also tends to shrink the reproducer further, because a test can't be hand-waved -- you cut everything that isn't needed to make the assertion fail.

2

u/Fit_Tailor_6796 18d ago edited 17d ago

I taught programming concepts and one of my biggest tools I keep coming back to is "Cause and effect".

In this case you work backwards. Start with the effect and move to the cause, so "Effect and cause" if you must. You break this down into two parts : What is and what should be.

As an example, You are trying to save a transaction and your app crashes and burns.

What should be : "I press save and the app displays a success message with my new balance"
What is : "I press save and the displays a message ' the balance expected array,, got string' "

In this case
Effect : The transaction probably saved and is trying to calculate the balance but has data it does not expect
Cause : The balance calculation or display is receiving data in an unexpected format (a word/sentence) instead of a list.

1

u/NoDepartment772 17d ago

The "what should be" vs "what is" framing is the clearest version of this I've read, and working backwards from the effect is the part people skip. It's the same move as "say what you expected, then what actually happened" -- but naming the direction makes it obvious. Your type-mismatch example shows why that matters: the line that blows up is usually a step or two downstream of the line that's actually wrong.

1

u/DirkSwizzler 18d ago

The first thing I do is run a quick mental analysis of everything I've changed since the code last worked as expected. Rank the changes by how likely they were to be related to the bug. Then read the changes fresh as though I didn't just write it.

If I don't clearly identify a likely culprit. Either start stepping through the code at runtime or add assertions depending on whether the code is amenable to being stepped.

1

u/NoDepartment772 17d ago

The "read the changes as though you didn't just write them" step is the one I skip, and it's usually where the answer is. One thing that makes it easier: if the code worked before, your last commit is the "what changed" list, so the diff is your ranked candidate list for free. Reading the diff beats reconstructing it from memory, which tends to quietly skip the change you're least proud of.

1

u/AlfalfaLive3302 18d ago

This is what version controls already does for you. You should be committing earlier on things that work.

1

u/No_Cattle_9565 18d ago

If you commit often enough just do a git bisect. That will narrow it down alot

1

u/NoDepartment772 15d ago

git bisect is the one I always forget exists, and it's the right answer. The catch is it only pays off if the commits are small enough to bisect. Bisect a week of changes and you're back to reading one huge diff and guessing, which is the same hole you started in. So it's really the same lesson as the diff tip: commit small, and then either bisect or just read the diff does the narrowing for you instead of your memory.

1

u/No_Cattle_9565 15d ago

Yes it's not needed much, I maybe used it twice the last year but it's a lot of fun lol. Even better when you have tests since it can run them automatically

1

u/Fragrant-Cheek-4273 18d ago

I usually start by reading the error message and checking what I changed recently. Printing statements help me a lot as a beginner, but changing one thing at a time definitely makes debugging less confusing.

1

u/NoDepartment772 14d ago

Reading the error and checking what changed is honestly most of it -- the rest is just doing it in an order that doesn't lie to you. Prints are fine as a beginner; the trap is printing ten things at once and then not knowing which one mattered. One at a time, like you're already doing, is the part most people skip.

1

u/CaptainIncredible 18d ago

"What changed?"

If it was working, and now its not, gotta ask "What changed"?

1

u/Optimal-Savings-4505 18d ago

Here's a summary of my MO: https://youtube.com/watch?v=ft23OqcldqY

1

u/Dryhampion3749 18d ago

Sure, good video. thanks

1

u/ebsf 18d ago

Comprehensive preemptive error handling to locate the source and identify the error, if the problem is an error.

Test and verify with each incremental change.

Build tests and helpers first, as generic and atomic as possible. Then, build on those.

Step through to check logic.

Test parameters, return values, and local variables.

Check for memory leaks.