 Hello, everyone. Good afternoon. Welcome to debugging effectively. And thank you for coming out. For those of you who don't know me, you haven't seen me before, my name is Colin O'Dell. And it also helps if I turn on my presenter. I am a lead web developer at Unleashed Technologies, where a web and hosting firm over in the US, just an hour north of DC. You might know me from my PHP League projects. I currently maintain the League's Common Mark parser, as well as the HTML to markdown converter. Or you might know me from my PHP 7 Upgrade Guide ebook. Something else I don't put as a bullet point on here, but I probably should, is that I'm a really good debugger. Whenever there's a really challenging problem that I'm faced with, usually nine times out of 10, I can figure out what that solution is. It's not because I'm particularly good at that particular platform I'm looking at. It's just that over time, I've kind of built up these really nice debugging skills. And I wanna share some of that information with you today so that you can become effective debuggers as well. So to accomplish that, we're gonna go through a few different parts in this presentation. We're gonna start off by talking about the importance of debugging. Why are we all here spending an hour learning about debugging, and why it's good to be an effective debugger? From there, I'll actually share the debugging process that I personally use, it's a five step process that I've kind of evolved over time that seems to work pretty well in most situations. I'll also touch on some specific tools and techniques that you can use when you're debugging different types of problems. And then of course, if time permits, I'll open it up for some questions and answers. So let's think about debugging. If you had to choose one word to describe what debugging means to you, what word might you describe? You might say, debugging is hard. Debugging is time consuming. Debugging is difficult. Debugging is annoying. If you like to solve puzzles and you like problem solving, like myself, you might say debugging is fun. Debugging can be a fun challenge. But I think debugging is important. More than anything, I think debugging is the single most important skill in programming. Now it's a very bold statement. Do I have some data to back that up? I do. If you think about how you spend your time as a developer, you might break down your time like this. You might say, okay, I spend maybe a third of my time planning things. Talking on Slack, sending emails, meeting with the shareholders and the stakeholders, triaging issues, things like that. And then the other two thirds of my time, I spend coding. But do you really spend two thirds of your time coding? According to the Mythical Man Month, you don't. You actually spend up to half of your time testing and debugging. You spend one sixth of your time actually developing new code. So one sixth of the time, you're in your ID typing out that new function or that new action that you need to write in your controller. And then the other half of the time, you're debugging why it doesn't work. You're writing unit tests for it, writing functional tests, things of that nature. So if we're spending up to half of our time testing and debugging, surely that's an area that it's a good idea to become efficient at. Now I like to put a good definition to the word debugging when I do this talk. And I really like this one from Wikipedia. Wikipedia says that debugging is the process of finding and resolving bugs or defects that prevent correct operation of computer software or a system. And I think the word process here is really important. It's not something you do haphazardly or randomly. When you have an issue, you don't jump from this file to that file, from this theory to that one. You really need to follow a really strong logical process. Process really is the foundation of effective debugging. Being able to go from seeing the issue all the way through the resolution of that issue. Now as you go through this process more and more and the better you become at your debugging process, the more experience you're going to gain. You're going to gain experience with the code that you work on. So if you're a symphony developer, the more you debug symphony issues, the more you'll learn about symphonies form components and dependency injection and validation and things like that. You'll learn more about the code base that you're working on. So whatever your product or project happens to be, you'll learn more and more about the different aspects of that system. But you'll also gain experience with the tools that you use while debugging. Things like X-Debug and your IDE. Other tools like S-Trace. And as you gain more and more of that experience and you resolve more and more of those types of issues, you're going to develop your intuition. What I'm talking about here is not this like magical sixth sense. It's really more of a logical heuristic type of approach to seeing a bug and immediately knowing what that bug is about. So if you see connection timed out, you immediately know that's a networking issue. I need to check the firewall. I need to make sure my SQL's running on that server. Things like that. Your immediate reaction isn't going to be, oh, connection timed out, better chmod everything to 777. You kind of immediately know, oh, I need to look at these things. I need to confirm or disprove these certain assumptions first and then move on. So in order to get to this intuition, in order to build up that high level of intuition to become an effective debugger, all you need to do is just focus on the process. And once you have that solid foundation in place, that experience and intuition will come with time. So I'd like to talk a little bit more about that debugging process, specifically what that looks like for a more junior developer. Someone who's kind of new to development doesn't really have a lot of experience. Now, when a junior developer encounters some kind of issue, they're usually going to try the usual steps that have worked for them in the past to resolve different types of issues. So they'll do things like clearing the application cache. Maybe they'll delete the vendor folder and reinstall everything with Composer. Maybe they'll chmod the entire project to 777 on production. Probably not a good thing. But they're going to try it because it's worked for them in the past. Maybe in the past they had some kind of permissions issue and chmodding it to 777 worked and they didn't really understand why it worked, but they said, oh, well, if it worked before, maybe it'll work now. So let me try that again. Now, it's okay to go through some of these steps. Well, maybe not the last one, but it's okay to clear the cache and reinstall dependencies if that's really the correct solution or if you think it might be the correct solution. But if you are a junior developer and you do these things, I would caution you that once you complete these steps and you see that they work, don't just move on with your day. Think about why declaring the cache resolved this particular issue for me this time, whereas changing permissions didn't. Why did reinstalling my dependencies work when clearing the cache didn't? Try and understand what was that fundamental nature of the bug and why did this particular solution work? Why did other solutions not work? Now, if the usual steps don't work for a junior developer, the next thing they'll do is ask someone else. Maybe that's a coworker, maybe they'll ask someone on Stack Overflow, maybe they'll Google the question to figure out what the heck is going on. And when they do these things, they'll get a bunch of different things to try and they'll go through them. They'll see, okay, first and second ideas didn't work. Third one did work. Awesome, I'm gonna commit this and move on with my day. Again, totally fine to go ask for help. Totally fine to try out different solutions. But when you find one that works, try and understand why does it work? Why does this solution work whereas this one didn't? What was it about this bug that made this solution apply to it? I think another kind of logical trap junior developers can fall into is what's known as the XY problem. So they'll be faced with some problem X. Maybe it's a bug that's cropped up. Maybe it's some new feature they need to implement. And they'll think, okay, how do I solve X? They'll think about it and then they'll realize they don't really know how to do X. Maybe they don't get the right answers from their colleagues on how to do X. So they'll say, okay, I don't know how to do X, but I think solution Y might work. And then they'll go out and ask, how do I do Y? The problem with this is that you're looking for a workaround when you do this. You're not looking for the proper approach. You're looking for a workaround to apply and you're gonna leave that in place and move on with your day. The problem with workarounds is that they're improper solutions. You're not actually solving the problem you want to solve. You're applying a workaround. And as you'll see in a moment, applying these kind of workarounds and improper solutions can cause issues later. Now at this point, I'd like to share a personal story. It's a little embarrassing, at least professionally speaking, but about seven or eight years ago, I was a junior developer and I got my first very big Magento project. I don't remember what version of Magento it was, but it was an old one. It was for a restaurant that sells seafood online and they ship it overnight on ice packs. And so as part of this custom implementation we needed to build, we needed to add the ability so that when the administrator went to process the order, Magento would go out to the FedEx website, go to FedEx web services and ask them for a shipping label and say, okay FedEx, we need to generate a shipping label that we can print out, stop on the box and then send that off to the customer. And I was able to figure out how to implement that functionality. It didn't exist in that time in Magento. It wasn't built in. I had to figure out how to build that integration, go out to FedEx, get the data, put it in PDF, serve the PDF out to the administrator as a junior developer. And I was really proud that I was able to figure that out. And it worked for several years. So that was one of the things I needed to implement. The second thing I needed to implement was a workaround. Basically, the backend of Magento was not compatible with IE9 at the time. Worked perfectly fine in Internet Explorer 8. For some reason there were just all these JavaScript errors popping up. And so to resolve that, I know I needed to add this little bit of HTML to every page in the backend of the website. So this was the proper solution. I knew I needed to add this to make it work. But the thing was I didn't know how to add this to every page in the backend of Magento. Being a junior developer, I didn't know what was the best way, what was the right way, what's the easy way, what's the hard way. So I thought, okay, well I don't know how to do this exactly, but I think I have an idea. What if I look for the pre-response hook? Just hook into that and then what I'll do is something like this. I will get the body and then use preg replace to look for a head tag. And when I find the head tag, I'll just inject this HTML. And surprisingly, this actually worked. So this solved the issue. The administrators were able to use Internet Explorer 9 to go process their orders. Life was great. Fast forward five years, the PDFs that were working just fine, stopped working. The admins would go to download the PDF. They tried to open it up and it would say it's corrupted. There was definitely data in the PDF. I confirmed that we were going out to the APIs and getting that data back. So I thought, okay, this is strange. Let me pop this PDF open in a text editor to see what's inside. You'll see right there, we have an HTML meta tag inside of this base 85 encoded data. Why did this happen? Well, it turns out that I was listening for every response coming from Magento, including when it served up static files. Okay, but aren't we looking for this head tag here? Well, we are, and it turns out base 85 encoded data can include characters less than H-E-A-D. So what lesson do we learn from this? Number one, don't parse HTML with regular expressions. You'll have a bad time. But more importantly, solve problems the right way. Even though this kind of solved the solution at the time and it worked for several years, it did cause an issue down the line. That was an issue that impacted the customer and they were not happy and we had to fix that for free. Lesson learned there. I think another trap that we can sometimes fall into is this form of magical thinking. So sometimes we'll see an issue and we'll think to ourselves, I don't know why this is doing this. Why is it behaving this way? For some reason, my code just isn't working right. This behavior just doesn't make any sense. And if you think these thoughts, you're really kind of thinking about the application, the computer as this magical black box with the mind of its own that does its own things. But that's not what a computer is. Computers are logical machines. They only know two things, ones and zeros, true, false, yes, no, on, off. There's no maybe value. There's no sometimes bit. It's yes or no, zero or one. It's Boolean, it's logical. There's no randomness to it. And if computers are logical, then the code that runs on them must also be logical, right? And if you have a bug in your code, a bug is just the defect in the code, but that bug and defect as a whole is still logical. So therefore, bugs must also be logical. They must have a logical explanation. Sometimes that explanation isn't immediately obvious, but there is always a logical explanation. You just need to be persistent enough to find it. And to that end, I really like this quote. It's from a guy named Nick Parlanti, who says, the bug is not moving around in your code, trying to trick or evade you. It is just sitting in one place, doing the wrong thing in the same way every time. When you fire up your debugger to figure out why something isn't working, if you're going through file A, when you're stepping through line by line, the bug isn't suddenly jumping into file B. And then when you go into B, it's not jumping out into file C. It's right there. You just need to be persistent enough to find it. Now, when I'm debugging issues, typically I always assume that my code is the problem. Because nine times out of 10, it is my code, that's the problem, my code has a bug in it. And so if I'm spending most of my time debugging, debugging my own code, my own issues, if a new issue pops up in some other part of the code base, it just makes sense from an efficiency point of view to assume that my code must be the problem because statistically speaking, it probably is. So instead of immediately saying, oh, Symphony's stupid, they must be doing something dumb, I'm totally using this the right way, I'll say, no, no, no, I'm gonna take a step back and I'm gonna validate that my code does indeed work the way I think it should work. And that I'm getting the right values out, that I have the right state, and I'll go through line by line and confirm that everything works as expected and that I'm not making any kind of faulty assumptions. Then, and only then, once I've confirmed everything works and I haven't made any untested invalid assumptions, then I'll go into the third-party library or the other parts of the code base and look at that to see if the culprit might be in there. Again, just trying to do things as efficiently and effectively as possible. So if you come across an issue, I would challenge you to assume that your code is the problem first. Start there, try and confirm whether or not your code does or does not have an issue. And if you're helping someone else out, assume that their code is the problem. You don't have to be confrontational about it. You don't have to say, sorry, Ben, you must be an idiot. Did you double-check this thing? You know, you can go about it in a nice way, but just make sure that they're not making any invalid assumptions that might be wasting their time. Try and help them get unstuck by confirming that their code does indeed work. So we've talked a little bit about the philosophy of debugging. Now I'd like to get into something a little more practical, which is the systematic approach that I take whenever I'm faced with an issue. There's five steps to it, and I'll go through each one in a little more detail. But at a high level, first step is to gather information about the issue, then replicate the issue, identify the culprit, fix it, retest it, and then mitigate future occurrences. Now, this isn't something that I just thought of one day. This is actually something that has kind of evolved over time. Right now I'm 28 years old. I've been programming since I was eight on an old Apple IIe. So over that 20 year span, this is just something that has evolved naturally. And it wasn't until I went to put this presentation together that I realized that I actually had this systematic approach in place that kind of evolved naturally. So this is what I'd like to share with you today. So step one, gather information. When you first come across an issue or you encounter an issue or your users encounter an issue, you want to gather information about what that bug is. The very first thing you should define is what is the expected behavior versus what is the actual behavior. I expect that when I fill out this form, hit this button, I'll get a green success message and this will be in the database. Actual behavior is I get a red message, the data is not on the database. Now you don't have to write that down if you don't want to. Just kind of get that in your head. What is the expected versus actual behavior? Some other useful information to gather. Number one, error messages. If you get a particular error message, error messages are nice. I love error messages because you can Google them or search your code base for them. And at least one of those two will give you a result. So if I get a certain error message and I Google the error message, I might get like a Stack Overflow post explaining, oh, this error message means this, you need to go check that part of your code. Or if I can't find any results on Google, I'll search my own code base and I'll see exactly where that exception was thrown. This particular string was thrown from this class in line 238. I can go right in there and figure out what's going on. So error messages are your friend. Log them, use them, they're awesome. Even better than an error message is a Stack Trace. A Stack Trace shows you the exact path of execution that your code took. It'll show you, okay, the error was thrown down here. This guy, this function was called by this one, which is called by this class, which is called by this. And you can kind of visualize in your head, okay, what path did the execution take through my code? Why was this exception thrown? If the issue is occurring on the front end, grab some screenshots. Screenshots are really nice because, especially if you get one of the full desktop, because it'll show you what browser has the issue, what operating system it is. It'll show you what information was in the form when it was submitted. If there is a form involved, it'll show you any kind of error messages. You have the URL bar. What's really nice about screenshots is that non-technical users can get these for you. You might get them in a Word document. You might get them faxed to you, but you can get screenshots from pretty much anyone. So those are really valuable as well. And then the last two things that are really nice to gather are the date and time that the issue occurred and log entries from around that time. There might be interesting tidbits in the log about what was going on at that time, maybe in the same request, maybe in a previous request. They'll kind of guide you to that answer. So once you've kind of defined those symptoms and you've gathered as much information as you can, the next step you want to take is to replicate the issue. You really want to be 100% sure that the steps you've identified to replicate the issue will in fact make that issue occur again. Let's assume that you aren't able to put together exact description of how to replicate the issue. Let's say you put together three steps and they say, okay, this only works half of the time. These three steps will only cause the issue to occur half of the time. Well, when you go to resolve the issue and retest it later, how do you know that things are working because you fixed them or things are working because it was that half of the time that things just magically work? You won't really know unless you're able to replicate the issue with 100% certainty. Now I know there are some edge cases. Maybe there's some kind of weird, I don't know, caching issue, some kind of, forgetting the word, race condition, that's the word. Maybe there's some kind of, you know, race condition going on where the issue only occurs if two people are signed in at the same exact millisecond and that can be hard to replicate. In those cases, do your best or at least try and identify in the code. Yes, this could happen when I take those steps. Now you can do this manually or automatically, whichever you prefer. But again, it's really important to be able to replicate this with 100% certainty. Now that we're able to replicate the issue and we know what the symptoms are, now we can go ahead and try and identify the culprit. And as we do this, we wanna be really methodical in how we go looking for that culprit. We want to assure that we're not making any fault the assumptions. We're not assuming that, oh, this part of my code base is working fine or oh yeah, that system, that's been working fine for years, no problems there. Don't make any assumptions. Challenge everything. And when you find the bug, try and understand the bug. Understand the fundamental nature of that bug. Why is it behaving in this way? Why is it affecting these systems and not these ones? Why is the bug doing what it's doing? Once you've identified the culprit, now you can go ahead and fix that issue. Once you fix the issue, retest. Go through those replication steps again and see if you're able to replicate the issue. And when you do try and put a solution in place, try to avoid that X, Y problem that I talked about earlier. Do not add any temporary workarounds. Temporary workarounds add technical debt. They could introduce other issues and quite often they never get replaced with a permanent true solution. Your temporary workaround is never going to be temporary. Personally, I work at a firm that makes websites for other clients. They pay us to make websites. My client is not going to pay me to go back and fix that temporary workaround that I put in because in their eyes, they already paid me to fix the issue and I fixed it. So that's going to stay in there. That technical debt, those other issues I've introduced are going to stay in the code base. So make sure that you're avoiding those kinds of temporary workarounds. And then lastly, the last step of this process is to mitigate future occurrences. Add an automated test. If you're doing TDD, great, add your test up front. And if you're not, that's fine too. You can add an automated test after the fact. That's totally okay. But mitigating future occurrences goes beyond just adding automated tests. It also means preventing other people or helping other people prevent those same kind of issues from occurring. So you can share your new knowledge with others. Maybe you came across this really bizarre issue that happened because you configured something a certain way. Go to the documentation, put a note, hey, watch out for this edge case. Make sure you're configuring it this way and not that way. Maybe it took you a couple hours to debug an issue and it was kind of a thrilling experience going throughout the code base and there was this like crazy answer to why everything was happening. You want to tell the whole world, awesome, go write a blog post. Go share that awesome story about how you debug that really, really difficult issue. And if you ask the question on Stack Overflow, it's totally okay to go back and answer your own question. And when you do that, don't just say, all right, solved it. Nevermind, don't need help. Go and list out what you did. Say this is the solution I implemented and this is why it works. That'll be really helpful to someone else who has the same issue in the future. They stumble across your post and see that great explanation that you've left behind for them. You've helped them to overcome that type of issue in the future. And then of course, if the issue was indeed in a third party library or a framework and you've confirmed that and you've written a patch, go ahead and submit that upstream so that other people can avoid that issue as well. So to recap that five-step process, number one, we gather information. Two, we replicate the issue with 100% certainty. Three, we identify the culprit. From there, we fix and retest. And then lastly, mitigate future occurrences. And by using this five-step process, I've personally seen some great long-term results. I think you will, too, if you are looking for something and you decide to adopt this. Number one, I've gained a lot of experience. I've gained experience with the different tools that I use, with the different code bases I've worked on. I've learned how different systems work from the inside out. I can tell you all about symphony's dependency injection, not because I wrote it, not because I've contributed to it, but because I've debugged it and I've debugged it several times. So I kind of have a good idea what's going on there. And I've also been able to build up this mental library of heuristics so that when I see an issue, that intuition just immediately clicks and says, yup, that's that kind of issue. That's networking issue. That's a race condition. That's a permissions issue. My mind just immediately jumps to that and I don't have to waste any time looking at other false scenarios. But perhaps most importantly, having a solid process like this will boost your confidence. I'm primarily a PHP developer, but lately I've been getting into Python. I've never taken a class on Python. I've never read a book on Python, but I know how to debug things. I know how to search Google. I know how to log things because I Googled it and I know how to put that in there. So I can work on Python code. Granted, I'm gonna be really slow at it. I may not write the best code, but I'm confident that I can write something that works. And you can use that too if you have that strong solid foundation, that solid process in place. All right, so I talked a lot about process. Sometimes there are certain types of bugs that this process maybe it'll work for, maybe it won't. There are a lot of really specific tools and techniques you can use when you're looking into different types of issues and different types of problems. So I'd like to share some of those specific tools and techniques with you today. Now there are two essential tools that I think every developer should have. A good IDE and an interactive debugger. Now some people will say, a good IDE, I don't need an IDE, I'm just as happy in Vim or Nano when that works for me. And if that's you, then awesome. Personally, that's not me, that's not most of my junior developers. For them, I'd like to set them up with the best equipment that they can have. The equipment that will get them to be more efficient at development and debugging. So in my opinion, a good IDE should have these as minimum features. It should have syntax highlighting, so you know if you forgot a semicolon or forgot a closing parenthesis. It'll have auto completion, so you don't fat finger a function name or accidentally do needle haystack instead of haystack needle. It'll have fast code navigation, so you can jump from one file to the next and see, okay, where is this class defined? Where is this variable being used to allow you to refactor your code quickly? And then of course, a good IDE should also have an interactive debugger integrated. I'm sure most of you know what an interactive debugger is, but just in case, an interactive debugger allows you to pause live code execution. So as PHP is running all the lines in your file, you can tell PHP, hey, stop on line 123. And you do that by setting a breakpoint. You can say, always stop on line 123, or you can say, always stop if this condition is true, if variable A is set to the value 42 or if B is false or whatever you need, which is really powerful. And then from there, once you've stopped the execution, you can step through line by line. You can say, okay, PHP, let's run the next line and see what happens. Let's run the one after that. And as you do that, you can examine the variables and see how they change over time. You can even explore the call stack. So if you're stopped in file C on some line, you can say, okay, I know this thing in file C was called from B. Let me jump back out to that point in time and see what was happening up there that caused C to be run. So interactive bugger, really awesome tool. You should definitely look into it if you don't use one today. Now in terms of techniques, there are six specific techniques I'd like to share with you. First one is tracing backwards from a known issue. After that, we'll talk about tracing forwards from the start and simplifying that with a binary search approach. I'd also like to share some more specific tools that are useful in very specific problems. I'll also talk about getting help and taking a break. So this is the final section of the talk and I'll go through these in a little more detail right now. So the first technique here, tracing backwards. This technique is one that you would use with an interactive debugger like XDebug. And you use this technique when you know what line is throwing the error. So you know that line 42 in this class is throwing an error. I wanna trace backwards and see what led up to that happening. So I fire my debugger, set a break point on that line, run the code, and then what I want to do is establish context. I wanna figure out why is this line of code being run? What was the state of the application that caused this to happen? And from there, I wanna work my way backwards to figure out how I got to that point in time. So let's imagine that this is our call stack here. Function A calls B, which calls C, which calls a D. D is throwing the error, so we've set our break point in there and you can see because it's this reddish-orange color, I'm currently stopped in there. I've hit that break point. What I wanna do now is reverse this. So even though the code ran this way, I wanna look at it in the opposite direction. I wanna see, okay, what was going on at function D that caused this break point to be hit? All right, well, it was called from C, so why did C call D? What was the state of the application and the state of my variables at that time? Let me look at B and see what it was doing all the way up as far as I need to go to figure out how that happened. Why did my application get to that point where it threw an exception or threw an error? So again, you can use this any time the error is thrown from another location. Sometimes we don't know what is throwing the error. We just know that a problem exists. One approach you could use is to trace forwards. So kind of a similar thing. We're using our interactive debugger, but we're setting our break point on our very first line of code. Maybe that's our index.php or app.php file. And then we use either a debugger or some kind of logging mechanism to see which lines get run and what things are running correctly and where either execution is suddenly stopping or throwing an error. So set the break point in A and then we see, okay, yep, that called A, let's see, okay, called B, called C, and then it called D. The problem with this approach is that it's very, very inefficient. You can have hundreds of thousands of lines of code that you have to step over, step into. It can be really time consuming. Sometimes this works well, but usually not always. If you don't know exactly where it is and you're finding that this isn't giving you a quick answer, you'll probably want to use like a divide and conquer type strategy. Basically what you want to do is to think about your code as this, how should I say this? This like grouping of different components that interrelate. So I have like these components that do their own thing and then they talk to these other components. So identify what are the different sections of your code? Where do they communicate? Where does one of those boundaries? Where does the data cross those boundaries? Set break points at those boundaries. The idea here is we want to narrow down the problem to one of those specific components or sections of code. So let me show you an example of that. Let's say this is a diagram of our system and what the rectangles and diamonds represent doesn't matter. But hopefully you can see that it's broken out into these different sections. So there's this blue section where everything kind of talks to itself but then it also branches out like this. These are the boundaries here. There's a boundary there, couple boundaries there as well. Now since we don't know where the error is coming from, let's just set break points at these different boundaries, run our code and at first let's just see what boundaries are hit when we execute the code. So we try and replicate the issue using those steps we've identified. Maybe we see that only these two break points were hit during the execution of our code. So that tells us the code took some path like this. It did not go through this code at all. This code therefore is probably not a culprit. Okay, so if we have these two boundaries here, let's keep the break points there, run our code again and see what's going on at that time. And maybe we'll see that when the code passes along this boundary, everything is great. All of the variables are set correctly. We have the correct state, everything looks good. But when the execution goes through the end of this little red section here, something's wrong. So now we've narrowed down the problem to this little bit of code here. And now we can trace forwards through this code to see what was happening here, what was happening here, what was happening here and so on. So we're not really wasting our time going through the other sections of our code base. So you can use that process as well. Now there are also lots and lots of really great tools you can use for specific instances and specific types of issues. Now I could show you hundreds of different tools, unfortunately I don't have the time for that, so I'm gonna share some of my personal favorites and some that you may not necessarily be thinking of. Starting with variable dumps. Good old var dump. Now sometimes var dump can be useful. If you're experiencing some kind of issue and maybe Xdebug isn't working, fire up var dump, just drop var dump somewhere in there. Don't commit it, don't push that to production, but you can use var dump. If you need nicer output, you can use library kint, which is really nice, it formats it nicely in your browser. Or maybe you just need to log data. Maybe you need to log data in production about why an issue is occurring. I actually had an issue a couple months ago. I could not replicate it on development. I needed more information about what was happening. So I just added a bunch of logger debug statements and just said, okay, logger, I need you to just save this away in case the issue happens again, I can go back and reference it. So these are totally valid techniques. They can be very useful, especially if you can't use a debugger to debug the issue. Another nice tool to have, particularly during development, is a debug toolbar. This up here is a screenshot of symphony's debug toolbar, the newer one. And down here you can see one that was built for Drupal, which was based off the older symphony's debug toolbar. These toolbars give you a lot of really great information about what the webpage is doing during the development process. So we can see here, when this page was loaded, we had a 200 response code. Here's the controller that was being run. Here's the class, here's the route name. It was forwarded. Here's the execution time, memory usage, how many strings were translated, who were logged in as, how much IO there was, how many database queries there were, how long they took. And you can click on these and expand into them and see, okay, the database query to grab the users is taking this long. There's a lot of great information here that can help you narrow down the source of some common issues or avoid them to start with. So I highly recommend using a debug toolbar if your system has one. Another useful tool is the console utility. Symphony and Laravel both come with built-in consoles. If you're using Drupal and Regento, there are third-party ones you can download. And they'll let you do things like clear the cache, enable and disable things. Some of them will even bootstrap the platform for you and drop you into a PHP shell. So you can just start putting in your Drupal code or your Laravel code to test out different things. It bootstraps the entire environment, so it's just like you put it in a test controller and ran it. Console utilities are awesome. Now sometimes the types of issues we deal with aren't necessarily errors or warnings or exceptions. They're just performance issues. Things are going slowly. Maybe there's a bottleneck somewhere. Maybe something is hogging all of your resources. Maybe there's this really inefficient loop somewhere. When you have a performance issue, in order to identify it, it's really helpful to have a profiler. Now there are a few tools that I do recommend, the first one being Blackfire. Blackfire is a freemium tool. Full disclosure, the company I work for is partnered with Sensio Labs. I'm not trying to sell you on this today, I just honestly think it's an awesome tool. And you can try it for free. They have a paid version if you want that. Blackfire lets you profile individual requests on development or production. So in your browser, you go to a page, you say, okay, I wanna profile this page, click a button, and it tells you what functions ran, how long they took, tells you memory usage, IO, all that great stuff. So Blackfire is great at profiling individual page executions, telling you what's going on. But if you need kind of a bigger picture idea of how is my application performing over time in production, I'd probably point you to New Relic instead. New Relic is also a freemium tool. I'm not partnered with them, again, I just love their product. And it gives you that kind of high level picture by Google Analytics does. Google Analytics will tell you who's going to what pages, that sort of thing. New Relic will tell you how fast or slow different pages are. And then of course, if you prefer open source solutions, you can use XH prof to do your performance profiling. So now we get to one of my favorite tools, Git bisect. I'm going to assume that pretty much everyone here knows Git every time I ask while the hands go up. But how many people are familiar with Git bisect? Oh, a good number of hands. All right, awesome. So Git bisect is this awesome tool and it runs a binary search through your commit history to help you figure out where a bad commit was introduced. Let's say this is a visualization of our history. And we know that at version 1.7, everything worked, our tests were passing, life was great. But now at our current head on our master or develop branch, tests are failing, something's wrong. There's a bug in the application. Whatever the case may be, we know that things are now bad, but they used to be pretty good. We don't know what commit introduced the issue and our efforts to find that are just not working. So what we can do is we can use Git bisect and Git will help us narrow down which commit introduced the issue. What we do is we run a few commands. I won't cover them here, but I'll give you the general gist. You basically tell Git everything was great at this point in time, things are bad here. Git will automatically check out the commit in the middle and it'll ask you, what about at this point in time? Were things working or was there an error here? And maybe you tell Git, yeah, I just ran the thing in the browser and the error is definitely present at this point in time. So this commit is bad. And then Git will say, okay, well, if things were bad at this point in time and they were bad here, then we can just narrow out, we can cross off all of those as being potential culprits. So now we've cut the problem space in half. Okay, so Git will check out the next commit in the middle. I'll check out this third one here and say, what about now, do things look good or are they still broken? And you might say, actually, things are working just fine at this point in my history. Okay, if things were working fine there and there, then that commit is probably good. So this commit is the first bad commit. It'll give you that commit hash right there. Now there's a couple of cool things beyond just what I showed you now. The first one being that this scales logarithmically. So if you're familiar with computer science and algorithms and big O notation, this runs in O log N time. So in this example, I had nine commits. I had to run four tests to figure out the culprits. If I multiply this number of commits by 10, the number of tests only goes up by a factor of two. So 90 commits takes eight tests. 900 commits takes 16 tests. So the more commits you have, the more efficient this actually becomes. The other cool thing is you can hook this up to something like PHP unit. So you can write a unit test or a functional test that'll run your code and either exit with a zero or one code. And you can just run that with your git bisect. So git bisect, instead of you needing to manually test your code, it'll just run PHP unit every time it checks out one of those potential commits. It'll automatically decide if something's good or bad. Wait a couple seconds and it'll tell you what the culprit is. So git bisect is awesome. Definitely recommend checking that out. Another nifty tool is Netcat. Netcat is a nice UNIX tool. You can test connections, actual TCP connections to different servers on different ports. So if you ever get some kind of connection timed out issue or connection refused and you're not really sure, okay, is that because I have the wrong MySQL user or is that because there's a firewall? You can use Netcat to help you narrow that down. The syntax is pretty straightforward. NC, space, dash vz, another space, hostname or IP, and then the port number. And you can see up here, we're testing if portedian local host is open. We can say, yep, connection succeeded. But down here, if we try to SSH into Google.com, connection times out, network is unreachable, they probably have a firewall up, which is a good thing, if you're a Google. Another nice tool is Curl. Curl is a command line HTTP client. If you want to get HTTP headers or make HTTP requests and you don't really want to do it in your browser, you can use Curl to do that. So here's an example where I pass the dash I flag and that's just going to show me all of the response headers here. So maybe I'm debugging some kind of caching issue. I can see all of these cache headers here. Here's the expires header, that kind of thing. So if I'm already in the command line already and I don't want to fire up my browser, Curl can be really nice. Now the last tool I want to share with you is one called the Strace. I am not a C developer, so I'll admit I don't fully understand this, but it saved my butt so many times, so I want to share that with you today. Strace shows you system calls that are being made by PHP. My understanding of this, and again, you're welcome to correct me if I'm wrong, my understanding is that anytime PHP needs something from the system or from somewhere else, it's going to initiate a system call to the Linux kernel. So if it needs more RAM, if it needs to open a file, if it needs to connect to my SQL, it has to ask Linux, hey, I need this stuff, please help me out with this. And that will occur, that'll be logged here in the Strace command. Now, again, I don't fully understand this, but if I look closely, I can see things that I do understand. Up here, I see set names UTF-8 and it's being written somewhere, so that looks like I'm sending data to a database. Down here, I'm opening up this crypt.class.php file. So I know that this PHP process is probably doing a require once or something like that on this file. Down here, here's a SQL query, select star from accesses. Okay, that looks like it's a database call. Down here, I'm opening dev slash random, so I'm doing something with random numbers. So this can be really useful, especially if you're trying to figure out why a process is running, but you're not seeing any results. Maybe you're doing some kind of, you know, large batch action on the website and it's on production. You don't really want to restart the server, but you're not sure if it's stuck or what's going on. Just attach to it with Strace and see what it's doing. And if you see, you know, it's doing a bunch of queries, you can kind of figure out, okay, I know what this process is doing. I know the process is doing what I expected to, but maybe the database is going slow, or maybe it's hit a deadlock. You can figure that out by Stracing it. So it's a really nice tool. Definitely recommend trying that out sometime. So that's it for the specialized tools. That kind of takes us towards the end of this list of techniques. The next one I'd like to share with you is getting help. Sometimes all of these different tools and all these techniques and all the experience in the world just doesn't get you the answers that you need. Sometimes you just need to get help. The first thing I'd recommend that you do is to go read the fantastic manual. Go read the fantastic documentation. Other developers have spent hours documenting how their projects work, how the system works. Go read that documentation. The answer might be in there. If you're dealing with a particular library or framework, go check the project forms. Go check the issue queue. Maybe someone else has reported that issue and maybe there's a patch you can apply or some solution you can use. And if not, post the issue yourself, say, hey guys, I'm having a problem with the library. Here's the simple code I'm trying to use. Here's the behavior that I'm seeing. Please help me. And chances are someone will help you out with that issue. You can of course go on Stack Overflow, IRC, Slack, Twitter, ask for help there. If you have colleagues, you can go ask them. If you're trying to debug a really difficult Laravel issue, go ask the resident Laravel developer. If you don't have one, just go ask any senior developer. A good senior developer should be able to at least get you going in the right direction even if they're not familiar with that particular technology because they have that good foundation in place. They know how to debug an issue. They know what kinds of issue you might be dealing with. They can kind of guide you in the right direction. And then of course you can do something that's called rubber ducking. Rubber ducking sounds kind of silly. You basically take a literal rubber duck, place it on your desk and if you have a problem you talk out the problem to the duck. So you say, all right, duck, I'm having this issue. When I submit the form, I'm getting this 500 error and I'm not seeing anything come in the logs, blah, blah, blah, and just talk out your problem to the duck. Now you'll look kind of silly to your coworkers. The reason you're doing this is to talk through what you've tried, what you haven't tried. Maybe as you're talking out what you've done to this little duck, you'll accidentally say, oh, I said I checked this, but I really didn't check it. Or maybe I'm assuming this thing that I actually did not verify. Or maybe it'll just jog your memory as you're talking that process out. Now you can use a literal rubber duck. You can use a coworker and say, hey, do you have a couple minutes? Can I run something by you? Whatever works for you. Just talking that problem out will sometimes help guide you to the right solution. And then the last technique is to take a break. Sometimes you may have this really difficult issue that you can't figure out. You've been at it for hours and hours and hours, and you're just not getting anywhere. Take a break. Step away from the computer. Clear your mind and come back to it with a fresh perspective, feeling refreshed, not spending 12 hours and then every hour after that you're only getting 10 minutes of good work out of it. Take a break. Taking a break is nice because it helps you forget any invalid assumptions. Maybe you spent hour after hour focusing on the cash layer. When the problem isn't with the cash layer, it's with the session handler. If you take a break and come back to it later, you'll start again from that high level and you'll say, oh, I didn't look over there. Let me do that. Of course, taking a break recharges your batteries so that you can continue putting time into the problem and that time you put in, you'll be getting something out of it. It won't be that you're wasting time. It's not, you know, you spend an hour, a clock time working on something, but you're really only spending 10 minutes because you're just brain dead. You're getting your full efficiency out of your mind. And then, of course, this gives you the chance for your subconscious to work out the problem. I'm sure many of you have been out for a walk or taking a shower or just daydreaming and then some idea pops in your head, something you haven't tried before. Now, I'm not saying if you have a hard problem, take a break, go take a nap because you'll get the solution right away. Doesn't work like that. But if you've spent all of this time just frustrated not getting anywhere, take a step back, give your subconscious the chance to bubble something up while you're recharging your batteries. That can be really helpful. So that wraps up my talk. There are four things that I'd like you to walk away from. Number one, computers aren't random and neither are bugs. There is always a logical explanation to the issues you're seeing. You just need to be persistent enough to find them. And that persistence will always pay off. Nine times out of 10, if you're persistent, you will find the source of that issue. That other 10% of the time, maybe you don't solve the issue, but you've learned more about how that framework or that library works. So don't be afraid to dive deep. It's a great learning opportunity. It's a great chance for you to become an expert in that system. And then lastly, don't make assumptions or take things for granted. Challenge everything. So at this point, I do believe we have a few minutes left. So if there are any questions, I'd be happy to take them. The last one? Question was what was the last one? And that is don't make assumptions or take things for granted. Challenge everything. Any other questions? I believe there's a question back there. At what point in the debugging process would you go to get bisect? Would you maybe start with that and find the commit that's causing the problem and then start debugging there or leave it until you found the problem and then go find the commit that caused it? That's a good question. Usually I'll try and just go through my normal debugging process up front. And if I spend, say maybe an hour and I don't have a good idea of what's causing the issue, then I'll go to get bisect. Especially if I know that things were working fine a week or two ago. Sometimes I'll also use get bisect if I found the issue. And I don't know exactly why the code introduced that issue. I mean of course I could do a get log to figure out when was that introduced. But sometimes get bisect gives me a good feel for at what point in time was that what was happening around that time. Maybe the commit that introduced the issue didn't really introduce the issue. Maybe it introduced the environment for the issue to crop up later.