Bug – undesired behavior in a program. Maybe it crashes, loops infinitely, doesn’t calculate something correctly, or has a security problem that makes it easy to hack.
Debugging – the process of identifying and fixing a problem in your code. You will start to notice a problem, but you’re only noticing its symptoms, not the root cause of it. Debugging involves trying to find the root cause. Sometimes, it can take an inordinate amount of time for you to find out the cause of the bug and how to fix it. It’s incredibly frustrating… until you fix it. Then you feel a wave of relief washing over you. You will go from severe despondency and feeling like the dumbest person on the planet when the bug is still a problem to feeling like a total genius when you figure it out. Debugging is a rollercoaster of emotions.
I’ve personally encountered some bugs that were so difficult to deal with that I just ended up refactoring my code. Sometimes, instead of fixing the mediocre code, you toss it out and start fresh. Please note that this isn’t suitable for every situation.
Debugging can take a while. If you knew what the problem was, it wouldn’t be a problem because you wouldn’t have coded it that way to begin with. If you change too many things all at once, you can’t expect a good prognosis. Change one thing at a time. It’s possible that your initial hunch is wrong. You will encounter plenty of red herrings.
When you write a fix for a bug, called a bugfix, you want to make sure to test it for many different possible scenarios. It’s possible to only partially fix a bug. You can also accidentally create new bugs when you’re attempting to fix an existing bug.
Kludge – putting something together quickly, poorly, and without using best practices. You might kludge together something and then want to refactor it later. “Quick and dirty” describes kludges well. You might kludge together some patch that fixes a bug because you want it done quickly rather than doing it perfectly. This is especially true of security-related bugs because people want to prevent hacking, and issue the patch relatively quickly, too.
Hasty patching can sometimes exacerbate the bug and even introduce new ones. Piling bad fixes on top of bad code is a palliative approach and should be avoided. The kinds of coding practices you used that gave you bugs will also give you bad fixes if you don’t recognize not only the problem in terms of code but also in terms of the thought process behind what you did. Think about how you can code things differently to avoid those kinds of bugs in the future. Just like how there are design patterns, I would say there are bug patterns – common mistakes people make that result in bugs.
Spaghetti code – when program flow is jumping around, reading and writing to so many places, and generally lacks encapsulation, it is referred to as spaghetti code, because spaghetti is very messy and the noodles are all intertwined instead of being organized. Spaghetti code is awful, and harmful practices can lead to writing it. It is tough to maintain, which makes stability, security, and extensibility hard. Eschewing spaghetti code practices and incorporating encapsulation into your coding is a good way to prevent bugs in the first place. You know what’s easier than debugging? Not having a bug to begin with.
Print debugging – sometimes, a kind of lazy way that people debug is just with print statements, to run the program normally and then to print out values of variables, or running a function and printing its return value. This isn’t a best practice, but many people do it anyway.
print(“The value of x at this point in the program is: ” + str(x))
Debugger – a tool to help programmers find and fix bugs in their code. A debugger will run the code much more slowly than usual and allow the programmer to see more detailed information about what’s running, such as the status of variables and things like that. Debuggers are much more powerful than print statements, but some people dislike their complexity.
Breakpoint – used for debugging, a breakpoint is a line the programmer selects to stop on so they can see the status of what is happening when the code is run up to that point rather than doing everything all at once. It’s a way of stopping and analyzing code while it’s in the middle of running. Humans are slow, and computers are fast, so we need breakpoints instead of running everything in real-time to find bugs. Some related terms include continue, step, and step into.
gdb – The GNU debugger. A text-based debugging tool to help figure out what the problems in your code are. It’s not as user-friendly as debuggers built into modern IDEs, but I guess it’s useful if you’re into more old-school stuff like vim. I wouldn’t recommend it when there are better ways of debugging.
Testing – programmers write many different kinds of tests to verify that their code works the way they want it to. Some kinds of tests include unit tests, integration tests, and regression tests.
Unit testing is the kind of thing that’s helpful, but you don’t know that it exists when you first start doing basic coding stuff. But in the back of your head, you’re thinking “okay, after I add this feature, I need to run the program and test it to make sure it does what I intended it to” and unit testing is a better-realized version of that, incorporating automation. This goes along with continuous integration and agile development, allowing people to commit more often and even merge to master more frequently, all without having to compromise security and stability (for the most part).
Unit testing sounds great in theory, but it seems like people either don’t do it well enough or at all in some cases. One time, Apple macOS had a bug that let you log in as root by entering nothing at all for the password. Proper testing before pushing out the update could have prevented this from happening. That’s the point of testing – to ensure no flagrant oversights make it to your production environment or the product you ship to end users. You might think you know what your code does, but no one’s perfect. Everyone messes up now and then. Testing will try to catch the times you mess up, though there can be limitations to the effectiveness of tests.
Unit testing alone is not enough. Unit testing is just testing a single piece of code. But many programs are humungous and have many different sub-programs all working together. Integration testing makes sure that the unit doesn’t break any other things in the bigger picture. A piece of code can pass a unit test and still fail an integration test. Lastly, there’s regression testing, which is making sure that new changes don’t break old things.
The main takeaway here is that you can never trust prima facie code without testing it thoroughly. Everything has to be vetted. When you start out coding, you might do simple manual tests, but eventually, you’ll have to use more unit, regression, and integration tests.
Syntax highlighting – when an editor changes the colors of words or characters in your program so that you can understand what it does. It helps to visually break up the flow of information and lets certain things pop out more, which can help you become a better programmer. Most modern IDEs have syntax highlighting on by default, but some need to be manually enabled. You can also choose your theme. There are usually light and dark themes, and you can sometimes use a custom color scheme. A popular syntax highlight color scheme is called Solarized, which you can get here: https://ethanschoonover.com/solarized/
That being said, default syntax highlighting is usually good enough.
Among other things, syntax highlighting can help you with debugging. If something is clearly wrong, you might be able to see the difference in the way that the text is highlighted.