 Thanks for coming to my first ever conference talk. Thank you. Before we begin, I just want to point out that I have the link to my slides in the top left there, if you wanted to follow along, and my Twitter handle is in the upper right, if that's something that you do. So the title of my talk in the program is Filling the Knowledge Gap, Debugging Edition. And that's really just a fancy way to say that this is a talk about what I wish my bootcamp had taught me about debugging. So first off, I'm going to do a little bit of introduction. My name is Mina Slater, and my pronouns are she and her. I graduated from a coding bootcamp in April of 2018, so about a year ago. I am currently a developer at a consulting agency in Chicago called Tandem. And it's my first time attending RailsConf. If I'm fact, if you can't tell already, I'm terrified. Cool. So if you were in a career for a long time like I was, starting a new job in a new field is scary enough. I was transitioning from a 10-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. Sure, I knew I was going to be the most inexperienced developer in my company, but I was ready to face the challenge of this new career head-on. Now, I was prepared to put a lot of time and 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. I quickly found out where my skills were lacking, though. I was inefficient and at a loss when it came to things like testing and debugging. So I was really at a loss a lot. And unfortunately, I don't think my experience was all that unique. A majority of coding bootcamps leave graduates with some knowledge gaps in essential skills like debugging and a lot of us struggle at our first jobs because of it. Now when I first started learning to code, seeing an error message pop up was really scary. It was my code telling me that I did something wrong or that I had made a mistake. And since we have all been conditioned to avoid mistakes, it felt like a failure on my part. The big red error messages reminded me of big red Xs that my teachers used to put on my tests in school. And they were intimidating me into inaction. And as an attempt to avoid them, I just didn't run my code as often because you can't get error messages if you don't run your program, right? So I would write code in the bubble of my text editor. I would stare at it wondering if it looked right. And the only indication that I had of rightness was the very limited knowledge that I had about the programming language I was working in. So as you can imagine, I wasted hours upon hours trying to make things right before I would run any of it. So when I eventually let the program run, it, of course, didn't work. And without having been taught to debug or seen anyone demonstrated, I didn't have any idea how to approach it. I didn't know how to read the error messages or that I should even read them at all. My only approach was staring at the code, comb through it line by line, and hoping to find any typo or obvious mistakes. No one told me that there were better ways. And I practiced myself into debugging, bad debugging habits without knowing it. Now 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-minute talk for another day. Now there's only so much that you can cram into someone's brain over the course of three months. So I understand that bootcamps have to make compromises and they usually have to focus on things like syntax and super basic practices like assigning variables, making loops and defining methods. Things like testing and debugging get shoved to the side. So after I finished bootcamp and started working, it was quickly apparent that there were better ways to fix bugs. I had to stop what I was doing, staring and hoping. But I didn't know what I was supposed to replace it with. So the part of my brain where debugging knowledge should live was a big empty gap. I had to overcome it by filling it with proper tools and strategies. At Tandem, we pair program all the time and pairing gave me an opportunity to observe a more experienced developer at work. I have a really supportive team that lets me openly confront what I don't know and they encourage me to ask questions. You will break things together and fix them as a pair. It also gave me exposure to how different people like to approach bugs and errors. For instance, Shamile is really good about using the network tab in the DevTools and Sasha is a wizard in the Rails console. Now someone really smart said to me once that in software you have to break everything first before you can piece it back together and make it work. And I think this applies to learning as well because if we can't admit what we don't know, we won't learn it. Having a supportive work environment allows me to confront 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 the course of my first year working in software. I'm not trying to turn you all into master debuggers. Honestly, 30 minutes just won't be enough for that. But I also want to give you a better place than I had to start. So you can go further in the course of that year. I've organized the things I learned about debugging into three main categories. Or lessons, if you will. Lesson number one, look under the hood. Lesson number two, tap the phone lines. And lesson number three, find a bug, write a test. Now, these are not meant to be roadmaps, but rather starting points where debugging can begin. And depending on the issue at hand, we can use one of the three, mix and match them, or just go another route altogether. So onto lesson number one, look under the hood. Okay, so 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. Mostly we use this to get a look at values or variables at runtime. We then compare these printouts to expectations to then figure out where our code needed to change. So I knew about the concept of print debugging. In Ruby, we use print, puts, or P instead of console log, like in JavaScript. Now, all three of these do very similar things. They output information from our program into the console. But where print and puts internally cost 2S to turn the thing we want to output into a human readable string, P costs inspect internally, giving us more useful information as developers. For instance, if you take an active record object, a puppy named Dottie. So inside the Rails console here, we can see the difference between using puts to output our puppy object and using P. So depending on what is useful for the circumstance, it's important to know the difference between them and remember what is available to us. Now, print debugging lets us leave breadcrumbs too 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. So Aaron Patterson is a famously self-proclaimed puts debugger. A link to the blog post about it is included at the end of my deck. Most of the things that he covers in this post feels very much over my head and honestly, I'm not a print debugger like him. I'm a pry debugger. And what we just talked about is the extent to which I use these print methods. But nowadays, my go-to tool for peaking under the hood of my Rails application is pry. Pry is a gem that allows us to open up a console session at any given point of our program just by dropping binding.Pry into the parts of the code that we want to get a closer look at. So the next time we run the program, it will 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 it probably gives us more freedom in our explorations. So I have a simple app here. It's called Puppy Gachi. I built it maybe a year and a half, two years ago when I was trying to teach myself Rails. It tracks the puppy that each owner, sorry, each user owns. And these puppies get hungry or bored over time so that users can log in to check on their kennel and feed or play with them. Now, it's inspired by Tamagotchi, if you remember what those are. I have put a couple of break points into the service object that I used to age the puppies and reset their hunger and boredom levels. We also have a few private methods that I have folded up below the keyword private there at the bottom. So if we reload the page here in the browser, we can see that the load catches and it appears to be unresponsive. So if we pop into our server logs, we'll be able to see the request from the reload hit the server and stop at our first binding pry. Now, most things that we can do in the Rails console are available to us in the pry session, but the gem also gives us a lot of extra commands to get more information about our program. One of the basic ones is LS. So using LS, we can get an overview of everything that is available to us from within the current context, which is puppy aging service. So we can see from the output that the puppy aging service 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 those from within pry. And a gem that is used a lot side by side with pry is pry by bug, which gives us commands to navigate through the code base. And the ones from that that I use most often are next, step and continue. So here we are still pride into the initialized method in the puppy aging service. We're stopped on line four and we can tell because of the arrow that's on the left of the line number. That line of code is telling our at puppy instance variable to point to the puppy object that we passed in when initializing the puppy aging service. And right now, it hasn't been executed yet. So if we look at our instance variable puppy, we can see that it doesn't have a value right now. It's no. But if we use the next command, the program will run the next line, set the result to app puppy and stop at line five. And we can then see that our puppy has a value now. Cool. So at this point, we've looked at a couple of variables. We've also run a couple of commands. So our displays are getting a little bit messy. We can clean this up by clearing the terminal and using where am I to help us reorient ourselves in the price session. So from here, if we want to have a look into the change time method line five, whose result is being set to the other instance variable, we have time elapsed. We can use step to step into this next method call. So you see that this lands us into the definition of change time, which is a private method defined on the service class. Now, sometimes step will take us into real source code or the source code of some other gem we're using. It's important to remember not to panic when price, the price session ends up somewhere unfamiliar. It's all just code so we can learn to read it in time. Now, the last navigation command I wanted to demonstrate is continue, which will tell pride to continue running the program until it hits the next break point, which remember we have in our public instance method process. Cool. So when we're done with the session and want our program to carry out the rest of the actions, we can then use exit to get out of the price session. So being able to peek under the hood of my program was a game changer for me. The hardest part of learning to code was overcoming the disconnect between the text and the editor 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 variables without any warning about potential problems. Now, has anybody run into this error message here before? Yeah, okay, as expected. The actual method name in this message changes, but this error and I are really close friends. I see them all the time. If you're unfamiliar with it, this message is telling us that the program wants to invoke a method select on something whose value is no. In this example, select is a Ruby built in method on the array and enumerable classes. So if the object that's being called on is anything but an instance of either of these classes, the method call results in this error. And it's a message we'll see every time we try to use a method on something that has the incorrect data type. Now, since there are many answers to where, how, and why the data is unexpectedly known, opening up the program using put 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 our solution. Lesson number two, tap the phone lines. So I worked at a consulting agency where all of us are polyglots. The project that I'm currently working on has a Rails API with a React front end. Testing features in the browser as we write code is fairly standard practice in our typical development flow. And usually when things aren't working properly, the best case scenario is that we would see some kind of error in the JavaScript console. But many times that wouldn't be the case. All we would see is an unresponsive UI or maybe a blank screen. Now, something that's really useful in these cases is built right into the browser. So if we open up the DevTools, we can see all of these tabs. Elements, which is your HTML and styling. Your JavaScript console. And for me in the Chrome DevTools, the fourth one over is the network tab. This is going to show us a log of our network activities. It's something that we would use to make sure that resources are being downloaded or uploaded correctly and where we would go to check the status of our applications trips to and from the server. When we first open up the DevTools, you won't see any network activities logged here yet. So let's go back to Puppy Gachi again with the DevTools 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. So 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 names of the resources, the status, which is the HTTP response code and the resource type. Now some lines will appear in red if there are bad requests, maybe a 500 internal server error or a 403 forbidden. When we click into one of these lines, we have access to more information like the request and response headers and the response body. These sections will let us look at the requests more closely. And what I typically do here is check to make sure that the requests are going out to the server with all the information that the back end needs to process the request. In the headers tab under an individual request line, 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. And on the flip side, there is a response tab that will show us what came back from the server as a result of our request as well. So in this case, it looks like our request resulted in a 500 internal error because it raised an argument error on the back end. Now this demonstrates something that it's really easy to forget, that even though a bug manifests itself on the front end doesn't necessarily mean it's something wrong with the front end code. I have been tricked by this and spent a lot of time investigating the React code only to realize that it's actually a back end issue disguising itself as a front end error. Now we can also look for similar information from the server side by peeking into the logs. This is where the server will log information about each request coming in, how the server resolved that request and the result of the request. This is also where exceptions and errors, warnings will show up and where the prize session will open if the program hits a breakpoint. This is also where server logs, this is what the server log will look like for the refresh we just did on Puppy Gachi where we saw the network tabs, where we saw the network logs in the browser. So like with the network tab, we can see the HTTP request which route it hits, the response status code and the exception that was raised, which we saw earlier is part of the response body. This is where it's really beneficial to know our programs inside and out. As you can see, the server logs don't always tell us when something is misbehaving. They're definitely not always color coded. So knowing what the server logs look like when it runs properly will help us recognize when it doesn't. So lesson number three, find a bug, write a test. Now bugs and errors are a very 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 any regressions and result in the same bugs again. Now a lot of times bugs exist because we fail to account for and write code to cover certain edge cases. Most of the time, the program will run without issue, but ideally, our program will run seamlessly for as many end users as possible. Tests to cover the condition under which our bugs show up will prevent that code regression and let us know when the bug has been fixed. So running the tests will also give us clues about how to proceed when looking for a solution. For example, recently, my co-worker Shamile and I were working on something where we had to write a method to sort some active record objects into a list to pass to the front end. We had one database query that returned an active record relation, a list of evaluations, and another query that returned one single evaluation. The purpose of our method was to push these results together into one list, sorted based on certain attributes, so that the front end will display them in the proper order. Now in our implementation of this method, we wrote tests to cover all the cases that we could think of. So with a fully agreeing test suite and the UI revealing no unexpected behaviors, we created a pull request for the feature. After the team had reviewed it, we eventually merged it into the code base. Now a couple of hours later, when another co-worker Sasha was working in the QA environment, she found that the page that we were working on was broken. And after a little bit of investigating, she concluded that the page couldn't render because one of the queries to the database returned nothing. Our method was adding a nil into the list of objects we passed to our front end. But when React tried to display something that was undefined, everything fell apart. I ended up working with her to find a solution for the bug. And the first thing she suggested that we do was write tests to account for this particular state of the program. Our tests basically said, hey, if one of the queries comes back with no results, we expect it to be excluded from the list that we passed to React. Now we didn't know when we started debugging what specific lines that we needed to change or what the change would look like. But backed up by the tests, we would know right away when it's been fixed. And not only that, each time we tried a solution, we would run the tests, hoping that they would fail in new and exciting ways. Now sometimes in our jobs as developers, it feels like we're just killing bug after bug. We fix code almost as often as writing new lines of them and good debugging habits are almost as important as writing clean code. So for the developer that I was a year ago, this was a really anxiety inducing idea. I could have written you method after method, but I had no idea how to find that variable that was unexpectedly no. But fortunately, I work with really smart developers with a lot of experience. Through pair programming, I was able to piece together little something from each of their strategies into my very own debugging toolkit. And little by little, I'm gathering lessons to fill up those knowledge gaps that I have found in myself. So in a perfect world, boot camps will put more emphasis on teaching the techniques and practice of debugging. It will prevent the graduates from learning bad habits and prepare us better for our first jobs. But as is, what we can do is help each other by sharing what knowledge we do have and admitting openly about what we do not. Because I knew early on that, hey, I don't know what to do when something breaks. I was able to focus on that and learn the lessons that I shared with you today. Now I would like to go back to Erin again here to close this talk. So I found a tweet the other day that he had pinned from back in January. It's not a bug, it's just taking the code path less traveled, he said. Now knowing tender love, and I don't know him that well, obviously, but 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 really be intimidated by them. And I hope after the time that we've spent together, you feel more equipped to confront your next bug or error. So now please go away and break your code. Thank you. Now, if I may have another minute of your time, I wanted to tell you a little bit about the company that I work for. The last time we were at RailsConf, we were known as DevMind. But we have since changed our name to Tandem. Now, Tandem is a design and technology consulting agency located in Chicago. As a company, we aim to create products and services that will affect the society and our community in a positive way. And having recently completed our apprenticeship program, I benefited firsthand from Tandem's dedication to hiring and growing talent. We use pair programming to support each other's advancement and maintain our quality of work. So if any of you are looking for work, we are hiring and if any of that sounds appealing to you, please do come talk to me. I would love to tell you more about the positions that we are currently looking to fill. Thank you again.