Debugging Techniques

Summarized using AI

The Mindset of Debugging

Kyle d'Oliveira • November 08, 2022 • Denver, CO

In his talk "The Mindset of Debugging" at RubyConf 2021, Kyle d'Oliveira addresses the critical yet often overlooked skill of debugging in software development. He emphasizes that debugging is a fundamental part of a developer's job, accounting for a significant portion of their time. The core message of the presentation is that debugging is a skill that can be honed through structured practice and a specific mindset.

Key Points Discussed:

- Importance of Debugging: Debugging comprises a notable percentage of a developer's work, with studies indicating about 50% of time spent on fixing bugs and making code function properly. Despite this, formal training on debugging is scarce.

- The Mindset Shift: Effective debugging requires a mindset that embraces curiosity and structured thinking. D'Oliveira shares a metaphorical story of two lumberjacks, highlighting how taking time to prepare (sharpening one’s axe) can lead to more effective outcomes in debugging.

- Systematic Approach: D'Oliveira outlines a systematic method for debugging that includes:

- Observation: Noting what is failing or behaving unexpectedly.

- Hypothesis Formation: Forming a clear hypothesis about the cause of the issue based on observations.

- Experimentation: Testing the hypothesis through structured experiments, analyzing data, and iterating as necessary.

- Active Recall: Emphasizing the importance of questioning existing assumptions and utilizing past experiences as tools for learning.

- Real-World Example: D'Oliveira presents a hypothetical debugging scenario to illustrate the systematic approach: debugging a contact update feature that fails to save correctly. He walks through the process of gathering observations, forming hypotheses, running tests, and reaching conclusions based on data, demonstrating the iterative nature of debugging.

- Continuous Learning: Emphasizes that debugging is not a one-time skill but requires ongoing effort - practitioners should intentionally dedicate time to debugging tasks to improve proficiency.

Conclusions and Takeaways:

- Debugging is an essential skill that greatly benefits from an organized approach and can be developed through active practice and curiosity.

- Developers are encouraged to embody a mindset that sees challenges as opportunities for learning, fostering both individual growth and improvement in overall team productivity.

- The talk ends with a call to action for experienced developers to mentor others, further multiplying the positive impact of debugging skills within their organizations.

The Mindset of Debugging
Kyle d'Oliveira • November 08, 2022 • Denver, CO

We, as developers, spend a large portion of our time debugging software. Sometimes the problems are easy to understand, but many times they are not and we are thrown into unfamiliar code and are expected to quickly find the problem and craft a solution. However, honing the skill of debugging doesn’t come up as much as it should. In this talk, I’ll go through a generic approach to debugging that can be applied and built upon to help you excel as you move through your career solving problems for a living.

RubyConf 2021

00:00:10.240 Hello everyone and welcome to my talk, 'The Mindset of Debugging'. I hope you've been enjoying the RubyConf so far.
00:00:12.799 It's almost the end of the last day, and you've almost made it through just a few more talks left.
00:00:20.880 My name is Kyle d'Oliveira, and I'm a senior software engineer based out of Vancouver, BC, Canada. I have been working with Ruby for over a decade now, and I think the language and the community are incredible.
00:00:30.800 I'm really happy to be here and to contribute back to the community as well. I work at Aha! where we help companies build what matters to them with our suite of products.
00:00:43.680 We have an amazing team that's fully distributed by design, all over the world. Our company culture is one of the best I've ever experienced.
00:00:45.840 Just like everyone else at this conference, we are hiring. Oh, and there was another talk yesterday called 'The Science and Magic of Debugging' that touched on similar topics. While there's a little overlap, I think these talks are quite complementary.
00:01:16.400 If you haven't seen that talk, I encourage you to check it out to dive deeper into debugging. To begin today's talk, I would like to share a very short story about a competition between two lumberjacks.
00:01:30.240 While this is a talk about the mindset of debugging, there's an important lesson here that I think we need to embrace when thinking about debugging or learning in general.
00:01:50.720 We have two lumberjacks, Fred and John. The first lumberjack, Fred, was a very hard worker. He believed he could chop more wood than anyone else, so he challenged John to a competition.
00:02:06.640 The rules of the competition were straightforward: whoever produces the most wood at the end of the day would win. The next morning, they began chopping away as fast as they could.
00:02:33.760 After a while, John stopped. Fred realized there was no sound coming from his competitor's area, and he thought, 'Aha! John must be tired already; there's no way he can catch up to me.'
00:02:44.400 Soon enough, John started chopping wood again, and the competition continued. However, after a while, once again, Fred heard his competitor go quiet.
00:02:52.560 This further motivated him; he could smell victory. He easily maintained the pace he was going, and this pattern of John’s starting and stopping continued throughout the competition.
00:03:05.200 As the competition came to a close, Fred was confident in his victory. However, when it was all over, and the amount of wood was chopped and counted, it turned out John had cut more wood than Fred.
00:03:14.400 Astonished, Fred asked, 'How could you have cut more wood than me? I heard you consistently stop working!' John replied, 'Every time I stopped while you were chopping away, I was sharpening my axe.'
00:03:19.280 This is the idea of slowing down in order to speed up. It comes up in many different areas and can be a really powerful tool for learning.
00:03:41.200 In the world of programming, we don't have literal axes; at least I really hope we don't. But instead, our axes are our minds, and we sharpen our minds through intentional learning and focus.
00:03:50.560 This talk is about the mindset of debugging, and the core message around debugging is that it's a skill. We should be intentionally sharpening that skill.
00:04:08.400 What comes to mind when you think about debugging? You may jump to tools like Pry or Bybug, or think about websites like Stack Overflow. These are tools and can be very helpful and valuable in the process. In fact, without access to the right tools, debugging can become much harder.
00:04:36.640 However, that is not the focus of today's talk. Debugging is a process—the process of detecting and removing current or potential errors in code. It requires looking at all the data in front of you, all the accumulated knowledge you've built up, to develop a theory of why something does not work as expected before writing code to fix it.
00:04:55.680 There is no silver bullet to learning how to debug. There's no magic tip that will make you ten times better at debugging if you just learn it. You first need to build a foundation of knowledge to debug quickly, something that takes effort and attentive practice.
00:05:20.480 What you can do, however, is approach debugging with a particular mindset—one that gives you a framework to build upon and helps increase your rate of learning.
00:05:35.680 The purpose of today's talk is to outline one possible mindset, while also encouraging you to think about how you can approach debugging in ways that facilitate your growth.
00:05:56.160 You could spend your efforts in a number of different places, so the question is: why should you put effort into learning how to debug? In 2013, a study conducted by the Cambridge Judge Business School found that about 25% of software engineers' time was spent fixing bugs, another 25% on making code work, 30% on writing code, and 20% on designing code.
00:06:23.120 Fixing bugs and making code work can both be considered debugging. While this study was not specific to Ruby, it holds true based on my experience.
00:06:36.720 Just take a moment to consider that up to 50% of your job might be spent debugging. That's wild! Yet, I have rarely seen training on how to debug.
00:06:56.640 There are plenty of talks and discussions on how to write good code and what constitutes good patterns, but how often are we taught how to debug something? Sometimes we get introduced to a new tool, and there are new ways to use it, but how often are you explicitly taught how to debug?
00:07:16.960 Often, learning to debug is just a side effect of doing your daily job. Yet this is the majority of how you spend your day. Debugging is also often thought to be very difficult.
00:07:45.280 Brian Kernighan once said, 'Everyone knows that debugging is twice as hard as writing a program in the first place. So if you're as clever as you can be when you write it, how will you ever debug it?' This encourages people to be less clever when writing code so it's easier to debug.
00:08:04.160 There’s another message here that implies if you encounter code that's really clever, you might not be able to debug it; it’s seen as impossible. Additionally, when debugging, you're often faced with external pressures that intensify those feelings and make hard things feel even harder.
00:08:20.880 It's a common joke in programming communities that if your tools are unavailable, you can't do your job—'Ahh! Stack Overflow is down; we're all gonna die!' But this isn't entirely true; you can still do your job.
00:08:40.080 There's also the joke that bugs eventually become features. Hearing this repeatedly can reinforce the idea, especially for early career developers, that debugging is hard and that they may as well just give up and find the easiest workaround.
00:09:01.600 Hypothetically, even if we were to deploy code with no bugs, making code work, as noted in the earlier study, will still take a significant amount of time. You’d still need to learn how to debug because it’s unrealistic to expect to deploy code free of bugs.
00:09:21.760 Debugging is an essential skill we will always need to learn and focus on. Debugging can be viewed as a series of experiments to determine what’s going wrong with the code, borrowing from the scientific method.
00:09:36.480 You’ve seen this referenced throughout the conference. Conceptually, this can be simplified into a few steps: figure out your observations, create a hypothesis that aligns with those observations, create an experiment to validate or test your hypothesis, and then analyze the data you gather.
00:10:02.240 However, it seems little effort is spent at the hypothesis stage, which is incredibly critical. This step can guide you through debugging by keeping you focused and deciding what to test next.
00:10:27.520 Without a hypothesis, debugging can often become a game of guess and check. I'm sure you've all had the experience of removing pieces of code in hopes that the bug goes away.
00:10:44.160 It's easy to dismiss these steps as obvious, but there is real value in taking the time to think through each stage. You should fight the urge that says this is obvious and does not need acknowledgment.
00:11:02.760 Fighting your assumptions is important for debugging, and part of that value comes from a principle called active recall. This isn't a new trend out of Rails; it's an efficient learning approach that emphasizes stimulating your memory during the learning process.
00:11:23.920 One way to practice active recall is to stop and ask yourself a question, digging into your past experiences to find an answer. What's crucial is to not give up when it gets tough; instead, engage your curiosity.
00:11:39.760 Discuss what you know, theorize about what it could be, ask for help, take a step back, do something else for a while, and return with fresh eyes. What you're doing is expanding your comfort zone bit by bit.
00:11:59.520 This could lead to more effective learning and ultimately better debugging skills. To illustrate this, I want to work through an example.
00:12:12.080 Imagine it’s your first day on the job, and you get a bug report from a customer. The customer says, 'I tried to update a contact's phone number. It says it's saved, but the number isn't updated, and they’re worried they're losing data.'
00:12:32.960 What do we do from here? We can document some observations. If this is all we have, we create a fact that states we know contact updates aren’t working. Using that, we formulate a hypothesis: something deployed recently may have broken contact updates.
00:12:54.800 How do we test this? A straightforward test would be to try it locally, so we spin up our server, enter the contact's phone number, and save it. If it works, that’s a bit perplexing, but we can continue to be curious.
00:13:28.560 We check our local logs to see where the request hits and what code was executed. It seems to hit a contacts controller, which delegates behavior to another service. We can keep diving deeper.
00:13:43.040 It’s a plain Ruby object that holds a reference to a contact. It updates the name, finds the phone number if it exists, or builds a new one, and updates it at the end. Nothing stands out as broken, so we check the tests.
00:14:07.920 The tests seem to check the update name, verify creation of new phone numbers, and confirm updating phone numbers—all appears to be functioning correctly.
00:14:27.120 What can we conclude? The typical contact update flow works for us. So, what do we do? Do we just give up and say 'Works for me?' Or do we suspect the customer is lying or doesn’t know better?
00:14:42.880 None of those options help us improve our problem-solving skills. We should embrace the discomfort of not knowing. How many times have you passed over a bug simply because you didn't understand that part of the code?
00:15:05.920 Sometimes we think we need a baseline experience before we can debug code, but that’s untrue. Debugging is often one of the best ways to learn a codebase. When you’re pushed slightly beyond your comfort zone, your capabilities grow.
00:15:29.760 So let's update our observations and consider a harder statement: perhaps not all contacts are updating properly, but instead, perhaps some contacts fail to update.
00:15:50.000 This shifts our understanding and we continue on this path. We can pose a new hypothesis: if my path worked, the customer may not have tried that path.
00:16:11.280 How do we test that? We need to determine what steps the customer took. We might do some forensic analysis to find out when they submitted their issue.
00:16:27.760 This might involve examining the logs to see what they did around the time of reporting their issue. In this example, it appears they did hit the contact endpoint to update their details.
00:16:46.640 Even though this seems a bit strange, we shouldn't give up; let's continue digging. We can note that the user did go through the expected path.
00:17:02.080 Next, we can formulate a new hypothesis: maybe they haven't passed the parameters correctly. How do we test that? We have a question to guide us.
00:17:15.040 We need to determine what parameters were included in the user's request. Maybe we can find this through our application performance monitoring (APM) tool.
00:17:34.560 There, we could analyze the logs and check whether there is a phone number available. We also might need to verify the contact ID.
00:17:54.480 If we conclude that the request did contain the expected parameters, we continue the process; update your observations, create a new hypothesis, and persist on this path.
00:18:06.560 One hypothesis could be that the record isn't being saved. To validate this, we may look up error tracking data from tools like Sentry, Datadog, Honeybadger, or others.
00:18:20.760 If we find no errors are recorded, that's informative because the logs indicate it completed without issue. However, absence of errors does not invalidate our hypothesis.
00:18:42.720 We need to keep investigating. If we suspect that the contact wasn’t saved, we should check the database directly, which could provide further insight.
00:19:01.440 You might access the production database or potentially utilize tools to analyze queries and results. Doing this will allow you to check the entries in the database.
00:19:20.160 This would help assure that the record does exist in the database which would further clarify our observations.
00:19:42.560 At this point, we can analyze the query that fetches the phone number; there can be multiple records due to the nature of relational databases. If there’s no unique setup on phone numbers, one contact might have more than one phone number.
00:20:05.760 Because the service may apply no order to which record to update, it could cause the selection to be non-deterministic.
00:20:21.840 Thus, adding an 'ORDER BY' clause could easily fix this hypothetical flaw in the example.
00:20:33.840 The process also teaches you about identifying entry points to the codebase, understanding the functionality it provides, and when things go wrong, it's okay to be wrong multiple times throughout your debugging journey.
00:20:50.720 Every time you're wrong, you learn something new about the system you're working with. This process can help you contemplate various questions about how to merge values in the database or prevent issues from happening again in the future.
00:21:14.720 By approaching debugging methodically, you can become stronger, learn the code domain you work within, and create a mental model of the system you're engaging with.
00:21:36.240 You can also identify useful tools or highlight gaps in your toolkit and promote building new tools to enhance the debugging experience for everyone.
00:21:50.160 Moreover, this method supports building a foundation that makes debugging other problems easier in the future. Ultimately, debugging isn't magic; it's about practicing until you get better at the skill.
00:22:06.480 I recommend dedicating 30 to 60 minutes a few times per week to intentionally focus on debugging tasks. This can involve fixing bugs from error-tracking software, or addressing tests that fail on your master branch.
00:22:31.680 We aren't designed to debug all day, every day, but structured bursts of focus can lead to significant improvements. This structured practice not only trains you but is also beneficial to the business, as you eliminate errors that impact customers.
00:22:51.840 Remember the earlier story of sharpening your axe and honing your skills. This structured effort is a way to hone your debugging ability, sharpening your mind over time.
00:24:01.120 Ultimately, the sharper your axe, the more proficient you will be when debugging, and if you're already skilled, consider investing time in helping others sharpen theirs. Transition from simply being an additive force in your company to a multiplicative force that enhances everyone's capabilities.
00:24:34.640 Thank you for listening to my talk. I'm here to answer any questions in the Discord channel after this talk, and we have about five minutes for any questions now. So, if anyone has any questions, feel free to ask.
00:25:00.000 What are some of the lesser-known debugging tools you really like to use? You're probably all familiar with Pry, Bybug, and New Relic. Any others that stand out to you?
00:25:32.000 Personally, I find that most debugging relies on knowing how to answer questions—identifying where your entry points are, whether in a controller, a job, or a rake task. I tend to go low-level using put statements or debuggers to inspect variables.
00:26:06.660 There's a gem called Method Source that helps display Ruby methods, very useful within a debugger to navigate code. Most of the time, we don't need complicated tools; we need the right tools to answer specific questions.
00:26:14.400 Thank you for coming! You've got a couple more talks left and you're almost through the day. Take care!
Explore all talks recorded at RubyConf 2021
+95