Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Bridging the Knowledge Gap - Debugging

13 views

Published on

Bridging the Knowledge Gap - Debugging
Talk given at RubyConf 2019 in Nashville, TN

Published in: Technology
  • Be the first to comment

  • Be the first to like this

Bridging the Knowledge Gap - Debugging

  1. 1. RubyConf 2019 @minar528 DEBUGGING BRIDGING THE KNOWLEDGE GAP: Hello. Thank you all so much for being here today! First before we begin, I have included my twitter handle in the upper right, if you do that. So the title of my talk, in the program, is Bridging the knowledge gap of debugging. That’s really just a fancy way to say…
  2. 2. RubyConf 2019 @minar528 DEBUGGING WHAT I WISH 2018 ME KNEW ABOUT This is a talk about what I wish 2018 me knew about debugging. So to start… a little introduction.
  3. 3. RubyConf 2019 @minar528 WHO IS MINA?! ๏ She/Her/Hers ๏ Bootcamp grad ๏ Software Engineer at Tandem First off, a little bit of introduction: My name is Mina and my pronouns are she/her/hers. I graduated from a coding bootcamp in April of 2018. I am currently a software engineer at Tandem, which is a consulting agency in Chicago. And fun fact: <<next>> I’m very nervous.
  4. 4. RubyConf 2019 @minar528 WHO IS MINA?! ๏ She/Her/Hers ๏ Bootcamp grad ๏ Software Engineer at Tandem ๏ I’m very nervous 😱
  5. 5. RubyConf 2019 @minar528 🥺 If you were in a career for many years like me, starting a new job in a new field is scary enough. I was transitioning from a 10-year career in theatre, where I was the expert at my job, to a new industry and a team where everyone knew more than me. Sure, I knew I was going to be the most inexperienced developer at my entire company; but I was ready…
  6. 6. RubyConf 2019 @minar528 ...to face the challenges of this new career head-on. I was mentally prepared to put a lot of energy into learning and improving and becoming the best developer I could, but I wasn't prepared to discover just how much I didn't know.
  7. 7. RubyConf 2019 @minar528
  8. 8. RubyConf 2019 @minar528 ❓❓ ⁉ I quickly found where my skills were lacking: I worked inefficiently and was at a loss when it came to doing things like testing and debugging, so I was lost a lot, because… let’s be honest, we spend most of our time squashing bugs. Unfortunately, I don't think my experience was all that unique…
  9. 9. RubyConf 2019 @minar528 MAJORITY OF CODING BOOTCAMPS LEAVE GRADUATES WITH KNOWLEDGE GAPS A majority of coding bootcamps leave graduates with knowledge gaps in essential skills like debugging and a lot of us struggle at our first jobs because of it.
  10. 10. RubyConf 2019 @minar528 When I first started learning to code, seeing an error pop up was so scary. It was my code telling me that I did something wrong or that I made a mistake. And since we've all been conditioned to avoid mistakes, it felt like a failure; the big red error messages reminded me of the big red X's my teachers used to put on my tests In school. They were intimidating me into inaction and as an attempt to avoid them, I just didn't run my code as often, because...
  11. 11. RubyConf 2019 @minar528 you can't get error messages if you don't run your program. I would write code in the bubble of my text editor and stare at screen wondering if it looked right. The only indication that I had of rightness was my very limited knowledge about the programming language I was working in and maybe an example or two. So as you can imagine, I wasted hours upon hours trying to make things “right” before I would execute any of it. When I eventually let the program run, it of course, didn’t work. And without having been taught to debug or seen anyone demonstrate it, I didn't have any idea how to approach it. I didn't know how to read the error messages, or that even I should read them. My only approach was...
  12. 12. RubyConf 2019 @minar528 👀 ...staring at the code and combing through it line by line,
  13. 13. RubyConf 2019 @minar528 & 👀 ...**hoping** to find any typo or obvious mistakes. Noone told me that there were better ways and I practiced myself into bad debugging habits without even knowing it. Don't get me wrong. I'm not saying that bootcamps don't produce quality developers; In fact, more companies should hire career changers and bootcamp graduates, because we bring past experiences and soft skills that are harder to teach. But that's a 40-minutes talk for another day.
  14. 14. RubyConf 2019 @minar528 RUBYCONF 2020? REMEMBER TO HYDRATE!!! There is only so much you can cram into someone's brain over the course of three months.
  15. 15. RubyConf 2019 @minar528 So I understand that coding bootcamps have to make compromises in their curriculum. They usually end up focusing on things like syntax and super basic practices like assigning variables, making loops and defining methods. Testing and debugging get shoved to the side.
  16. 16. RubyConf 2019 @minar528
  17. 17. RubyConf 2019 @minar528 ➡ After I finished bootcamp and started working, it was quickly apparent that there were better ways to fix bugs. I had to change what I was doing, staring and hoping, but I didn’t know what to replace it with. The part of my brain where knowledge about debugging should live was a big empty gap.
  18. 18. RubyConf 2019 @minar528 I had to overcome it by filling it with proper tools and strategies. <<REMEMBER TO HYDRATE>>
  19. 19. RubyConf 2019 @minar528
  20. 20. RubyConf 2019 @minar528 ( At Tandem, we pair program all the time and pairing gave me an opportunity to observe a more experienced developer at work.
  21. 21. RubyConf 2019 @minar528 ( 👀 I have a really supportive team that lets me openly confront what I don't know and encourages me to ask questions. We would break things together and fix them as a pair. It gave me exposure to how different people like to approach bugs and errors, and the different options available to me. For instance, Shamyle is good about using the network tab in the devtools and Sasha is a wizard in the Rails console. Someone really smart said once that in software, you have to break everything first...
  22. 22. RubyConf 2019 @minar528 ...before you can piece it back together and make it all work.
  23. 23. RubyConf 2019 @minar528
  24. 24. RubyConf 2019 @minar528 <<BEAT>> This applies to learning as well: Because…
  25. 25. RubyConf 2019 @minar528
  26. 26. RubyConf 2019 @minar528 IF WE CAN'T ADMIT WHAT WE DON'T KNOW, WE WON’T LEARN IT. …if we can't admit what we don't know, we won't learn it. Having a supportive work environment allows me to identify my knowledge gaps so I will know when I see something that I want to store away in those spaces, and that can be a new approach to a problem or a new tool. And what I want to achieve in the next 30 minutes is to share with you the more important lessons I learned about debugging over my first year working in software. I'm not trying to turn any of you into Master Debuggers; we don’t have time for that and it’s not the point of this talk. But if you walk away with even one thing that you didn’t know when you came in the door, I would have done my job well. I’ve organized the things I learned about debugging into three main categories of lessons.
  27. 27. RubyConf 2019 @minar528 SO YOU WANT TO DEBUG A RAILS APP… ๏ Look under the hood ๏ Tap the phone lines ๏ Find a bug, write a test Lesson number one: "Look under the hood" Lesson number two: "Tap the phone line" Lesson number three: "Find a bug, write a test" These are not meant to be roadmaps, but rather starting points where we can begin the process of debugging. Depending on the issue at hand, we can use one of the three, mix and match them, or go another route altogether. All the examples in my presentation are done in Rails, but other languages and frameworks generally have equivalent tools and the core ideas are transferrable. So, onto lesson 1, “Look under the hood”
  28. 28. RubyConf 2019 @minar528 🔎 💻 Ok... I was maybe exaggerating when I said that I wasn't taught any debugging strategies at all. We were told to "console log everything", so I was aware that I can tell the program to print out certain things. We mostly used this to get a look at values of variables at runtime, compare these printouts to expectations to figure out where our code needed changing. So I knew about the concept of print debugging; in Ruby we use `print`, `puts` or `p` instead of `console.log`.
  29. 29. RubyConf 2019 @minar528 All three of these do similar things: output information from our program into the console. But where `print` and `puts` internally calls `to_s` to turn the thing we want to output into a human-readable String, `p` calls `inspect` internally, giving us more useful information as developers. For instance, if you take an ActiveRecord object, a puppy named Dottie.
  30. 30. RubyConf 2019 @minar528 <<WAIT>> Inside the Rails console here...
  31. 31. RubyConf 2019 @minar528
  32. 32. RubyConf 2019 @minar528 ...we can see the difference between using puts to output our Puppy object… <<play video>> …and using p. <<play video>> So depending on what is useful for the circumstance, it's important to know the difference between them, and remember what is available for us. In this case, we would opt to use puts if we needed to know which class our puppy belonged to, but p would give us more specific information about this particular instance of Puppy. Print debugging lets us leave breadcrumbs, in the form of these print statements, around our code and visually track the path of our program and look at actual pieces of data. We can watch and make sure that all the data is as expected and if an output we expected to see didn't show up at all, it's safe to say that our program never hit that section of code at all.
  33. 33. RubyConf 2019 @minar528
  34. 34. RubyConf 2019 @minar528
  35. 35. RubyConf 2019 @minar528 Aaron Patterson is a famously self-proclaimed puts debuggerer; a link to his blog post about it is included at the end of my deck, which I will share later. Most of the things that he covers in the post feels very much over my head, but he does make a powerful case for print debugging. Now, honestly, what we just talked about is the extent to which I use these print methods. I usually use something like a gem or library like Pry for peeking under the hood of my Rails application.
  36. 36. RubyConf 2019 @minar528 Pry is a gem that allows us to open up a console session at any given point of our program, just by dropping the line <<NEXT>>
  37. 37. RubyConf 2019 @minar528 binding.pry ... binding.pry into the parts of the code we want a closer look at; we call these breakpoints. So the next time we run the program, it would pause there and give us an interactive console in the terminal. That's why something like this is called interactive debugging. It serves largely the same purpose as using puts: to explore the inner- workings of our program. But Pry gives us more freedom in our exploration.
  38. 38. RubyConf 2019 @minar528 Puppygotchi concept by @feministy I have a simple app here, called Puppygotchi, that I built back in the day to try and teach myself Rails. It tracks the puppies that each user owns. These puppies get hungry or bored over time so users can log in to check on their kennel and feed or play with them. Inspired by Tamagotchi if you remember those. I have put a couple of breakpoints… <<highlight>> ...in the service object I use to age the puppies and reset their hunger and boredom level. We also have a few private methods that I have folded below the keyword private there at the bottom. If we reload the page here in the browser...
  39. 39. RubyConf 2019 @minar528 Puppygotchi concept by @feministy
  40. 40. RubyConf 2019 @minar528 ...we see the load catch and appear to be unresponsive. If we pop into our server logs, we can see the request from the reload hit the server and stop at our first breakpoint
  41. 41. RubyConf 2019 @minar528
  42. 42. RubyConf 2019 @minar528 Most things we can do in the Rails console are available to us when we're in a Pry session, but the gem also gives us a lot of extra commands to get more information about the program. One of the basic ones is ls. Using this command, we can get an overview of everything that is available to us within the current context, PuppyAgingService.
  43. 43. RubyConf 2019 @minar528
  44. 44. RubyConf 2019 @minar528 WAIT We can see from the output of our `ls` command that the PuppyAgingService has a public instance method, `process`, among other things, but it doesn't show me the private methods, because I don't have access to them from within Pry. A gem that is used sometimes side-by-side with Pry is pry-byebug, which gives us commands to navigate through the codebase. The ones I most commonly use are `next`, `step` and `continue`. So, we're still pry'd into the initialize method in PuppyAgingService here. We are stopped on line 4, and we can tell because of the arrow on the left of the line number. That line of code is telling our @puppy instance variable to point to the puppy object we passed in when initializing the PuppyAgingService and right now, it hasn't been executed yet. If we look at our instance variable puppy...
  45. 45. RubyConf 2019 @minar528
  46. 46. RubyConf 2019 @minar528 ...it has no value right now; it's `nil`... ...but if we use the `next` command, the program will run the next line, set the result to `@puppy` variable and stop at line 5...
  47. 47. RubyConf 2019 @minar528
  48. 48. RubyConf 2019 @minar528 We can then see @puppy has a value now.
  49. 49. RubyConf 2019 @minar528
  50. 50. RubyConf 2019 @minar528 So at this point, if we have looked at a couple of variables and ran a few commands, our terminal can get a little bit messy. We can clean it by up clearing the display and use the command whereami to help us reorient ourselves in the Pry session.
  51. 51. RubyConf 2019 @minar528
  52. 52. RubyConf 2019 @minar528 Then from here, if we want to have a look into the change_time method, whose result is being set to the other instance variable, @time_elapsed, we can use another command, step, to step into this next method call.
  53. 53. RubyConf 2019 @minar528
  54. 54. RubyConf 2019 @minar528 …this lands us into the definition of `change_time`, which is a private method defined on the PuppyAgingService class. Sometimes `step` will take us into Rails source code or the source code of some external library we're using. It's important to remember not to panic when our pry session in the middle of unfamiliar code; in fact, that’s kind of the point: to dig deeper into the program. The last navigation command I want to demonstrate is continue, which will tell Pry to continue running the program until it hits the next breakpoint, which remember we have in our public instance method, process.
  55. 55. RubyConf 2019 @minar528
  56. 56. RubyConf 2019 @minar528 And when we're done with the session and want our program to carry out the rest of the actions, we can use exit to get out of the Pry session.
  57. 57. RubyConf 2019 @minar528
  58. 58. RubyConf 2019 @minar528 Being able to peek under the hood of my program was a game-changer! The hardest part of learning to code for me was overcoming the disconnect between the text in the files and what shows up in the browser. It's especially hard to conceptualize when working with a dynamically typed language like Ruby, where we can assign data of any type to the same variable without any warning about potential problems. Has anyone seen an error message like this before?
  59. 59. RubyConf 2019 @minar528
  60. 60. RubyConf 2019 @minar528 undefined method ‘select’ for nil:NilClass The actual method name in the message changes, but this error and I are close friends... I see it all the time! If you're unfamiliar, this message is telling us that the program wants to invoke a method, select, <<highlight>> …on something whose value is nil. <<highlight>> In this particular example, select is a built-in method on the Array and Enumerable classes, so if the object it's being called on is anything but an instance of either class, the method call results in this error... and it's a message we'll see every time we try to call a method on something has the incorrect data type.
  61. 61. RubyConf 2019 @minar528 undefined method ‘select’ for nil:NilClass
  62. 62. RubyConf 2019 @minar528 undefined method ‘select’ for nil:NilClass
  63. 63. RubyConf 2019 @minar528 Since there are many answers for where, how and why the data is unexpectedly nil, opening up the program using puts or Pry is a decent place to start this debugging process. We can use these tools to identify the problem spots and follow the clues from there to the solution.
  64. 64. RubyConf 2019 @minar528 ☎📡 Lesson number 2: Tap the phone lines. I work at a consulting agency where all of us are polyglots; the project I am currently working on has a Rails API with a React frontend. Testing features in the browser as we write code is fairly standard practice in our typical development flow. Usually, when things aren't working properly, the best case scenario is that we'd see some kind of error in the javascript console, but many time, that wouldn't be the case; all we'd see is an unresponsive UI or blank screen. Something that's really useful in these cases is built right into the browser. If we open up the devtools, we can see all these tabs:
  65. 65. RubyConf 2019 @minar528 Elements, <<highlight>> which is your HTML and styling… <<highlight 2x>> ...your Javascript Console. <<highlight 2x>> For me in the Chrome dev tools, the fourth one over is a tab labeled Network. This tab will show us a log of our network activities. It’s something we would use to make sure that resources are being downloaded or uploaded correctly and where we would go to check on the status of our application's trips to and from the server. <<unhighlight>> When we first open up the dev tools, you won't see any network activities logged here yet. Let’s go back to Puppygotchi again, with the dev tools open and we can see that the network tab is empty. Once it’s opened up, we can reload the page or repeat the action and watch the logs populate with all the network activities. <<play>>
  66. 66. RubyConf 2019 @minar528
  67. 67. RubyConf 2019 @minar528
  68. 68. RubyConf 2019 @minar528
  69. 69. RubyConf 2019 @minar528
  70. 70. RubyConf 2019 @minar528
  71. 71. RubyConf 2019 @minar528
  72. 72. RubyConf 2019 @minar528
  73. 73. RubyConf 2019 @minar528 <<WAIT>> Each row in the table at the bottom represents an HTTP request and the column gives you more information about each of the requests. The ones that I look at most often are the name of the resources, the status, which is the HTTP response code, and the resource type. Some lines will appear in red if there are bad requests, something like 500 or 403, which are internal server error and forbidden, repsectively.
  74. 74. RubyConf 2019 @minar528
  75. 75. RubyConf 2019 @minar528 When we click into one of these lines, we have access to more details like the request and response headers, and the response body.
  76. 76. RubyConf 2019 @minar528 These sections will let us look at the communications more closely; what I typically do here is check to make sure the requests are going out to server with all the information that the backend needs to properly process them. Under Headers, there's a request payload section that will tell us what information went out to the server, and we can see whether it's behaving as expected or identify where it's not.
  77. 77. RubyConf 2019 @minar528 On the flipside, there's a Response tab that will show us what came back from the server as a result of our request. In this case, it looks like our request resulted in an internal server error, because it raised an ArgumentError on the backend. This means that our UI sent a request that tried to invoke a method by passing in the wrong number of arguments. This demonstrates something that is often easy to overlook: that even though a bug manifests itself on the frontend doesn't necessarily mean it's something wrong with the frontend code. I have been tricked by this and spend a lot of time investigating the React code only to realize that it was actually a backend issue disguising itself as a frontend error.
  78. 78. RubyConf 2019 @minar528 REMEMBER TO HYDRATE!! We can also look for similar information from the server-side by peeking into the logs.
  79. 79. RubyConf 2019 @minar528
  80. 80. RubyConf 2019 @minar528 This is where the server will log information about each request coming in, how the server resolved the requests and the result of the requests. This is also where exception and warnings will show up and where the Pry session will open if the program hits a breakpoint. This is what the server logs looked like for that refresh we just did on Puppygotchi, where we saw the network logs in the browser. <<play>> PAUSE <<advance>>
  81. 81. RubyConf 2019 @minar528
  82. 82. RubyConf 2019 @minar528 Like with the network tab, we can see the HTTP request and which route it hits; <<highlight 2x>> ...the response status code; <<highlight 2x>> and the exception that was raised, <<highlight>> which we saw earlier is part of the response body. <<unhighlight>> Monitoring lines of communication using the network tab and server logs requires us to be familiar with our program. As we can see, the logs don't always tell us when something is misbehaving; they're definitely not always color-coded, so knowing what these requests and responses look like when everything runs properly will help us recognize when it doesn’t.
  83. 83. RubyConf 2019 @minar528
  84. 84. RubyConf 2019 @minar528
  85. 85. RubyConf 2019 @minar528
  86. 86. RubyConf 2019 @minar528
  87. 87. RubyConf 2019 @minar528
  88. 88. RubyConf 2019 @minar528
  89. 89. RubyConf 2019 @minar528 🐞 🕸 Finally, lesson #3: Find a bug, write a test. Bugs and errors are a normal part of software development, because developers are only human; but, we also want to make sure that any future changes we make to the code won't create regressions and result in the same bugs again. A lot of times, bugs exist because we failed to account for and write code to cover certain edge cases. Most of the time, it will run without issues, but ideally, our program will run seamlessly for as many end users as possible. Tests to cover the conditions under which our bugs show up will prevent that code regression and let us know when the bug has been fixed. Running the tests also gives us clues about how to proceed when looking for a solution.
  90. 90. RubyConf 2019 @minar528 STORY TIME! For example, recently, my pair Shamyle and I were working on something where we had to write a method to sort some ActiveRecord objects into a list to pass to the frontend.
  91. 91. RubyConf 2019 @minar528 / We had one database query that returned an ActiveRecord Relation, a list of Evaluations, and another query that returned one single Evaluation. The purpose of our method was to push the result of these two queries into one list, sorted based on certain attributes, so that the frontend will display them in the proper order. In our implementation of this method, we wrote tests to cover all the cases we could think of. With a fully green test suite and the UI revealing no unexpected behaviors, we created a pull request for the feature and, after the team had reviewed it, we eventually merged it into the codebase.
  92. 92. RubyConf 2019 @minar528 / ( A couple hours later, when Sasha was working in the QA environment, she found that the page we worked on was broken. <<bug!>> After a little bit of investigating, she concluded that the page couldn't render because one of those queries to the database returned nothing. Our method was adding a `nil` into the list of objects we pass to our frontend, but when it tried to display something that was undefined, everything fell apart.
  93. 93. RubyConf 2019 @minar528 / (🐞
  94. 94. RubyConf 2019 @minar528 (🐞 I ended up working with her to find a solution for the bug and the first thing she suggested we do was write tests to account for this particular state of the program. <<net>> Our tests basically said, "hey, if one of the queries comes back with no result, we expect it to be excluded from the list we pass to React!” We didn't know when we started debugging what specific lines of code we needed to change or what that change looked like. But, backed up by the tests, we would know right away when it's been fixed. Not only that, each time we tried a solution, we would run the tests, hoping that they would fail in new and exciting ways. Sometimes in our jobs as developers, it feels likes we're just killing bug after bug.
  95. 95. RubyConf 2019 @minar528 (🐞 🕸
  96. 96. RubyConf 2019 @minar528 REMEMBER TO HYDRATE!! We fix code almost as often as writing new lines of them and good debugging habits are almost as important as writing clean code. For the developer I was a year ago,
  97. 97. RubyConf 2019 @minar528
  98. 98. RubyConf 2019 @minar528 ❓❓ ⁉ …that was an anxiety-inducing idea. I could've written you method after method, but I had no idea how to find the variable that was inexplicably nil. Fortunately, I work with really smart developers with a lot more experience. Through pair programming, I was able piece together a little something from each of their strategies into my very own debugging toolkit. Little by little…
  99. 99. RubyConf 2019 @minar528 💡⁉ ❓ I'm gathering lessons to…
  100. 100. RubyConf 2019 @minar528 💡⁉ 💡 to fill up those knowledge gaps I have found.
  101. 101. RubyConf 2019 @minar528 💡💡 💡 In a perfect world, bootcamps would put more emphasis on teaching the techniques and practice of debugging; it would prevent their graduates from learning bad habits and prepare us better for our first jobs. But, as is, we can help each other by sharing what knowledge we do have, and admitting openly about what we do not. Because I knew early on, "hey, I don't know what to do when something breaks!", I was able to focus on and learn the lessons I shared with you today. I'd like to go back to Aaron again here to close this talk. I found a tweet the other day, that he had pinned from back in January.
  102. 102. RubyConf 2019 @minar528 He said: ”It's not a bug, it's just taking the code path less traveled.” Knowing Tenderlove, he was probably making a joke, but this actually made me feel more at ease about debugging. Because when we frame it in this way, errors and bugs are just edge cases that we haven't considered yet. So we shouldn't be intimidated by them.
  103. 103. RubyConf 2019 @minar528 PAUSE And, I hope after the time we've spent together, you feel more equipped to confront your next bug or error. Now, go away and break your code!
  104. 104. RubyConf 2019 @minar528
  105. 105. THANK YOU! ๏ Twitter: ๏ https://minaslater.blog ๏ madeintandem.com We are hiring! 🎉 RubyConf 2019 @minar528 Thank you!
  106. 106. THANK YOU! ๏ Twitter: ๏ https://minaslater.blog ๏ madeintandem.com We are hiring! 🎉 RubyConf 2019 @minar528
  107. 107. RubyConf 2019 @minar528 RESOURCES ๏ “I am a Puts Debuggerer” by Tenderlove ๏ Pry-byebug Docs ๏ Inspect Network Activity in Chrome DevTools

×