The entire Digma team has been working out on our first official release! Here are some of the highlights we’ve added to Digma over the past few months
As a Java developer, I can’t help it but I often think about the good old days when I started my journey as a programmer. Back then, the systems we developed were relatively simple, and developers spent more time building software rather than fixing it. Sadly, gone are those days as the rise of distributed systems, microservices, and cloud-based computing has introduced a new level of complexity and potential pitfalls to building software.
The realization that I’m spending much of my creative energy and time debugging software sometimes makes me think that maybe I’m not good at my job. However, it turns out that software developers, in general, spend about 30 to 50 percent of their time debugging and fixing software. Since, just like Maurice Wilkes, I realized that a large part of my life will be spent finding and fixing bugs in my code.
“I realized that a large part of my life from then on was going to be spent in finding mistakes in my own programs”. Maurice Wilkes
Rather than getting frustrated about how much time I spend debugging software, I decided instead to embrace it as an integral part of the software development lifecycle. And find the most efficient techniques to effectively debug or just write better code with fewer bugs in the first place.
However, to reduce the time we spend debugging, we need to adopt effective debugging techniques and understand the power of modern Java debugging developer tools.
So I decided to share some Java debugging tips and techniques you can use to debug Java apps using IntelliJ IDE. I’ll also discuss how to leverage the IntelliJ IDE debugging features to track down and fix bugs efficiently. I hope you get some value from it:)
11 Java debugging tips and techniques using IntelliJ IDE
- Leverage IDE Debugging Capabilities
- Setting Line Breakpoints for Precise Debugging
- Leveraging Watchpoints for Targeted Debugging
- Step into Selection: Focusing on Specific Code Segments
- Step Filtering: Navigating Code Execution Efficiently
- Step Over and Out: Fine-Tuning Code Execution during Debugging
- OpenTelemetry and Observability
- Use the Variables View and Expression evaluation for Comprehensive Insights
- Remote Debugging and Collaboration
- Thorough Testing and Validation
- Maintain a debugging checklist
1. Leverage IDE Debugging Capabilities
Long gone are the times when developers used to skim through thousands of lines of code just to find the missing semicolon or bracket. Modern IDEs such as IntelliJ now include various tools that make debugging less excruciating.
For example, the IntelliJ IDE includes Java debugging features such as advanced breakpoints, integration with testing frameworks, remote debugging, and integration with testing frameworks.
Such features will save you time and enhance your visibility and precision while debugging.
2. Setting Line Breakpoints for Precise Debugging
Breakpoints are so common in programming chances are you have already encountered them. In real life, breakpoints are synonymous with red traffic lights that you are almost certain to encounter while driving, allowing you to temporarily stop, observe the surroundings, and ensure a safe passage before you resume your journey.
Similarly, line breakpoints are special markers that suspend the execution of a piece of code at a specific line of code. Pausing the execution of code just before the line of code is suspected to have a bug allows you to examine the program’s behavior at runtime.
3. Leveraging Watchpoints for Targeted Debugging
Just like line breakpoints, watchpoints allow you to pause the execution of a program. However, unlike line breakpoints, watchpoints allow you to focus on a specific field. For instance, you can focus on a particular instance variable allowing you to pause and investigate your program behavior whenever that variable is read or written to.
This is very helpful when your program returns a wrong value. Setting a watchpoint allows you to investigate and correct the issue. Watch points are sometimes called data breakpoints.
To set a field watchpoint on IntelliJ, you can place a caret on the line where the field is declared and press Ctrl + F8.
4. Step into Selection: Focusing on Specific Code Segments
Debugging large Java code bases can be overwhelming, even for an experienced developer. There are many techniques that can be used to debug large codebases effectively. One of the most common strategies is using IntelliJ stepping actions.
IntelliJ IDEA provides different stepping functions that you can choose from depending on your strategy. Generally, stepping functions allow you to perform step-by-step execution of your code.
Step into is one of the step actions provided by Intellij. This technique is particularly useful whenever you’re unsure which methods in your code are returning the wrong result. Using this action, you can focus your debugging efforts on code segments that you think are more likely to cause issues.
IntelliJ IDEA, press the F7 function key to toggle this action. Alternatively, you can use the Step Into button under the debugger actions menu.
It is also important to note that in cases where you have multiple method calls, it’s wise to use the Smart Step Into action. This action lets you choose which method to enter whenever you have multiple methods.
5. Step Filtering: Navigating Code Execution Efficiently
Suppose you’re debugging a huge code base, and you’ve decided to use the step into action. However, upon further inspection, you realize your code includes helper methods and external libraries you don’t want to enter. This is where the step filtering action comes in handy.
Using the step filtering action in your IntelliJ IDEA allows you to skip through JDK classes, packages, and external libraries during debugging. The step filtering action streamlines your debugging process, allowing you to focus on debugging the most relevant sections of your code, saving you time and unnecessary mental strain.
You can set up Step Filtering by going to Settings -> Build, Execution, Deployment -> Debugger -> Stepping
6.Step Over and Out: Fine-Tuning Code Execution during Debugging
We’ve highlighted how Step into Action can help you delve into methods, while Step filtering can be useful if you want to skip through unnecessary code. However, what if, during a debugging session, you wish to skip through a line of code that has a method call into the next line? This is where you can use the Step Over action.
The Step Over actions is useful whenever you are certain that the method implementation works fine. For instance, in the example below, skipping line 5 skips the debugger to line 6.
You can also use the step out action to return to step out of the current method and return to the caller method.
7. OpenTelemetry and Observability
If you have been developing applications for the last couple of years, certain practices such as continuous development and continuous integration (CI/CD), microservices, DevOps, and tools such as Docker and Kubernetes have become very popular.
These tools have allowed us to develop more distributed and complex systems that are loosely coupled. Relying on traditional Java debugging techniques for such applications isn’t reliable.
However, using modern observability tools, you can instrument your code and obtain valuable information such as logs, traces, and metrics.
One such observability tool that Java developers can use is OpenTelemety (OTel). OTel is an open-source observability framework made up of a collection of APIs, tools, and SDK. Using OTel, Java developers can collect, instrument, and analyze data and use it to understand, troubleshoot and fix bugs in complex distributed systems.
Another useful software tool that Java developers can leverage is Jaeger. When working with complex distributed systems, Jaeger allows developers to gain visibility through features such as transaction monitoring, root cause analysis, service dependency analysis, and distributed context propagation.
Java developers can also use Digma to understand and debug complex code bases. Digma leverages OpenTelemetry behind the scene to collect data such as traces, logs, and metrics. Once the data is collected, Digma analyzes it to detect meaningful insights about the code. It looks for regressions, anomalies, code smells, or other patterns that can be useful for knowing about the code and its use making the debugging process much easier.
8. Use the Variables View and Expression evaluation for Comprehensive Insights
Debugging is an interactive process that involves changing values and refining code to see if the most recent changes work. However, as developers, sometimes we wish to have an idea of how the changes we intend to make will affect our code or how the output might change.
Instead of using print statements, variable inspection allows you to view variable values inline at the end of the line where they are used. Alternatively, if you would like more information about variables, the Variables tab under the Debug window enables you to do that. In this window, you can modify variables and observe the program’s behavior under certain conditions without affecting the code.
Examining your program’s state and the effect of potential code changes at runtime is also important. The Evaluate Expression editor provides a sandbox environment to test code changes and try variables. This can be useful when debugging code, and you’re not sure whether your changes will work.
9. Remote Debugging and Collaboration
The Covid pandemic has pushed most teams to remote working, which is the norm now for most development teams. Unfortunately, in such a setup, you can’t quickly have a physical debugging session with colleagues in different physical locations.
However, IDEs such as IntelliJ now have advanced features enabling you to collaborate with other developers. Collaborative debugging sessions enhance productivity and knowledge sharing, allowing teams to solve complex bugs quickly.
Remote debugging and collaboration can also be useful if you’re working with distributed systems. If your program is running in a remote environment, using remote debugging allows you to connect to those environments and fix issues in real-time as if the code was running locally on your machine.
Remote Java debugging can also be useful whenever you wish to replicate different real-world scenarios, allowing you to observe how your application will behave in different use cases.
10. Thorough Testing and Validation
Testing your applications can help you evade debugging nightmares by enabling you to detect and fix issues in your code early in the development phase. This is commonly called proactive debugging, where developers focus on finding bugs before they occur.
There are different ways that you can leverage tests to identify bugs. You can start by making sure that you write comprehensive unit tests for your application. Unit tests are meant to ensure that individual code units in your program are working as intended. You can cover different edge cases, scenarios, and potential failure points through unit tests, increasing your chances of detecting potential bugs.
Besides unit tests, you should also use integration tests, functionality tests, and regression tests to ensure that different components in your program are working effectively. Using these tests, you can easily identify bugs that may come up due to compatibility issues or communication failures. You can also leverage system tests to uncover bugs that may come up when your app is deployed to real-world users.
11. Maintain a debugging checklist
As simple as it sounds, maintaining a debugging checklist can save you from confusion and simply getting overwhelmed when debugging Java applications. Maintaining a Java debugging checklist can also help you maintain a systematic approach when debugging your applications.
Documenting your debugging details can also serve as a future point of reference whenever you face a similar bug again. Similarly, your team members can refer to the same document if they encounter a similar bug in future development. Such a practice can also improve knowledge sharing in your team.
Conclusion: Debugging Java using Intellij
Debugging is a skill that every developer needs to have. However, mastering the art of easily finding and resolving bugs takes time and requires understanding the codebase you’re dealing with. In this article, we’ve shared Java debugging tools that, if incorporated into your debugging workflow, can help you debug faster and deliver robust applications.
What is Java debugging?
Java debugging is the process of identifying and fixing errors and bugs in Java code, ensuring that an application works smoothly.
How do I start Java debugging with IntelliJ or Eclipse?
You need an IDE such as IntelliJ or Eclipse that has a set of debugging tools. To start Java debugging, you can set breakpoints in your code and run your Java program in debug mode.
Is debugging Java using IntelliJ IDE easy?
Java and model IDEs such as IntelliJ include a set of tools that make Java debugging easier. However, debugging depends on many other factors such as experience and availability of tools.
What is the best debugger for Java?
The choice of a debugger depends on your needs and personal preferences. However, modern IDEs such as IntelliJ, Eclipse, and VS Code have excellent debugging capabilities.
Digma is a runtime linter that lets developers quickly identify risky code, potential errors, and bottlenecks in complex codebases. Discover how Digma can help you with debugging Java apps using IntelliJ IDE: Here.