I hit a really phenomenal personal milestone this week, which is that I was elevated from regular old Software Engineer to being the Dev Lead for my team. It feels like time has flown, and it also feels really impossible that me (who couldn’t keep straight the difference between arguments and params, who spent two years straight forgetting how to link a stylesheet to an html page) could have come so far.
But I did!
I don’t think I have any particular talent as a developer, but the one thing I do feel like I’ve gotten quite good at over time is debugging.
I’ll share my starting point with you just so you can see how non-fluid I was a debugger when I first started coding. I had major issues with my mind going completely blank if the task was full of unknowns, sometimes I’d get partway through a solution and wind up stuck on some minor item of syntax so long that my brain would melt and the code I just wrote wouldn’t even make sense to me.
Often I would get doggedly 1/3 of the way to a solution and hit some minor issue, then go on an overly complicated side quest to overcome it and wind up introducing new bugs on top of whatever I was supposed to be fixing.
In the time between then and now, a mix of techniques (some for my mood/outlook, some for practical debugging) have taken me from someone who agonized over bugs to someone who kind of enjoys them, the weirder the better. It doesn’t matter if they are related to a language I know well, or one i’m new to – they are universal techniques.
It doesn’t matter how you do it, although there are a lot fancier techniques than just console.logging your way out of a hole. That’s another post for another time although there’s a great write up on some of these techniques here.
As a junior engineer, don’t think about anything else. Your first task is to go to the affected area of the program, and get it talking to you – period. If you can’t tell what area of the program is affected, guess.
This might sound simple, but it isn’t always. Sometimes you can do this in the browser. Sometimes you might need to generate a log. Sometimes you may need to generate output data from a SQL query or whatever the case may be. But getting the program to talk to you is your first step, always.
Some non zero number of bugs are eliminated at this step. Sometimes you find out you can’t get the program talking to you, and that’s because part of it wasn’t running. A file got axed somehow, a function wasn’t being called, a nightly script errored out. For those reasons and others this is a crucial step.
2. Take notes on the exact nature of the program, and take notes on the changes you are making as you try to debug
This is good for you on a couple levels. If you wind up coding yourself into a corner and getting into the brain melt stage (ie, “I’ve looked at this code so long I no longer understand any of it or remember what I did to it, my brain is fried”) it will remind you what you’ve done. Its your bread crumbs, a la Hansel & Gretel.
If you get really stuck and need to involve a more senior person for advice, it will also be invaluable to letting them help you.
I didn’t do this step at first as a junior developer. It felt like cheating to just back out of changes and see if that fixed a problem without having a full understanding of the cause.
Yes and no.
Ideally, you would learn the root cause, but sometimes it is beyond the scope of your understanding as a junior person. If the chances are understanding everything may not be possible, I feel in hindsight like my next priority should have been making progress as a good use of the funds of the company paying my salary. For that reason, I wind up thinking its perfectly ok to back out of a change and see if that fixes your issue. Git and branching are the best mechanism I’ve found for that stage of debugging – git diffs are also great for this.
If you are gung-ho like me, take it to a more senior person afterwards. Say, “I’ve resolved the issue, but I’m not quite sure why it had this effect…” and they’ll probably be able to give you a TL:DR on the root cause. No shame in that game. I’d rather be a person who gets things done than a person on top of all the minutia.
There’s a reason rubber ducking works. Instead of being head down in code, using human terms to describe an issue makes you describe a narrative, and sometimes that makes you have an aha moment about the problem type.
Have you ever come up with user stories? Non technical descriptions of what an app can do? This is a similar idea. Come up with bug stories.
I can’t tell you how many times I’ve started telling someone “About every forth time when a user does we see…” only to append my story by saying “Actually, saying it out loud that sounds like a race condition.” Or a data anomaly. Or . Most of the time, that gut feeling is right.
Most of us will have heard of binary searches, a highly efficient way of parsing through data that always reminded me of gambling. You take a gamble that one half of the data will take you where you need to go, and keep cutting it in half, then in half, then in half again.
I subconsciously began binary-search-debugging by just commenting out random hunks, as big as possible while making my bug behavior reproducible still, to save myself time in finding issues. Again, it feels like a cheat – there’s no skill involved just simple guesswork.
It turns out binary search debugging is actually completely valid, and a real thing, and my guess of what the name is IS the actual official name! How funny.
As you maybe can guess, a binary search would have potential to be way faster than an iterative search, and the same holds true for debugging.
Iterative debugging would be commenting out lines one by one, or conversely commenting everything out then uncommenting lines one by one (thats my go to).
Now for some more philosophical advice that is more related to outlook/mood/not feeling like a hot mess as a junior tasked with debugging.
Finding where a bug is is probably 75% of the effort for me in finding out WHAT it is. Thinking back on my early debugging, figuring out where the real cause of an issue was wound up being my major time suck. Simplifying my task into “right now, I’m just looking for where this behavior lives” also had a somewhat soothing effect on me as opposed to thinking “oh my god I have to find the bug AND find out how to fix it AND (…)”
8. Don’t bother developing an expectation on how hard a bug is, you may well be wrong and build yourself up for nothing
When I moved into development after having a former career that was unrelated, I was shocked at how bad I was at producing time estimates. It wasn’t my fault, I think its pretty common for a junior developer to not be equipped to estimate time well.
The truth is, maybe half the time or more your ideas about whether a bug is simple or complicated are completely wrong, so there is no point making an assessment and getting yourself psyched up for what may be nothing.
Around the time I started to not have major freakouts and imposter syndrome attacks when debugging, I wound up being able to channel this kind of zen mindset that was a night and day switch for me. I think the two biggest breakthroughs were “I’m just going to find where this bug is and not worry about anything else yet” (covered in 7) and the second was, “I’m going to find one thing I think this program is doing then prove it.”
Snowballing is a concept in getting out of debt, where you pick the most approachable item first to make something emotionally fraught more manageable.
In code, that would look like “I think the first half of this function is working, but the second half isn’t.”
Ok, prove the first thing. Then prove the next thing you think is happening. Sometimes your assumptions are wrong. Sometimes they are right. Either way, breaking it down clarifies what is happening
I think to a tee, every time as a junior dev I wound up doing something convoluted to fix a bug, I was using the wrong approach. Sometimes, there was a simple library or function I'd never heard of that could fix my issue. Sometimes, I was operating with wrong assumptions, and that's why my "fix" got so out of hand.
I used to think that being a good developer meant I could write complicated code. I now know that being a good developer means I rarely have to.
Gut check yourself: if it feels too complicated, it probably is.
I recently was sitting with a relatively senior developer who was new to my org, and he lamented that a card he was assigned in our sprint had kind of fizzled with no real progress, because he hadn’t wanted to bother anyone.
I told him something similar to what I’ll say now. Developers are in general well paid. If you as a developer are stuck, its a bad use of company money. You are too valuable to be stuck more than, I don’t know, an hour and a half?
A classic new person pitfall though is that you can’t really describe what’s happening since you don’t understand it, so the more senior person you finally call in to triage has to do a lot of, idk, emotional labor to even get to the heart of your issue.
You, savvy early career dev, will be different though. Here’s how.
You already will have your notes on the steps you’ve taken (from number 2), and one of them may immediately tell your senior dev what they need to know. You will ALSO come correct, by making a minimum repeatable example of your bug, preferably in a code pen or some other isolated environment where your senior person could play with it without having to walk over to look at your machine. (Sometimes moving the code into isolation, you will also solve your own issue.)
If your code is protected by NDA and can’t be moved into a code pen, push it up in a branch so your senior can grab it at their leisure.
You will pass along what you tried, any stack overflow answers that had solutions you were considering, and descriptive information about the behavior. (Side note: can’t tell you how many times as a junior dev I found the right answer on Stack Overflow but just implemented it slightly wrong. That type of information still helps! Pass it on.)
Is the behavior intermittent? Does it specifically follow other events? Is there any pattern or seeming anti-pattern to who it affects or when it occurs?
All this should be wrapped up in a bow for hand off. There is no shame getting stuck on a bug. But you want to help people help you.
I was really tough on myself as a junior dev, and the pressure I felt around debugging and how fast or independently I should be able to do things was one of the biggest areas of turmoil for me.
I hope I can maybe save someone else out there those feelings of anxiety and frustration.