Early-Career Developers

Bridging the Knowledge Gap: Debugging

Bridging the Knowledge Gap: Debugging

by Mina Slater

In her talk "Bridging the Knowledge Gap: Debugging" at RubyConf 2019, Mina Slater addresses the complexities and challenges that early-career developers face in debugging their code. She emphasizes that despite the significant time spent fixing bugs, formal training in debugging techniques is often lacking in coding boot camps and tutorials, leading many developers to struggle in their first roles. Slater shares her personal journey from a theater career to becoming a software engineer, highlighting feelings of inadequacy and confusion when faced with debugging early in her career.

Mina outlines her experiences and provides practical debugging strategies grouped into three main lessons:

- Look Under the Hood: She explains the importance of using print commands like 'puts' and 'p' within Ruby to trace the flow of data and understand application behavior. She also introduces the use of Pry, a powerful debugging tool that allows for interactive exploration of code during runtime, enabling developers to inspect variables and navigate through the application easily.
- Tap the Phone Lines: Slater describes the significance of using browser dev tools, especially the network tab, to monitor HTTP requests and responses. She illustrates how this can help identify errors and ensure the application processes are functioning correctly, thus addressing issues effectively.
- Find a Bug, Write a Test: She highlights the necessity of writing tests to handle edge cases, ensuring robust code that prevents regression errors. An example from her work showcases how writing tests helped her and her colleague identify and resolve a bug resulting from a database query returning nil values.

Mina concludes her talk by encouraging developers to embrace mistakes as learning opportunities and underscores the importance of a supportive work environment. She believes that fostering discussions about gaps in knowledge and learning from more experienced colleagues is crucial for overcoming challenges in debugging. Slater's overarching message is one of resilience and continuous improvement in the field of software development.

00:00:11.870 Hi everyone! Thanks so much for being here today. Before we begin, I just wanted to mention that I have included my Twitter handle in the upper right corner of the slides. If you want to look me up, I won’t be offended if you're checking your phone instead of paying attention to me.
00:00:18.420 The title of my talk, as you can see here in the program, is "Bridging the Knowledge Gap: Debugging." To me, that’s really just a fancy way to say that this talk is about what I wish my 2018 self had known about debugging.
00:00:31.529 So, to start off a little introduction: my name is Mina, and my pronouns are she, her, and hers. I graduated from a coding bootcamp in April of 2018, and I am currently a software engineer at Tandem, which is a consulting agency in Chicago. Fun fact: I'm very nervous right now!
00:00:51.840 This is the third time I’ve given this talk, and I’ve never received such a positive reaction—it’s really cool! If you’ve been in a career for many years, like I was, starting a new job in a new field can be really scary. I was transitioning from a ten-year career in theater, where I was the expert at my job, to a new industry and a team where everyone knew more than I did.
00:01:58.220 I knew I would be the most inexperienced developer at my entire company, but I was ready to face the challenges of this new career head-on. I was mentally prepared to put in a lot of energy into learning, improving, and becoming the best developer I could be. However, I wasn’t really prepared to discover just how much I didn’t know.
00:02:48.200 I quickly found where my skills were lacking. I worked inefficiently and often felt lost when it came to things like testing and debugging. Let's be honest, we spend a lot of our time as developers squashing bugs. Unfortunately, I don't think my experience was unique, as a majority of coding bootcamp graduates leave with knowledge gaps in essential skills like debugging. Many of us struggle at our first jobs because of this.
00:03:00.590 When I first started learning to code, error messages were really scary. It felt like being scolded by a teacher for making mistakes. Since we’ve all been conditioned to avoid mistakes in life, I felt like a failure whenever I encountered large, red error messages. They reminded me of the big red X’s my teachers used to put on my tests in school, which intimidated me into inaction.
00:03:39.310 In my attempts to avoid these error messages, I often didn't run my code out of fear. I would write code in the bubble of my text editor and stare at the string, wondering if it looked right. My only indication of correctness was my limited knowledge of the programming languages I was using and maybe an example or two. As you can imagine, I wasted countless hours trying to fix things before I would let my program run. When I finally did run the program, it, of course, didn’t work. Without having been taught how to debug or having seen anyone demonstrate it, I didn’t know how to approach debugging, such as reading error messages.
00:04:51.160 My only approach was to stare at my code line by line, hoping to find any typos or mistakes. No one told me there were better ways to debug, so I unknowingly practiced bad debugging habits. Don’t get me wrong; I’m not saying that bootcamps don’t produce quality developers. In fact, I believe more companies should be hiring career changers and bootcamp graduates because we bring past experiences and soft skills that are sometimes harder to teach. However, it’s challenging to cram so much knowledge into someone's brain in just three months.
00:05:31.720 I understand that coding bootcamps often have to make compromises in their curriculum, focusing on syntax and basic practices like assigning variables and making loops. Consequently, important topics like testing and debugging often get pushed aside.
00:06:08.420 After I finished bootcamp and started working, it quickly became apparent that there were better ways to fix bugs. I had to change my habit of merely staring at the code and hoping it would work, but I didn’t know what to replace it with. The part of my brain where knowledge about debugging should live felt like a big empty gap, and I had to overcome it by filling it with the right tools and strategies.
00:06:49.980 At Tandem, we pair program all the time, which gave me a chance to observe more experienced developers at work. I’m fortunate to have a supportive team that encourages me to confront what I don’t know and ask questions. We would break things together and fix them as pairs, which exposed me to how different people approach bugs and errors.
00:07:20.360 For instance, my coworker Shirai is great at using the network tab in the developer tools, while Sascha is a wizard in the Rails console. A smart colleague once told me that, in software, you often have to break everything first before you can piece it back together and make it work. I believe this applies to learning as well because if we can’t acknowledge what we don’t know, we won't allow ourselves to learn.
00:07:40.990 Having a supportive work environment allows me to identify my knowledge gaps. This way, when I encounter something I want to learn about, whether it’s a new approach to a problem or a new tool, I’m ready to fill that space in my knowledge. In the next thirty minutes, I’d like to share the most important lessons I’ve learned about debugging during my first year in software development. I don't aim to turn any of you into master debuggers today, as there simply isn’t enough time for that—but if you take away even one new insight, I’ll consider my job a success.
00:08:26.740 I’ve organized the things I’ve learned about debugging into three main categories: lesson number one: look under the hood; lesson number two: tap the phone lines; and lesson number three: find a bug, write a test. These are not meant to be rigid roadmaps, but rather starting points for the debugging process. Depending on the issue at hand, we can use one of these three strategies, mix and match, or explore alternative routes.
00:09:06.140 All the examples in my presentation will be done in Rails, but other programming languages and frameworks have their equivalent tools, and the core ideas are transferable. So, let’s begin with lesson number one: look under the hood. Perhaps I was exaggerating a bit when I claimed my bootcamp didn't teach me anything about debugging.
00:09:56.100 I was told to use console.log for debugging, so I was aware I could use print commands to check variable values at runtime. Comparing these printouts to expectations helped me figure out where my code needed to change. In Ruby, we use print, puts, or p instead of console.log. While all these methods serve a similar purpose in outputting information to the console, puts is more straightforward, while p gives us a more detailed view of the object being debugged.
00:10:53.420 For instance, with an Active Record object, we can observe the differences between using puts and p. In the Rails console, using puts will show us the class of an object, whereas p will provide specific information about the instance. Implementing piecemeal debugging through print statements, or 'printing breadcrumbs,' allows us to visually trace the path of our program and examine actual pieces of data.
00:12:06.580 We can verify that all the expected data appears and if any expected output doesn't show up, it likely means the program never executed that piece of code at all. Aaron Patterson, also known as Tenderlove, is famously known as a 'puts debugger.' He makes a compelling case for printed debugging in a blog post, which I will share later. Although his insights often feel over my head, they do reinforce the basic principle of using print methods.
00:12:51.610 However, I usually convene with Pry, which is a gem that lets me peek under the hood of my Rails application. By adding 'binding.pry' into the code where I want closer examination, I can run the program and have it pause, providing me with an interactive console in the terminal. Using Pry is a great way to explore our program's internals while offering more flexibility for debugging. For instance, I've created a simple app called Puppy Gachi, which tracks puppies and their activities based on user interactions.
00:13:38.380 In this app, I can place breakpoints in the service object that manages the puppies’ needs. By reloading the page in the browser, we can observe that the request hits the server and pauses at our first breakpoint, allowing us to inspect the current state of our program. In the interactive Pry session, I can use commands like 'ls' to list the methods available in my current context and navigate through the codebase.
00:14:56.320 For example, as I step through the code, I can check the status of instance variables, which provides insight into what’s happening at each line. This kind of nuanced exploration is invaluable in understanding the flow of logic within the application.
00:15:43.020 Once I’ve checked a couple of variables, I often clear the display and use the command 'where am I' to reorient myself within the Pry session. If I'm interested in examining a specific method, I can use the 'step' command to dive into the definition of that method. It’s crucial to remain calm when I find myself in unfamiliar code, as this is often a part of the learning process.
00:16:29.600 The last navigation command I’d like to discuss is 'continue.' This tells Pry to resume execution until it hits the next breakpoint. After examining all the necessary information, I can exit the Pry session to allow the program to finish executing.
00:17:03.170 Peeking under the hood of my program has been a game-changer for me. The most challenging aspect of learning to code was figuring out the connection between what I typed and what appeared in the browser, especially in a dynamically typed language like Ruby— which allows any data type to be assigned to the same variable without warnings.
00:17:46.210 You know that feeling when you see an error message that seems incomprehensible? This particular error indicates that the program tried to call a method on something that has a value of nil. Understanding these error messages is crucial in troubleshooting, as they often reveal the source of the issue.
00:18:28.620 However, simply debugging does not only involve the code. As I work at a consulting agency, my current project includes a Rails API backend and a React frontend. Therefore, testing features in the browser while writing code is standard practice. When things go wrong, the best-case scenario is seeing an error in the JavaScript console, but often, the interface becomes unresponsive or displays a blank screen instead.
00:19:12.840 During these moments, the built-in browser dev tools, specifically the network tab, become invaluable. This section allows us to inspect all network activities, ensuring that our application requests and responses are functioning correctly. By viewing the logged requests and responses, we can more easily identify the root of errors.
00:20:05.840 For example, each row in the network activity log represents an HTTP request, showing the request's name, status, and type. Requests with errors, such as 500 or 403 status codes, will appear in red, providing immediate visual feedback of issues. Clicking on a request allows me to delve deeper into the details of that HTTP transaction, which is vital for accurately diagnosing issues.
00:20:49.560 Additionally, checking the server logs provides similar insights from the server side. Here, we can see how the server resolves incoming requests, logs exceptions, and potentially identifies any anomalies. Monitoring both the network activity and server logs allows us to be proactive in understanding our application's behavior.
00:21:42.320 This brings me to lesson number three: find a bug, write a test. Bugs are a normal part of the software development process, and as developers, we must ensure that any future code changes avoid creating regressions or repeating issues. Writing tests for edge cases allows us to prevent regressions by ensuring our code behaves as expected.
00:22:29.490 For instance, recently my colleague and I faced a situation where we needed to combine results from two database queries, one that returned a list and another that returned a single result. We wrote tests to cover various scenarios, and after merging the changes, we discovered a bug in the QA environment due to one of the queries returning no results. We quickly recognized that our method was not handling nil values appropriately.
00:23:25.930 To address this, we wrote a test to ensure that if one query returned no results, it would be excluded from the list we passed to the frontend, preventing any undefined errors. While we might not immediately know which lines to change, having well-structured tests guided us through the debugging process and indicated when our code worked correctly.
00:24:20.660 In our roles as developers, we often find ourselves killing bug after bug, fixing existing issues almost as frequently as we write new lines of code. Developing good debugging habits is as critical as writing clean, efficient code, if not more crucial. The anxiety of not knowing how to locate a variable that unexpectedly held a nil value only amplifies the challenges of coding.
00:25:06.490 Fortunately, through pair programming and collaboration with experienced colleagues, I've gradually built my own debugging toolkit, gathering lessons from each team member. This continuous learning process has helped fill the knowledge gaps I’ve recognized within myself.
00:26:00.720 Ideally, coding boot camps would emphasize teaching debugging techniques, preparing graduates for their first jobs by preventing bad habits. However, the challenge lays on us to share our knowledge and openly discuss areas where we can improve.
00:26:41.390 Being aware of what we don’t know allows us to focus our efforts on learning the skills necessary for success in our roles. As a concluding thought, I'd like to revisit a tweet from Tenderlove that I've found insightful. It read, "It's not a bug; it’s just taking the code path less traveled." While he may have made a joke, the perspective resonates—it reminds me that errors and bugs are simply edge cases we haven't considered yet.
00:27:42.680 We shouldn't feel intimidated or view ourselves as failures due to errors that arise during our coding journey. I hope that after our time together, you feel better equipped to confront your next bug or error. Thank you!