As programmers, we can’t escape debugging. It is an integral part of our daily work, and it often means trouble. Programming is the act of producing value by writing code. When spending time debugging we aren’t producing value, to the contrary; It means we have bugs in our code, which means we have produced a lower value than we originally anticipated.
Therefore, the answer is simple- Don’t write code with bugs! Unfortunately, this is almost impossible, for the code is dark and full of errors. Instead, we should aspire to minimize debugging time as much as possible.
Debugging is like Game Of Thrones. If you want to play it, you ought to win, because when you play the game of thrones, you win or you
The following are a few tips that might help you win.
Be Like Tyrion Lannister.
We’ve already established that we need to minimize the time we spend debugging. We can define debugging time by the “Aha!” factor- We usually start debugging after a “What the heck” moment and finish after an “Aha!” moment. Using this terminology, debugging is the time spent between those two moments.
While the easiest choice is to spend this time F10’ing and F11’ing (stepping Over and Into) line by line, hoping to discover the bug, this is the worst approach, as you might waste a long time debugging code portions that are not related to the actual bug.
A better approach is to use the scientific method while debugging: Start with a hypothesis about the bug’s root cause and carry on by testing this hypothesis. If it is correct, great, if not, use what you just tested and learned to develop a new hypothesis that can explain both; the bug reproduction and the failure of your previous hypothesis(s).
The scientific method will help you reach the “Aha!” moment faster as you spend minimal time F10’ing and debugging unrelated code, and instead focus on a small portion of the code that either is responsible for the bug or might provide additional insights about the cause.
Be like Khaleesi and get familiar with resources and the tools available for you for finding the bugs faster.
The main tool a .Net developer usually uses is Visual Studio. Visual Studio is a powerful IDE and Debugger that is full of features. For example, it provides a great number of handy shortcuts that you can use to execute common actions and commands. Utilizing shortcuts can save you an aggregated substantial amount of time.
For example, if you need to attach to a process, use Ctrl+Alt+P to select which process you want to attach to, and once that is done, you can always reattach to that process by using Shift+Alt+P.
Fortunately for us, Visual Studio has many more built-in tools and third-party extensions like OzCode that we can utilize to squash all of the bugs.
Leave one bug alive and the heap is never safe.
- If you want to break on a specific iteration in a loop, or only when a condition is met, use Conditional Breakpoints.
- If your objects contain complex data and that is hard to visualize, such as images or arrays of numbers representing a graph, use DebuggerAttributes
- If you’re debugging a Multithreaded app and/or working with Async/Await, consider using the Threads, Parallel Stack and Parallel Tasks Windows.
- Use extensions that specialize in optimizing your debugging tasks such as OzCode. OzCode has many useful features, such as an improved Conditional BreakPoints, a set of tools for debugging LINQ, a way to visualize data that is faster and easier to use than DebuggerAttributes, and many more awesome features.
My favorite OzCode feature is Time Travel Debugging, which is like going back and forth in time while debugging, exactly like Bran Stark.
Be A Maester
Maesters tend to write down everything and anything strange they encounter. By doing so they are making sure that knowledge is preserved, sharable and accessible for a really long time.
Software Instrumentation (logging) has similar benefits for when it is hard to debug bugs that don’t always reproduce, or when there is a need to investigate a strange behavior or a crash in production.
Logging lets you trace back the steps your software has executed and let you explore the state that led to the crash or the unwanted behavior.
Instrumenting your software properly can be considered an art. You have to instrument just enough to be able to receive enough data about the execution, but not too much as it might hurt performance or contain just too many irrelevant data points that will eventually be harder to read and understand.
You can instrument your software using different approaches; from logging plain strings and texts to writing structured data that can be parsed and query easily.
Not everything has to be locally, you can write logs to remote databases or even to services like Azure Application Insights and Azure Kusto.
Choose what is suitable for you and for your needs. A good log can eliminate the need for debugging, as everything you need to know and test is already found in the logs.
Don’t reinvent the wheel! There are great logging libraries in .Net such as NLog for plain texts, Serilog for structured logs, and AppInsight/Kusto for logging structured data to a Monitoring system in Azure.
Using AppInsights for logging exceptions and crashes in production is pretty handy. It can be configured to notify the dev team about bugs and strange exceptions before the client has noticed anything at all.
Bugs are Coming!
New bugs are inevitably going to be introduced. Therefore, you ought to try and prevent them from entering your software, exactly as The Night Watch and The Wall tries to stop the White Walkers from entering Westeros.
Unit Tests and good code coverage are essentials for making sure your software is stable and that new code will not (probably) introduce new bugs.
In addition, good Unit Tests can be used as a contract for your software’s functionality. If tests cover all of the requirements of a feature, then you can be highly positive in the quality of your code, and there will be less chance for creating new bugs when modifying that feature.
I’m an advocate of TDD (Test Driven Development). It can be hard to start, but once you start, it is really easy to add new tests and the benefits are worth it- Stable Software, fewer bugs, high-quality software and it usually lead to a better code, since writing tests forces you to write code that is more modular and loosely coupled (otherwise it will be hard to test).
A Programmer Always Fixes His Bugs
Debugging will always be part of a developer’s job. We should always try to avoid it, but when we can’t we should try to do it as efficiently as possible to win the Game of Thrones!
- You can do things faster by using Visual Studio’s shortcuts and extensions like OzCode.
- You can find hard to reproduce bugs by applying instrumentation to your software.
- Using Unit Tests you can eliminate new bugs from being introduced to your software.
Good luck and Geros ilas!