Prying Open the Black Box
CC Attribution - ShareAlike 3.0 Unported:
You are free to use, adapt and copy, distribute and transmit the work or content in adapted or unchanged form for any legal and non-commercial purpose as long as the work is attributed to the author in the manner specified by the author or licensor and the work or content is shared also in adapted form only under the conditions of this
Chan, Godfrey
When your code doesn't work the way you expect, it could be a frustrating exercise to figure out what exactly went wrong. In this session, we will walk through the process of debugging an Ember app. Along the way, we will learn about some of the tools and techniques that could help shed light on the behavior of our code. Finally, we will also take a brief tour into the Ember codebase to look for clues. Upon completing this orientation, you will be well-equipped to pry open the mysterious black box the next time your app misbehaves!
 How's it going? It's me. Well, I dressed up as my own emoji today. I could do this all day, but to be honest, I probably need that snack brick more than you do. I just didn't eat that much alone. I'm just not much of a barbecue person, you know. Anyway, I'm Godfrey. That's me. You can find me on the internet as Chanking Code. Before we begin, I've got to do the Snapchat. So if you cannot share with me, that would really help with the internet points. So are you ready? Good to go? All right, let's do this. Wait, that's, come on. Let's try it again. All right. Oh, my God. Welcome to Ambercon Canada. I'm so excited to be doing the closing keynote for Ambercon Canada. I work at a local company called Tilda. We make land yurts. But if you like our land yurts, you're probably also like our performance monitoring product. We have a booth outside. And if you like our land yurts, you'll probably also be into crypto currencies and you should check out our ICO. You can find it at Skylight.io slash fefficon.icl. This is what it looks like. Some day it's going to work a lot of money. But if you like money that much, you should also sign up for Skylight using this referral link. That way you and I both will be swimming in Skylight credits. Or you can just come to the booth and we have a $50 promo code there as well. And finally, if you maintain any open source Ruby apps, you should definitely come over and sign up for a completely free account. So with that, I'll do it. Let's talk about debugging. Before we dive in, I just want to set some expectations. If you are a new developer, I had you in mind when writing this talk, so I really hope I can help. If you have been programming for a while and Ember is your first or primary programming environment, this talk is also for you. I know sometimes debugging in Ember could be intimidating or even a little bit frustrating. So I hope to help reset your expectation of what debugging should be like. And at the end, I will mention why debugging in Ember might feel a little bit more difficult and what you can do about it. And finally, if you are an intermediate or advanced developer, then you probably already know everything I'm about to tell you. And this talk is probably not going to blow your mind. But I would like to invite you to follow along and think about how we can more effectively teach these things to new developers and what we can do to improve the debugging experience for everyone. So let's get started. I have been pretty obsessed with failures lately, specifically engineering failures and catastrophes. Not because I enjoy watching people suffer, of course, but I'm quite fascinated by the ways that we manage to analyze and learn from these mistakes that we make. A lot of things happened in 2017. However, one of the things that didn't happen was plane crashes, specifically crashes involving commercial passenger jets. You see, we flown over 4 billion passengers last year, which is more than half of the world's population, pretty mind blowing. And yet, there was not a single fatality in commercial passenger jet liner in 2017. That wasn't always the case, though. In the early days of A-Vision history, flying was a pretty risky business. And in fact, it was so risky that the early ML pilots called themselves members of a suicide club. They aren't wrong because it was later calculated that their average live expectancy was as low as 900 flying hours, which is roughly how many hours a commercial pilot fly every year today. And since those early days, we have invested a lot of resources into improving the safety of flying for everyone. And in the modern aviation days, every accident is very heavily scrutinized and investigated with the results of these investigations leading into new regulations and engineering improvements to prevent the same kind of failure from ever happening again. So every time you fly today, in some ways, you fly saver in parts because of the pilots and passengers who lost lives in these hard-earned lessons. So going back to 2017, outside of the A-Vision history, one of the unfortunate things that did happen was the North Korean crisis. If you remember, last year, the North Korean military fired some ICBM test missiles and got everyone seriously worried. January 15th this year, if you were watching TV at home in Hawaii, there was a pretty nasty surprise for you. The US Pacific Command has detected a missile threat to Hawaii. A missile may impact on land or sea within minutes. This is not a drill. That was some pretty scary stuff, right? Fortunately as we have later learned, there were no actual missiles heading for Hawaii that day. It was a false alarm. But millions of people were terrified and many have sent farewells and last words to loved ones. So how did something like that happen? How did a false missile alarm get broadcasted on TV? Well, that's the question that the FCC investigators need to answer. But figuring that out is no easy task. Obviously, the false alarm got broadcasted on TV because the TV station operator pressed the button to put it on the screen. But if you go ask him, he will tell you, well, I'm just doing my job because my manager called me up and told me to put it on the screen. And if you ask the manager, she will tell you, well, I got a call from the Hawaii Emergency Management Agency, so that's not my fault either. And following this chain all the way up to the top, you will find out that, well, turns out this was supposed to be a drill at the EMA. So what went wrong? Clearly, there's some miscommunication going on there, right? Because on one hand, the supervisor at the EMA intended that to be a drill, but on the other hand, it came out as a real alert. So who didn't get the memo here? Well, according to the investigation report, the EMA supervisor initiated the unscheduled drill during a shift change. And despite including the phrase, exercise, exercise, exercise, as they're required to, he went off script a little bit and at one point said the phrase, this is not a drill mistakenly. And in turns, the employee responsible for sending out the alert claimed that he missed the exercise part of the announcement and generally believed there was an actual emergency. Supposedly, a coworker put the message, put the announcement on speakerphone part way into the message. That's basically all we know. So unfortunately, we don't have a very good record of what actually went down there. So that's basically all we have to work with. But wouldn't this be a lot easier if we have some sort of proper paper trail of who talked to who during that time? Something better wouldn't it be nice if we can freeze time in that moment and interrogate all the parties involved? Well, that probably sounds to be true for FCC investigators. Luckily for us software investigators, this is totally feasible. And in fact, your computer is always trying very hard to give you this information all the time. So if you remember this red arrow thing that you see in the console all the time, if you don't know how to read it, it could be quite overwhelming. So why don't we try to use this format to describe what really went wrong in the Hawaii incident? So the top line is the error message, a description of what went wrong. This should always, always, always be the first thing you read because if you got lucky, sometimes this is all you need to read to figure out what went wrong. But in this case, this is really just telling us something we already know, right? Like we know we send alert, we don't think we should have. And so the real question is why did we do it? This is where the stack trace comes in, right? Like the scary looking thing at the bottom of the error is a paper trail of who talked to who, something that the FCC investigator would love to have in this situation. At the very top, that's where the error actually happened, right? So that's the TV operator triggering the alert. And the next line is why he did it in this case because the manager asked him to. And in turns, you can follow the trail all the way back to the EMA supervisor. And if you click on any file name in the stack trace, your browser will actually just take you directly to the definition of the function. So it looks something like this. So looking at the code here, it might be very tempting. You just fix the problem right here, right? Okay. We sent an alert and we shouldn't have sent alerts. So maybe we can just double check with the specific command before we do it. Now I totally understand why this might be tempting, right? Maybe this is the part of the code base that you know really well. You just don't want to open a can of worm that is sitting in the other file. That is the legacy, that predated you in the company. There's a bug in my code. I fixed the problem. I even wrote a test and it passed. So problem solved, right? Well there are two problems with that. The first problem, imagine how this code would look to the next person working with this code. Maybe they have to come fix a completely different bug, right? If that's me, I'll probably look at this and think, well, I thought I knew what this method is supposed to do based on the name, but now reading the source code, I'm not so sure anymore. Right? Like why would this method even be called when there are no incoming miss out? What does that mean? Like I guess probably that's probably because of some very complicated reasons I don't understand yet. So I don't want to break things. So I'll just work around the problem somewhere else instead. And with that history has repeated itself and we have seen the full life cycle of how a spaghetti code base is formed. So that's problem number one. Problem number two is that we didn't actually fix the problem enough, right? Sure we have prevented the TV station from broadcasting a false alert, but if you go a few frames up the stack, you will see that the system is also supposed to notify the cell phone providers to send SMS and push notifications. So we still have a problem there. Sure we can add another conditional around it, but I think the better thing to do is probably get to the bottom of this and try to fix it from there. Remember what we said about freezing time earlier. This is what the pause on exceptions button does. If you enable it, refresh the page, it will freeze everything as the error is happening, giving you a chance to go up and down a stack and capture any evidence you might want and interrogate the part you are involved. So similar to the stack tracing error message, clicking through the call stack on the right would jump you directly to the function definition. And however, because we are frozen in time, there are more we can do. You can probably see it highlighted a variable there. Because we are frozen in time, you can use the console to basically try to poke at any value that might be in scope at that point in time. So here we can see that the problem is the employee is called with is drill set to false, but is exercises undefined. So they generally believe this to be a real missile threat. So how come is exercises undefined here? Is that supposed to happen? To find out, we have to go at one level and it looks like the problem is the coworker forgot to pass that option along so we can fix that pretty easily. And after running a test, everything is green and so we are good to go. But is that good enough? Well, we can actually go one level further than that. If we go one more thing, we will see that this is really a bug in the supervisor. Why are there two separate options for is drill and is exercise? We should probably just fix the supervisor so it doesn't go off script and pass two conflicting things in a critical moment like this. With that, we have eliminated some unnecessary code and entangled some of the spaghetti for future readers. So we fix our first bug. We learn to read error messages. We learn to understand the stack trace. We learn to freeze time when exceptions are happening and use the debugger to go up and down the stack to find the problem. Most importantly, we learn that debugging is not just about fixing the immediate problem. We need to dig through the different layers to find the root cause and sometimes that might mean doing some refactoring upstream to prevent similar errors from ever happening again. All right. So that was quite a lot to take in, so I'll give you a little break. Let me tell you a story. This time we have to go back to 2003. On January 16, 2003, the Columbia space shuttle is ready to come home after spending 15 days on mission and orbit. Sadly, they never made it. The orbiter disintegrated completely during reentry, killing all seven crew members on board. So what happened here? If you were able to freeze time in this few critical minutes, you'll notice that there is a pretty big hole on the left wing. So that's why it disintegrated because it overheated when it was during reentry. But this is only half of the story. The critical question is why is there a hole on the left wing of an orbiter in the first place? Well, unfortunately, the ability to freeze time alone won't help you here. The event that led to the failure actually happened much earlier. It turns out during the launch, a piece of foam broke off, hitting the left wing at very high speed, leaving a hole behind. And these are going to problems where the root cause happened in a much earlier time frame than the eventual failure is very common in both the real world and in software. In the Hawaii incident, we knew that the supervisor intended to perform a drill, but it came out wrong on the other end. So we know to look for the problem somewhere in between those two points in time. As we explained, the stack trace basically gave you a moment in time snapshot of the explosion, which is represented on the right-hand side here. But in this case, instead of looking just looking at the very last moment, we need to look at the whole timeline of the mission to figure this out. We know that we didn't have a hole in the space shuttle before we launched it, which hacked. And we know that there was already a hole before the reentry. So our answer must lie somewhere in between these two points in time. So instead of going up and down the stack, we need a way to go back and forth in time in this case. And unfortunately, time traveling is actually quite difficult, but what we can do is freeze time in some of the key moments in the timeline and inspect them individually. This is what brick points do. You can add brick points to your code by using the debugger statement, which some of you are probably already familiar with. When the browser encounters one of these debugger statements, it will pause the execution, giving you a chance to inspect everything. And as you can see, we're currently paused in the time just before the prelaunch status check. Just like when we were paused on an exception, you can navigate up and down the stack and inspect any variables that are in scope. There's one additional thing you can do here. You can go forward in time in a very precisely controlled manner. The step over button let you go forward in time one statement at a time. Despite the pretty root name, it is usually a pretty good default because it will try to keep you in the current stack frame so you won't feel too disoriented. So clicking on that will advance time one statement at a time inside the current function. In this case, it will perform a stack check in a different team in the mission control every time you click that button. So far, so good. But as we learned, sometimes we need to go deeper than that. So here, we paused right before we pulled the flight director for a go or no go decision. But what if we don't want to just take the word for it? What if we want to double check to make sure they're doing the job correctly? That's where the step into button comes in handy. Instead of just calling the function and going over to the next line, it brings you into the implementation of the function. Once you're inside, everything works exactly the same way. You can keep going forward one statement at a time. You can keep going deeper if you want. And in this case, I'm happy with what I have seen here. So I decided I don't need to micromanage them any longer. In which case, you can use the step out button, which for Canadians, I find it root AF, but naming is hide. It does what you would expect. It will keep going forward in time until you have returned out of the current function back to the previous stack frame. And I won't waste your time here. So if you think you can trust the rest of the teams, you can just click the set ball function again and we'll be back to... We'll be put back in the previous stack frame. So finally, when you're done inspecting the current timeframe, the resume button will advance time until the next break point if you have one. So at this point, you're probably thinking, yes, I feel invincible. I can debug anything now. So I can't wait to try this in my Ember app. Well, before I let you off and go do that, I do have a few warnings for you. Remember when... At the beginning, when I said the debugging experience in Ember could sometimes be less than amazing? Well, I'm going to tell you what the problems are, why it exists, and what you can do about them. So the first problem is there are usually a lot of things in the call stack in an Ember app. So this is, I believe, a screenshot from Skylight, the Ember app. So part of the reason is that Ember is doing a lot of legwork for you, like managing the run loop or... So by the time the hope that you implemented is called, there are already a million things in the stack, and if nothing else, that just makes everything feel overwhelming. And the other part is that you will often be going through a lot of Ember app's fractions in an Ember app that is intended to make your life easier when they're working properly, such as this to underscore super. So if you try to step into this, you will end up inside the Ember super wrapper function, as we call it, instead of where you properly want it, which is the implementation you inherited from your super class. So of course, if you keep stepping, like this is not magic, so if you keep stepping, you will eventually end up in the place you want it, but you would have to first understand how you got here in the first place and what a nerve you're looking at. So it would be nice if you don't have to do that. To solve this problem, you can use the framework like boxing feature. The easiest way to do it is just right-click on any frame that you don't want to see, and you can click like box script, and as you can see, it go away. So it's hidden from you in the cost stack on the right-hand side, but more importantly, now when you try to step into it, you will end up directly inside the super class implementation instead of going through the underscore super wrapper. So essentially, you can label any code that is part of Ember and treat them sort of like native code in the browser. You don't really need to look at the implementation, because generally, you can just assume that it works. If you don't want to black box everything one by one, alternatively, you can also go into the developer tool settings and black box the entire vendor.js file, and with that setting, you can see that everything from Ember or other dependencies where they come from NPM or other sources will be hidden away from the stack trace, sorry, from the cost stack on the right. And you can see that in this case, all we have left is code we have written in our application, and that even fit inside one screen's worth of space, so that's pretty neat. A word of caution, though. A lot of time, ignoring the whole vendor file might end up hiding too much stuff that you might actually want to see. In particular, you might want to look at the implementation of some add-ons, or sometimes if you have pause on exception turn on and the exception happens inside the black box code, like maybe in Ember.onError, it isn't paused correctly. So it probably takes some tweaking to find the settings that work best for your team. But black boxing, the vendor file is certainly a pretty good starting point. And, okay, on to the second problem with debugging in Ember. This is essentially the same kind of problem as we encountered in the Columbia space shuttle problem, where the root cost and where the root cost and its effect is very, very far apart. As we saw earlier, sometimes black boxing helps a little bit here. I don't know if you can see that, but you can see at the bottom, the bottom frame is basically the model hook and application route, and you go through all the layers, even including a same code to your serializers and adapters. So that's pretty neat. But sometimes the browser could never figure it out for you. For example, you might have loaded some data in your parent route, and you didn't notice that you have a bug in your adapter. So it's not until way later when you try to access the data in your child route that you notice that there's an error, or maybe there's not even an error, there's just a blank screen of nothing. As far as the browser is concerned, those are completely separate events, so it will be pretty hard for the browser to tie them together for you. And arguably, this problem is not really unique to Ember, but it certainly is very common in Ember apps because of the way we tend to do things in Ember. In this case, you basically have to remember the tricks we learned from the Columbia Space Shuttle example and basically drop in breakpoints and strategic times to investigate the situation. In summary, you should try to ask yourself, what did I expect the code to do? And can I find a point in time where it was still behaving correctly? And from there, you can see when did it start diverging from your expectation. And from there, you can use the tools that we learned here to narrow in and find the red-costs of your problem. Finally, I will use the remaining time by which I mean the first five minutes of your snack rick to show you an assortment of tips and tricks that may help you in your journey. I'm going to go through these very, very quickly, so don't feel bad if you can't follow along. You can always rewatch this part when you're working, when you're running into problems. So I'm going to drink some water. And here we go. So number one, breakpoints. So far we have been talking about using the debugger's name as new code, but you know you can also add them directly from the developer tools without editing and saving and reloading. You can just click on the line number and boom, there you go. To remove it, just click it again. Now you can even turn it into a conditional breakpoint by right-clicking on it and select edit breakpoint. And for example, you can turn this into a conditional breakpoint that only trigger when the set is, it's not 200. So when you have an error from the server, you will trigger this breakpoint, otherwise it would just go through. And for comparison, this is the equivalent code that you would have written in your code for the same effect. And finally, you can also set a breakpoint that triggers whenever your app tried to do any Ajax request or you can scope it more narrowly by matching certain URLs. So in this case, I matched it to meet.json. So it triggered a breakpoint when the app is trying to load the user profile. Next continuation. So let's say you have a breakpoint at the top of the function and you want to fast forward to the middle of it, you can just keep pressing step over, but you can also just right-click on the line and say continue to here and it will just do exactly that. Sometimes you may have the opposite problem where you have a debugger statement in a for-loop that is triggering too many times because you're looping through a very big array, you can right-click and select never pause here or alternatively, you can click on the resume button and you can click and hold on the resume button and you can press the play button inside which will ignore the breakpoints and go all the way to the end. Next up, debug functions. So you might have a lot of console.log statements in your app. They are pretty useful for debugging, but maybe you don't want to ship them to production where they will spam your customer's console. So you know that Ember provide a debug function from the add Ember slash debug package. The nice thing about them is they are stripped in the production built. So you won't have to worry about the noise that they produce but also they won't increase the file size of your app or they won't slow down your app if you're doing any expensive inspection inside the message. Along those lines you can also use the assert function that is pretty useful for verifying some assumptions that you might have made in your code which can often help you catch bugs before they become a problem. If nothing else, it is a pretty good way to communicate to your readers that you expect certain things to be true all the time. They also strip from the production build so you can sprinkle them in pretty liberally without worrying about performance or file size, et cetera. Finally, templates and debugging templates in UI. You know that there is also a debugger statement you can use in handlebars that would bring you into the JavaScript debugger so you can just like a real debugger would. From there you can have access to your block frames or your context object, et cetera. That's pretty handy. And separately you probably already know about the Ember inspector but you know that any time you see a dollar E button you click on it and it will send the object to the console in the dollar E global variable so you can play around with it. And by the way, while we are here, any time you see a function in the console you can right click and click show function definition and it will bring you directly to the source code. This is pretty handy if you have something like the actions hash where you're trying to figure out where the functions are coming from. And also by the way, if you have source maps, you can do command P or probably control P on Windows and you can start typing a file name and it will open up the file you want. Like you can do, for example, command P and type routes slash application and it will generally do it right there. And over in the elements panel, similar to the Ember inspector, every time you select an element it will go to the dollar zero global variable so you can also play around with it in the console. And you can also right click on it to trigger things like hover state or focus state which are generally quite tricky to reproduce when you're debugging in the console. You can use one hand to hover it and type at the same time so this will come in handy. And finally, one last thing, you can select an element and set a break point on it so that every time that element is updated in any way, it will put you in a jar of a break point. This is pretty handy for debugging rendering problem where Ember is adding or removing news or attributes that you didn't expect and you don't know where they come from or what triggered it. So, yeah, you can basically use this to set a break point so you will have an opportunity to investigate that. And that concludes my talk and I hope that no matter where you're at in your debugging journey, you will feel more empowered in some way after a talk. And because in my opinion, I think debugging is one of the most important programming skills you could learn that is not often talked about enough. So once you have mastered debugging your own code, you can start peeling away the black box by stepping into the source code of libraries that you depend on to understand how they work. And from there, fixing bugs and sending poor requests to these libraries or even Ember itself is only a small hop at work. And that's how I personally started contributing to Open Source and maybe one day that could be you too. So that's it. Thank you very much. Thank you very much. Thanks, everyone. You
 How's it going?  It's me.  Well, I dressed up as my own emoji today.  I could do this all day, but to be honest, I probably need that snack brick more than  you do.  I just didn't eat that much alone.  I'm just not much of a barbecue person, you know.  Anyway, I'm Godfrey.  That's me.  You can find me on the internet as Chanking Code.  Before we begin, I've got to do the Snapchat.  So if you cannot share with me, that would really help with the internet points.  So are you ready?  Good to go?  All right, let's do this.  Wait, that's, come on.  Let's try it again.  All right.  Oh, my God.  Welcome to Ambercon Canada.  I'm so excited to be doing the closing keynote for Ambercon Canada.  I work at a local company called Tilda.  We make land yurts.  But if you like our land yurts, you're probably also like our performance monitoring product.  We have a booth outside.  And if you like our land yurts, you'll probably also be into crypto currencies and you should  check out our ICO.  You can find it at Skylight.io slash fefficon.icl.  This is what it looks like.  Some day it's going to work a lot of money.  But if you like money that much, you should also sign up for Skylight using this referral  link.  That way you and I both will be swimming in Skylight credits.  Or you can just come to the booth and we have a $50 promo code there as well.  And finally, if you maintain any open source Ruby apps, you should definitely come over  and sign up for a completely free account.  So with that, I'll do it.  Let's talk about debugging.  Before we dive in, I just want to set some expectations.  If you are a new developer, I had you in mind when writing this talk, so I really hope I  can help.  If you have been programming for a while and Ember is your first or primary programming  environment, this talk is also for you.  I know sometimes debugging in Ember could be intimidating or even a little bit frustrating.  So I hope to help reset your expectation of what debugging should be like.  And at the end, I will mention why debugging in Ember might feel a little bit more difficult  and what you can do about it.  And finally, if you are an intermediate or advanced developer, then you probably already  know everything I'm about to tell you.  And this talk is probably not going to blow your mind.  But I would like to invite you to follow along and think about how we can more effectively  teach these things to new developers and what we can do to improve the debugging experience  for everyone.  So let's get started.  I have been pretty obsessed with failures lately, specifically engineering failures and  catastrophes.  Not because I enjoy watching people suffer, of course, but I'm quite fascinated by the  ways that we manage to analyze and learn from these mistakes that we make.  A lot of things happened in 2017.  However, one of the things that didn't happen was plane crashes, specifically crashes involving  commercial passenger jets.  You see, we flown over 4 billion passengers last year, which is more than half of the  world's population, pretty mind blowing.  And yet, there was not a single fatality in commercial passenger jet liner in 2017.  That wasn't always the case, though.  In the early days of A-Vision history, flying was a pretty risky business.  And in fact, it was so risky that the early ML pilots called themselves members of a suicide  club.  They aren't wrong because it was later calculated that their average live expectancy was as  low as 900 flying hours, which is roughly how many hours a commercial pilot fly every  year today.  And since those early days, we have invested a lot of resources into improving the safety  of flying for everyone.  And in the modern aviation days, every accident is very heavily scrutinized and investigated  with the results of these investigations leading into new regulations and engineering improvements  to prevent the same kind of failure from ever happening again.  So every time you fly today, in some ways, you fly saver in parts because of the pilots  and passengers who lost lives in these hard-earned lessons.  So going back to 2017, outside of the A-Vision history, one of the unfortunate things that  did happen was the North Korean crisis.  If you remember, last year, the North Korean military fired some ICBM test missiles and  got everyone seriously worried.  January 15th this year, if you were watching TV at home in Hawaii, there was a pretty nasty  surprise for you.  The US Pacific Command has detected a missile threat to Hawaii.  A missile may impact on land or sea within minutes.  This is not a drill.  That was some pretty scary stuff, right?  Fortunately as we have later learned, there were no actual missiles heading for Hawaii  that day.  It was a false alarm.  But millions of people were terrified and many have sent farewells and last words to  loved ones.  So how did something like that happen?  How did a false missile alarm get broadcasted on TV?  Well, that's the question that the FCC investigators need to answer.  But figuring that out is no easy task.  Obviously, the false alarm got broadcasted on TV because the TV station operator pressed  the button to put it on the screen.  But if you go ask him, he will tell you, well, I'm just doing my job because my manager called  me up and told me to put it on the screen.  And if you ask the manager, she will tell you, well, I got a call from the Hawaii Emergency  Management Agency, so that's not my fault either.  And following this chain all the way up to the top, you will find out that, well, turns  out this was supposed to be a drill at the EMA.  So what went wrong?  Clearly, there's some miscommunication going on there, right?  Because on one hand, the supervisor at the EMA intended that to be a drill, but on the  other hand, it came out as a real alert.  So who didn't get the memo here?  Well, according to the investigation report, the EMA supervisor initiated the unscheduled  drill during a shift change.  And despite including the phrase, exercise, exercise, exercise, as they're required to,  he went off script a little bit and at one point said the phrase, this is not a drill  mistakenly.  And in turns, the employee responsible for sending out the alert claimed that he missed  the exercise part of the announcement and generally believed there was an actual emergency.  Supposedly, a coworker put the message, put the announcement on speakerphone part way  into the message.  That's basically all we know.  So unfortunately, we don't have a very good record of what actually went down there.  So that's basically all we have to work with.  But wouldn't this be a lot easier if we have some sort of proper paper trail of who talked  to who during that time?  Something better wouldn't it be nice if we can freeze time in that moment and interrogate  all the parties involved?  Well, that probably sounds to be true for FCC investigators.  Luckily for us software investigators, this is totally feasible.  And in fact, your computer is always trying very hard to give you this information all  the time.  So if you remember this red arrow thing that you see in the console all the time, if you  don't know how to read it, it could be quite overwhelming.  So why don't we try to use this format to describe what really went wrong in the Hawaii  incident?  So the top line is the error message, a description of what went wrong.  This should always, always, always be the first thing you read because if you got lucky,  sometimes this is all you need to read to figure out what went wrong.  But in this case, this is really just telling us something we already know, right?  Like we know we send alert, we don't think we should have.  And so the real question is why did we do it?  This is where the stack trace comes in, right?  Like the scary looking thing at the bottom of the error is a paper trail of who talked  to who, something that the FCC investigator would love to have in this situation.  At the very top, that's where the error actually happened, right?  So that's the TV operator triggering the alert.  And the next line is why he did it in this case because the manager asked him to.  And in turns, you can follow the trail all the way back to the EMA supervisor.  And if you click on any file name in the stack trace, your browser will actually just take  you directly to the definition of the function.  So it looks something like this.  So looking at the code here, it might be very tempting.  You just fix the problem right here, right?  Okay.  We sent an alert and we shouldn't have sent alerts.  So maybe we can just double check with the specific command before we do it.  Now I totally understand why this might be tempting, right?  Maybe this is the part of the code base that you know really well.  You just don't want to open a can of worm that is sitting in the other file.  That is the legacy, that predated you in the company.  There's a bug in my code.  I fixed the problem.  I even wrote a test and it passed.  So problem solved, right?  Well there are two problems with that.  The first problem, imagine how this code would look to the next person working with this  code.  Maybe they have to come fix a completely different bug, right?  If that's me, I'll probably look at this and think, well, I thought I knew what this  method is supposed to do based on the name, but now reading the source code, I'm not so  sure anymore.  Right?  Like why would this method even be called when there are no incoming miss out?  What does that mean?  Like I guess probably that's probably because of some very complicated reasons I don't understand  yet.  So I don't want to break things.  So I'll just work around the problem somewhere else instead.  And with that history has repeated itself and we have seen the full life cycle of how  a spaghetti code base is formed.  So that's problem number one.  Problem number two is that we didn't actually fix the problem enough, right?  Sure we have prevented the TV station from broadcasting a false alert, but if you go  a few frames up the stack, you will see that the system is also supposed to notify the  cell phone providers to send SMS and push notifications.  So we still have a problem there.  Sure we can add another conditional around it, but I think the better thing to do is  probably get to the bottom of this and try to fix it from there.  Remember what we said about freezing time earlier.  This is what the pause on exceptions button does.  If you enable it, refresh the page, it will freeze everything as the error is happening,  giving you a chance to go up and down a stack and capture any evidence you might want and  interrogate the part you are involved.  So similar to the stack tracing error message, clicking through the call stack on the right  would jump you directly to the function definition.  And however, because we are frozen in time, there are more we can do.  You can probably see it highlighted a variable there.  Because we are frozen in time, you can use the console to basically try to poke at any  value that might be in scope at that point in time.  So here we can see that the problem is the employee is called with is drill set to false,  but is exercises undefined.  So they generally believe this to be a real missile threat.  So how come is exercises undefined here?  Is that supposed to happen?  To find out, we have to go at one level and it looks like the problem is the coworker  forgot to pass that option along so we can fix that pretty easily.  And after running a test, everything is green and so we are good to go.  But is that good enough?  Well, we can actually go one level further than that.  If we go one more thing, we will see that this is really a bug in the supervisor.  Why are there two separate options for is drill and is exercise?  We should probably just fix the supervisor so it doesn't go off script and pass two  conflicting things in a critical moment like this.  With that, we have eliminated some unnecessary code and entangled some of the spaghetti for  future readers.  So we fix our first bug.  We learn to read error messages.  We learn to understand the stack trace.  We learn to freeze time when exceptions are happening and use the debugger to go up and  down the stack to find the problem.  Most importantly, we learn that debugging is not just about fixing the immediate problem.  We need to dig through the different layers to find the root cause and sometimes that  might mean doing some refactoring upstream to prevent similar errors from ever happening  again.  All right.  So that was quite a lot to take in, so I'll give you a little break.  Let me tell you a story.  This time we have to go back to 2003.  On January 16, 2003, the Columbia space shuttle is ready to come home after spending 15 days  on mission and orbit.  Sadly, they never made it.  The orbiter disintegrated completely during reentry, killing all seven crew members on  board.  So what happened here?  If you were able to freeze time in this few critical minutes, you'll notice that there  is a pretty big hole on the left wing.  So that's why it disintegrated because it overheated when it was during reentry.  But this is only half of the story.  The critical question is why is there a hole on the left wing of an orbiter in the first  place?  Well, unfortunately, the ability to freeze time alone won't help you here.  The event that led to the failure actually happened much earlier.  It turns out during the launch, a piece of foam broke off, hitting the left wing at very  high speed, leaving a hole behind.  And these are going to problems where the root cause happened in a much earlier time  frame than the eventual failure is very common in both the real world and in software.  In the Hawaii incident, we knew that the supervisor intended to perform a drill, but it came out  wrong on the other end.  So we know to look for the problem somewhere in between those two points in time.  As we explained, the stack trace basically gave you a moment in time snapshot of the  explosion, which is represented on the right-hand side here.  But in this case, instead of looking just looking at the very last moment, we need to  look at the whole timeline of the mission to figure this out.  We know that we didn't have a hole in the space shuttle before we launched it, which  hacked.  And we know that there was already a hole before the reentry.  So our answer must lie somewhere in between these two points in time.  So instead of going up and down the stack, we need a way to go back and forth in time  in this case.  And unfortunately, time traveling is actually quite difficult, but what we can do is freeze  time in some of the key moments in the timeline and inspect them individually.  This is what brick points do.  You can add brick points to your code by using the debugger statement, which some of you  are probably already familiar with.  When the browser encounters one of these debugger statements, it will pause the execution, giving  you a chance to inspect everything.  And as you can see, we're currently paused in the time just before the prelaunch status  check.  Just like when we were paused on an exception, you can navigate up and down the stack and  inspect any variables that are in scope.  There's one additional thing you can do here.  You can go forward in time in a very precisely controlled manner.  The step over button let you go forward in time one statement at a time.  Despite the pretty root name, it is usually a pretty good default because it will try  to keep you in the current stack frame so you won't feel too disoriented.  So clicking on that will advance time one statement at a time inside the current function.  In this case, it will perform a stack check in a different team in the mission control  every time you click that button.  So far, so good.  But as we learned, sometimes we need to go deeper than that.  So here, we paused right before we pulled the flight director for a go or no go decision.  But what if we don't want to just take the word for it?  What if we want to double check to make sure they're doing the job correctly?  That's where the step into button comes in handy.  Instead of just calling the function and going over to the next line, it brings you into  the implementation of the function.  Once you're inside, everything works exactly the same way.  You can keep going forward one statement at a time.  You can keep going deeper if you want.  And in this case, I'm happy with what I have seen here.  So I decided I don't need to micromanage them any longer.  In which case, you can use the step out button, which for Canadians, I find it root AF, but  naming is hide.  It does what you would expect.  It will keep going forward in time until you have returned out of the current function  back to the previous stack frame.  And I won't waste your time here.  So if you think you can trust the rest of the teams, you can just click the set ball  function again and we'll be back to...  We'll be put back in the previous stack frame.  So finally, when you're done inspecting the current timeframe, the resume button will  advance time until the next break point if you have one.  So at this point, you're probably thinking, yes, I feel invincible.  I can debug anything now.  So I can't wait to try this in my Ember app.  Well, before I let you off and go do that, I do have a few warnings for you.  Remember when...  At the beginning, when I said the debugging experience in Ember could sometimes be less  than amazing?  Well, I'm going to tell you what the problems are, why it exists, and what you can do about  them.  So the first problem is there are usually a lot of things in the call stack in an Ember  app.  So this is, I believe, a screenshot from Skylight, the Ember app.  So part of the reason is that Ember is doing a lot of legwork for you, like managing the  run loop or...  So by the time the hope that you implemented is called, there are already a million things  in the stack, and if nothing else, that just makes everything feel overwhelming.  And the other part is that you will often be going through a lot of Ember app's fractions  in an Ember app that is intended to make your life easier when they're working properly,  such as this to underscore super.  So if you try to step into this, you will end up inside the Ember super wrapper function,  as we call it, instead of where you properly want it, which is the implementation you inherited  from your super class.  So of course, if you keep stepping, like this is not magic, so if you keep stepping, you  will eventually end up in the place you want it, but you would have to first understand  how you got here in the first place and what a nerve you're looking at.  So it would be nice if you don't have to do that.  To solve this problem, you can use the framework like boxing feature.  The easiest way to do it is just right-click on any frame that you don't want to see, and  you can click like box script, and as you can see, it go away.  So it's hidden from you in the cost stack on the right-hand side, but more importantly,  now when you try to step into it, you will end up directly inside the super class implementation  instead of going through the underscore super wrapper.  So essentially, you can label any code that is part of Ember and treat them sort of like  native code in the browser.  You don't really need to look at the implementation, because generally, you can just assume that  it works.  If you don't want to black box everything one by one, alternatively, you can also go  into the developer tool settings and black box the entire vendor.js file, and with that  setting, you can see that everything from Ember or other dependencies where they come  from NPM or other sources will be hidden away from the stack trace, sorry, from the cost  stack on the right.  And you can see that in this case, all we have left is code we have written in our application,  and that even fit inside one screen's worth of space, so that's pretty neat.  A word of caution, though.  A lot of time, ignoring the whole vendor file might end up hiding too much stuff that you  might actually want to see.  In particular, you might want to look at the implementation of some add-ons, or sometimes  if you have pause on exception turn on and the exception happens inside the black box  code, like maybe in Ember.onError, it isn't paused correctly.  So it probably takes some tweaking to find the settings that work best for your team.  But black boxing, the vendor file is certainly a pretty good starting point.  And, okay, on to the second problem with debugging in Ember.  This is essentially the same kind of problem as we encountered in the Columbia space shuttle  problem, where the root cost and where the root cost and its effect is very, very far  apart.  As we saw earlier, sometimes black boxing helps a little bit here.  I don't know if you can see that, but you can see at the bottom, the bottom frame is  basically the model hook and application route, and you go through all the layers, even including  a same code to your serializers and adapters.  So that's pretty neat.  But sometimes the browser could never figure it out for you.  For example, you might have loaded some data in your parent route, and you didn't notice  that you have a bug in your adapter.  So it's not until way later when you try to access the data in your child route that you  notice that there's an error, or maybe there's not even an error, there's just a blank screen  of nothing.  As far as the browser is concerned, those are completely separate events, so it will  be pretty hard for the browser to tie them together for you.  And arguably, this problem is not really unique to Ember, but it certainly is very common  in Ember apps because of the way we tend to do things in Ember.  In this case, you basically have to remember the tricks we learned from the Columbia Space  Shuttle example and basically drop in breakpoints and strategic times to investigate the situation.  In summary, you should try to ask yourself, what did I expect the code to do?  And can I find a point in time where it was still behaving correctly?  And from there, you can see when did it start diverging from your expectation.  And from there, you can use the tools that we learned here to narrow in and find the  red-costs of your problem.  Finally, I will use the remaining time by which I mean the first five minutes of your  snack rick to show you an assortment of tips and tricks that may help you in your journey.  I'm going to go through these very, very quickly, so don't feel bad if you can't follow along.  You can always rewatch this part when you're working, when you're running into problems.  So I'm going to drink some water.  And here we go.  So number one, breakpoints.  So far we have been talking about using the debugger's name as new code, but you know  you can also add them directly from the developer tools without editing and saving and reloading.  You can just click on the line number and boom, there you go.  To remove it, just click it again.  Now you can even turn it into a conditional breakpoint by right-clicking on it and select  edit breakpoint.  And for example, you can turn this into a conditional breakpoint that only trigger when  the set is, it's not 200.  So when you have an error from the server, you will trigger this breakpoint, otherwise  it would just go through.  And for comparison, this is the equivalent code that you would have written in your code  for the same effect.  And finally, you can also set a breakpoint that triggers whenever your app tried to do  any Ajax request or you can scope it more narrowly by matching certain URLs.  So in this case, I matched it to meet.json.  So it triggered a breakpoint when the app is trying to load the user profile.  Next continuation.  So let's say you have a breakpoint at the top of the function and you want to fast forward  to the middle of it, you can just keep pressing step over, but you can also just right-click  on the line and say continue to here and it will just do exactly that.  Sometimes you may have the opposite problem where you have a debugger statement in a for-loop  that is triggering too many times because you're looping through a very big array, you can right-click  and select never pause here or alternatively, you can click on the resume button and you  can click and hold on the resume button and you can press the play button inside which  will ignore the breakpoints and go all the way to the end.  Next up, debug functions.  So you might have a lot of console.log statements in your app.  They are pretty useful for debugging, but maybe you don't want to ship them to production  where they will spam your customer's console.  So you know that Ember provide a debug function from the add Ember slash debug package.  The nice thing about them is they are stripped in the production built.  So you won't have to worry about the noise that they produce but also they won't increase  the file size of your app or they won't slow down your app if you're doing any expensive  inspection inside the message.  Along those lines you can also use the assert function that is pretty useful for verifying  some assumptions that you might have made in your code which can often help you catch  bugs before they become a problem.  If nothing else, it is a pretty good way to communicate to your readers that you expect  certain things to be true all the time.  They also strip from the production build so you can sprinkle them in pretty liberally  without worrying about performance or file size, et cetera.  Finally, templates and debugging templates in UI.  You know that there is also a debugger statement you can use in handlebars that would bring  you into the JavaScript debugger so you can just like a real debugger would.  From there you can have access to your block frames or your context object, et cetera.  That's pretty handy.  And separately you probably already know about the Ember inspector but you know that any  time you see a dollar E button you click on it and it will send the object to the console  in the dollar E global variable so you can play around with it.  And by the way, while we are here, any time you see a function in the console you can  right click and click show function definition and it will bring you directly to the source  code.  This is pretty handy if you have something like the actions hash where you're trying to  figure out where the functions are coming from.  And also by the way, if you have source maps, you can do command P or probably control P  on Windows and you can start typing a file name and it will open up the file you want.  Like you can do, for example, command P and type routes slash application and it will  generally do it right there.  And over in the elements panel, similar to the Ember inspector, every time you select  an element it will go to the dollar zero global variable so you can also play around with  it in the console.  And you can also right click on it to trigger things like hover state or focus state which  are generally quite tricky to reproduce when you're debugging in the console.  You can use one hand to hover it and type at the same time so this will come in handy.  And finally, one last thing, you can select an element and set a break point on it so  that every time that element is updated in any way, it will put you in a jar of a break  point.  This is pretty handy for debugging rendering problem where Ember is adding or removing  news or attributes that you didn't expect and you don't know where they come from or  what triggered it.  So, yeah, you can basically use this to set a break point so you will have an opportunity  to investigate that.  And that concludes my talk and I hope that no matter where you're at in your debugging  journey, you will feel more empowered in some way after a talk.  And because in my opinion, I think debugging is one of the most important programming skills  you could learn that is not often talked about enough.  So once you have mastered debugging your own code, you can start peeling away the black  box by stepping into the source code of libraries that you depend on to understand how they  work.  And from there, fixing bugs and sending poor requests to these libraries or even Ember  itself is only a small hop at work.  And that's how I personally started contributing to Open Source and maybe one day that could  be you too.  So that's it.  Thank you very much.  Thank you very much.  Thanks, everyone.  You
Prying_Open_the_Black_Box_0_251.png Prying_Open_the_Black_Box_252_1331.png Prying_Open_the_Black_Box_1332_1439.png Prying_Open_the_Black_Box_1440_1487.png Prying_Open_the_Black_Box_1488_1607.png Prying_Open_the_Black_Box_1608_1751.png Prying_Open_the_Black_Box_1752_2003.png Prying_Open_the_Black_Box_2004_2051.png Prying_Open_the_Black_Box_2052_2183.png Prying_Open_the_Black_Box_2184_2627.png Prying_Open_the_Black_Box_2628_2699.png Prying_Open_the_Black_Box_2700_2759.png Prying_Open_the_Black_Box_2760_2903.png Prying_Open_the_Black_Box_2904_3323.png Prying_Open_the_Black_Box_3324_3623.png Prying_Open_the_Black_Box_3624_6167.png Prying_Open_the_Black_Box_6168_6359.png Prying_Open_the_Black_Box_6360_7379.png Prying_Open_the_Black_Box_7380_8003.png Prying_Open_the_Black_Box_8004_8207.png Prying_Open_the_Black_Box_8208_8315.png Prying_Open_the_Black_Box_8316_8723.png Prying_Open_the_Black_Box_8724_8855.png Prying_Open_the_Black_Box_8856_9167.png Prying_Open_the_Black_Box_9168_9707.png Prying_Open_the_Black_Box_9708_12563.png Prying_Open_the_Black_Box_12564_20195.png Prying_Open_the_Black_Box_20196_20591.png Prying_Open_the_Black_Box_20592_20639.png Prying_Open_the_Black_Box_20640_21071.png Prying_Open_the_Black_Box_21072_22091.png Prying_Open_the_Black_Box_22092_22139.png Prying_Open_the_Black_Box_22140_22259.png Prying_Open_the_Black_Box_22260_22415.png Prying_Open_the_Black_Box_22416_22547.png Prying_Open_the_Black_Box_22548_22631.png Prying_Open_the_Black_Box_22632_23591.png Prying_Open_the_Black_Box_23592_24731.png Prying_Open_the_Black_Box_24732_26219.png Prying_Open_the_Black_Box_26220_31007.png Prying_Open_the_Black_Box_31008_36575.png Prying_Open_the_Black_Box_36576_37199.png Prying_Open_the_Black_Box_37200_37571.png Prying_Open_the_Black_Box_37572_39923.png Prying_Open_the_Black_Box_39924_45251.png Prying_Open_the_Black_Box_45252_48947.png Prying_Open_the_Black_Box_48948_50195.png Prying_Open_the_Black_Box_50196_50543.png Prying_Open_the_Black_Box_50544_50843.png Prying_Open_the_Black_Box_50844_50952.png