There is one final and inevitable truth in computer science: The machine does nothing but what we ask. The straight consequence is that if there is a point of failure, then it must be us, the humans.

Being honest with ourselves is indeed a great sign of maturity and humbleness. But for a programmer, such a quality is an essential need. It is the foundation of software development. Without admitting we can be wrong, we can’t debug a single line of code. Ever.

Know The Tricks

A skilled gamer has a very different style from a newbie. In the same way, a senior programmer has a different and quite an effective mindset, achieving bug-solving in little time — while on the other hand, the junior usually has no idea at all.

Experience is the key, but also humbleness and steady nerves. Even as a junior, there is a high chance to track down bugs effectively if you know the tricks.

You Are Wrong, That’s It

The first and essential step is to recognize that you have been wrong. Having the right attitude can do wonders. Before deciding where, what, and why, calm your nerves, cool down your frustration, and let it sink in: You coded it. You were wrong. The machine only executes. Bugs are right there, hidden before your eyes.

Let It Breed

If you cannot reproduce the bug, then you have no idea what to do. There is no ‘sometimes’, no room for doubt, only mathematical certainty. You must be able to reproduce the bug at your will, with 100% probability.

Beware: This does not mean you have understood the problem. The aim is to have a clear way to test the hypothesis which you will make in the following analysis.

Read The Error, Read It All

Many programmers find it boring to debug, so they skip to a solution, using trial and error and making random changes to their code. This is not debugging, it is random exploration. Shotgun debugging (the name really says it all) skips the analysis and leads only to frustration. This gives the illusion of moving faster and not wasting time since we did not read the stack trace.

But the chance of guessing correctly is really low, and the more trials, the more anger. Thanks to other developers, we are given (hopefully) a complete information stack concerning the error we stumbled on. Reading it fully gives precious hints: the line that led to error, what went wrong, and how things should have gone — plus the whole set of nesting and nested calls inside and outside the failure point.

Sometimes this is not enough since the semantics of your code might be at a much higher or lower level. Or there might be no error trace and stuff just fails silently and gracefully – like one of the worst developer’s nightmares.

Divide et Impera

As with many other things in coding, debugging is a matter of isolation. You must identify the issue to solve it. Analyse it. If you knew where the bug was, there would be no bug. Fixing is the easy part.

Isolation is critical. If you cut out everything that is not related to the bug, what remains is indeed the bug. If not, there’s a high chance you are not chasing the bug, but a shadow of it. Knowing where the problem stands is 80% of the bug-solving. The rest usually is extremely straightforward, even when the solution is complex.

Learn From Your Errors

This sounds like a cliché. But of course, knowing what happened in the past may help you in the future.

Here’s where everything you know about yourself fits in. If you are a lazy guy, maybe look at the copy-paste. If you know you don’t like a specific library, framework, or API, start from there. If you don’t like reading the documentation, then check it out properly this time. Be honest with yourself.

Curse, Laugh, Cry

You need a safety valve. It does not matter what you prefer: Find something the whole debugging team is fine with and let it pull out the frustration.

Debugging is an exhausting task, made of many failures in a row until you correctly understand what’s happening, why it’s happening, and how to make it not happen. To keep the tension low, spit it out!

Your Mind Flaws, Your Code Flaws

What if after cutting to the specific point, it still does not make any sense to you? Your mind might have missed the point, literally skipping it.

Our brain is meant to pursue an aim. It always tries to foster and consolidate hypotheses rather than recognize errors and change its position. Most of the hints that follow are weak links in our thought chain. We keep our focus on intent and neglect the structure and the syntax. Or underestimate complex parts and just assume they are fine. And we keep doing it all the time, also when we check the code without great attention. Because the brain does not want to go into details or waste energy doing the same analysis over and over.

Typos

There is a precise reason why nowadays IDEs have an integrated spellchecker. Back in the days when no help was available, the worst bugs ever were from the number 1 in place of the letter l. More recently, it still happens that a trailing s gets dropped or there are underscore, dash, or case issues.

Your brain has a clear intent in mind and an autocomplete-fast-reading plugin, so it guesses the words rather than actually reading them.

Bottom line: Use a spellchecker, meaningful variable names, and autocomplete whenever available. And pick the right IDE to have them available.

Copy-Paste

The plague. No matter if it’s from self-plagiarism, Stack Overflow, the senior’s code, the existing snippet right there, or whatever suits your taste: copy-paste always smells. Because the code does not fit the paste spot right away.

The adaptation messes everything up. Even if the chance of breaking the code up can be reduced by CPD tools and willingly applying the DRY principle, there is still some space left for copy-paste bugs. Don’t paste code if possible. If you do, double-check it before and after. And when you find a bug, check it again twice.

Last but not least: You put all of your trust in the original author of the code you copied. Make sure that they are really worthy of it.

Reference vs. Value

Sometimes it is the programming language that tricks you. It is often forgotten that there is a broad difference between a literal, a variable, and an object. Most of the time, objects are accessed and assigned via reference, and this can have funny consequences. Imagine filling a circular buffer with a buffer object. Terrible. This, moreover, depends on the language. Nasty!

Recursion and Loop

Desiring or thinking of being in Venice does not mean the same as being in Venice. Maybe you are close, and sometimes you are deceived by appearances. But when it comes to loop and recursion, always be skeptical about the indexing, the sequence, the termination clause.

There is nothing special in realizing you are one step away from the intended index or so. Or the index is completely wrong, scrolling another list. Or even looping forever or never at all.

Quasi-Boolean

If” clauses are often too simple to be investigated. But boolean conversion hides so many trickeries! Many funny things happen when you check the trueness of something which is not a boolean. Depending on the language, empty collections, negative numbers, type unsafe conversion — one cannot be sure. Sometimes they are true, sometimes false. In case of doubt, be explicit, cast it, and check the value directly.

Timings

Sometimes code shows an erratic bug. No matter what you do, you cannot reproduce it steadily. Question yourself if it might be related to shared resources. Their accesses may conflict in time. Do not think only of the obvious I/O issues. It may happen also with threads and tasks accessing to list of elements, cache management, etc. In these situations, pairing is highly recommended. Debugging points are no help (since execution with attached debug has different timings). Also, logging here rather than there may change timing with disruptive effects.

Encoding and Encryption

Your code features some encoding, decoding, or encrypting. If the result is no good, then put a close eye on the processing stage. There is a great delicacy concerning precise byte arrays and string, and even a single byte error may scramble the whole process.

If in doubt, try to cut out complexity. Double-check variables’ values along the process. And read the documentation again. Finally, maybe drop a question on Stack Overflow. Better to get a sour answer to a silly question rather than be stuck with no encode-decode.

Legacy Code

Clearly, if you wrote it one hour ago, the code fits the task better. But if it was authored one month ago, it may sound a little stale. One year ago, and it would be even worse. Worst case scenario: more than a year old, authored by someone not working in your company anymore, with no spec and no doc. Awesomeness. Probably you see dead code here and there. There are funny parts that nobody knows what they do, but they’re still there because “last time we touched it, everything broke and we could not deliver for a full week.”

The bug may sit right there, after some missing refactoring, wrong usage, partial modification, and misunderstood integration. Be brave and precise. Cut it out. Take the time it needs. Maybe rewrite it or fully analyze it and make it yours. Better be ex-owners of a rotten arm rather than dying from gangrene.

Reality Check

Are you sure this is the code being executed now? This is the funniest moment ever in debugging history. You call someone because “they are the smart dev for tracking all the bugs down.” They have been standing behind you for five minutes, munching some cookies. Having just joined the process and with no idea of the context, they drop the bomb: “Are you sure this is really coming from the source code you are reading?”

Seriously, there may be manual copies, wrong compilation, failed compilation, distribution, packeting failing ‘silently’, and another multitude of causes resulting in you running a different version of the code. If this is a possibility, add some stuff you can easily get feedback from – such as the famous printf("here") – in the code so you know it is executed. You may save a decade of mind-blowing, nonsensical hypothesizing and cursing by just realizing that the build did not succeed.

Too High, Too Low

The error you have been given is puzzling. You have no idea where it came from and it carries little or no mention of your source code. There is a high probability it is not directly in your code but rather one of the following sneaky situations:

  • Wrong configuration:
    Somewhere you are breaking a library, the framework, or other software you depend on. There is something low that is broken by an error. The error is so naïve that whoever wrote the documentation could not spot it or the error was too naïve to be mentioned. And the document was so boring you only read half of it.
    In short, double-check the reference of the external dependency and this time don’t skip the doc. Plus, just in case, drop a question on Stack Overflow.
  • Blank page:
    Things don’t just happen. Even though you think you did it well, you might have messed it up so naïvely that the overall stuff you are trying to inflate does not do anything — without any stack trace, silently, and graciously. Time to analyze the logs deeply. And maybe to read the doc properly (again).
    If you think twice about it, the two problems originated from the same critical point. Reading the documentation takes time and it’s no fun. However, reading it deeply (enough) saves a lot of silly mistakes. Neither is writing the documentation very fun. So, forgive all the devs out there who wrote the documentation if it does not mention your special-snowflake case.

Wrapping Up

Code is fun, but if it does not work then it stops being fun — especially if it is your job and your monthly income that depends on its quality. All the flaws we went through originate from the same author of the code. Be humble and keep that attitude. Doubt is your friend.

One last but shotgun thought. The positive part is that by coding your program, you created something where nothing stood before. You authored all the other good things in the program. And they do work, even if nobody cares about it. Do not get stuck on bugs. You have the power of creation.

Most-Read Articles

10 Tips To Become A Better Software Engineer

2nd April, 2021

Practical advice from a programmer with more than 12 years in the field.

Read more

How To Become A Golang Developer: A 6-Step Career Guide

6th April, 2021

Go is an in-demand language across the board. Here are the steps to become an employable and modern Go developer.

Read more

5 Most Asked Qs In The Amazon Software Engineer Interview

11th June, 2021

Analyzing over 1,300 personal experiences of those who’ve gone through the process.

Read more

World-class articles, delivered weekly.