 Good morning. I do not have a slide with fancy pictures and stuff about myself in this particular slide deck, so I'm just going to talk about it for a while. My name is Bart Xano, Xano Xano, which reminds me of my first DrupalCon, which was in DC, back in the day. And it was like, oh, it's this guy. Welcome. Welcome in the group. And then someone asks me the awkward question. How do you pronounce your nickname? I have no idea. Come up with something pretty. Hence the different variations of my nickname and me not even knowing how to pronounce it myself. I'm a background developer primarily, although when one works in teams and works in client projects, there's a lot of other stuff that you do, you have to do, want to do on the side, because just writing a bit of PHP doesn't actually make a mean that a software product ends up on a production server and is used by clients, which is also the reason why this presentation is going to include a few other languages than PHP. I've been trying to expand myself a little, and I feel that having a bit of knowledge about other languages, about other systems, not even being able to write in them necessarily, but just knowing about their quirks, about their best practices, really opens your mind to the things in the language, which is PHP, that we work with ourselves. Sometimes you realize, oh, it's not a PHP thing. It's not a Drupal thing. It's a generic computer science thing. This is just a really weird PHP thing after all. It helps you attack problems from different angles. So that's basically the reason that I started the idea of this presentation early in the spring. I've been doing Drupal for about a decade. I started with 4.6 when it looked way, way, way earlier than it does now. I think when Drupal 5 came out, was it 5 or 6? One of the main new features of the time was table-less layouts. That was a thing back then. I've done contrib development, core development. I've helped with Meetups and Conferences to greater and lesser extent over the course of that decade. I've learned a lot from a lot of fascinating and passionate people, very, very patient people as well. Sitting down with someone and guiding them through something can be stressful at times, so thanks to other people who have got me in or in stage, which brings me to the mentors here this week. We've got a mentor program at DrupalCon, which practically speaking means that my slides don't work. Literally, the light has gone out. Is that? No, it is actually. Did you try turning it off and on again? I was hoping to keep this joke for later in the presentation, but I am honestly not sure what's going on right now. If you're listening, we're having a similar problem as with the other adapter that the laptop doesn't recognize it anymore. So you're getting nothing. OK. Oh, well. Now stop touching it, Brad. We were talking about mentors and everything. We have big, big code sprints at DrupalCon. In recent years, we've had community-organized sprints on the weekends before and after. But traditionally, Friday, our sprints at the conference venue where we've got hundreds of people contributing, including a lot of people who are total experts in their fields, but just have never gone through the bureaucracy of contributing a patch or reviewing a patch in the Drupal issue queue, which is what mentors do. Sit down, have patience, listen to people, not just in the right direction. They're wearing purple t-shirts with the conference logo this year. If you see them, give them a big thanks and a big hug. If you haven't seen them, maybe you should go to the mentor experience yourself on Friday and work on your very first core patch yourself. Documentation, copywriting, bug fixing. We've got it all. We need it all. Defensive programming. This is basically my definition of it from a very, very rational, practical point of view. It's a fancy term, but it solves practical problems. It tries to limit problems by thinking about what you're doing, by making it more difficult for code to fail. Because if code is not that difficult, if it is simple, the problems tend to be a lot simpler, or at least not appear in great numbers as opposed to code that is a spaghetti go. We're going to see a few examples of this. But the one thing that I do want you to remember, things go wrong in a project. We're all human. Luckily, we still are. We don't have any bionic implants or body parts. We're people who work together, who like it sometimes. Who sometimes dislike working together. We make great things, and we also fail sometimes. The main point is that you need to accept that. Things go wrong in your budgeting, in how you handle your coworkers socially. If a mistake is made, don't point the fingers straight away. Think about, what can we do to solve it? And you do that socially. So you don't start yelling at each other. You do that in planning to go back on track. And you do it in code to make sure that it doesn't happen again. Prevent regressions. So, things can go wrong because there are bugs in the code. If there are any programmers in this room at all. Oh, there you are. I didn't see you between all the other programmers. Bugs, they're the obvious elephant in the room of things that can go wrong in a software project. And we all know that we generally try not to introduce any of these things on purpose. So again, it happens. And we fix these. Another interesting side effect of the problems going wrong. Not necessarily what you as a developer introduce, but what other people introduce in not necessarily your software, but more the ecosystem. How people use your software, whether they're coworkers or users of your open source library. There's always someone who will knock on your door and be like, hey, yeah, I know I'm using this in a way that's completely different from what you designed us to do, but I still think this should be possible. This is fun. As an author of open source software, this is a really great way of expanding your horizon, making your software available to more people, to more use cases. And because someone looks at your code and is like, this other thing should be possible as well. Maybe it should have been possible. You wrote that code. You wrote that code. The code documents itself and says that it does a certain things. If someone's like, maybe my use case, it's not in the read me, but if I read the code, I think it should be supported. Go along with this. Fix the bugs that have been reported. And even if you don't want to fix them, and we'll get to that a little later, why is this person reporting these bugs or lack of usefulness in the first place? Was it a lack of documentation on your end? Like if you really don't want to or can't support it, maybe you should have documented that. If there was some kind of a weird error or you can tell that person, he didn't use this function the right way. Well, it should have thrown an exception. That's what this is about. To make sure that the code that we write, the software that we produce is as nice and easy to use for the people around us, including ourselves, present-day selves and future selves. This way of working, like pausing when you design yourself or figuring out, where do we need to make this more robust, more solid so that people actually like working with it? They reduce problems in, for instance, these fields. Your planning is hurt if many, many unexpected bugs show up and they take hours or days to fix. Time is money, especially with the agile methods that a lot of us use. Try explaining that to the client if it keeps happening again. No, no, we need another week. Why? You don't want to have to say that because they have to explain it to their stakeholders. You get this whole chain of people who gets entirely frustrated with each other. You want to limit that. If we go back to just the pure scheduling, you don't want to block your coworkers too much. We do all kinds of things like making small issues so we can very easily between issues help each other out, review each other's work, build on top of each other's results. The more things go wrong, like I can't work on these issues, I have to spend six hours of my day tracking this inexplicable bug. You just blocked two of your teammates. Mental and social is how you interact with each other, don't point the finger at the person, point the finger at the problem. As an engineer, regression testing is great. Your coworker can also regress into making the same mistake again. Don't point the finger, make sure they don't make the same mistake again. Teach people. So one of the main concepts is simplicity. The less your software does, the less that can go wrong. That's generally how it works. With some notable and exotic exceptions, of course. Keep things simple, input, output. Don't take too many different things. Don't output too many different things. If you're writing a function that's 100 lines long, maybe it does a few too many things. Try writing a test for it. If that's too frustrating, maybe the world's telling you something. And I've heard, I can't remember the name of who this was, but some of the time early this year, we were having dinner with someone or with a group of people and someone was like, you know, maybe we do need to slow down. Maybe we need to take a break in the beginning. Before we start outputting all this, before we start being productive and pushing code and deploying code, maybe we should slow down, think about it. What are we gonna write? Why are we writing it? Does it need to be reused later on? One of the things of coding is that you generally do a lot more reading than writing. Optimize for that. The simplicity doesn't mean that your code is not allowed to be verbose. Sometimes longer is a little bit better. There are tons of coding standards, for instance, that tell you, oh, your variable names cannot be longer than this many characters. But I think by this time, we've all made the mistake of naming our variables inner loops K and V. That doesn't work either. So yes, code should be short and simple, but code should also serve us. It needs to be readable. If you need a bit more to clearly document what your code does or what it should be doing, or why it does what it does, then take that time. Slow down, write it. Think about your function names, your variable names and all that. Think about your code comments. White space is not evil. Code comments are not evil. And as we'll see in the next few slides, having more lines is also not evil, because it generally means you have shorter and simpler lines. This is an example of something really, really simple. This is a bit of pseudo PHP code. We've got a function with, these are three nested blocks, and nested blocks in general are a little hard for readability. We've got two conditions here, and there's one if statement, and then we've got a forage loop. And this is simple. You write this, you're like, ah, it's fine. It's still fairly readable. Your coworker comes in a couple of weeks from now, and it's like, oh, we need to add a little bit to this. Well, before you know it, you get a couple of nested if statements and loops, all spread out across 50 lines, and the third person coming in is like, I have no idea what this does. So we can rewrite this. And I call this short and wide code. For the sake of the slides, it's not that wide, otherwise it would sort of end here. But if you make it a little bit narrow, if you make simpler blocks, we see that we can remove a level of nesting, like the for loop, or the forage loop is no longer inside another block, and we no longer have combined conditions in if statements. So the code is narrower. You may not remember the 80 character line limit for our coding style in Drupal, which comes from like old, was it 1970s, 80s, text terminals, or maybe even before that, I'm not that old. But there's a reason for it. There are, in literature and printing, there are tons of theories with proof that prove that human beings can only read lines this long. The shorter it is, the easier it is for us to read it very, very quickly. You've got one thing per line, and that's it. Especially in this example, we saw in the code in the previous slide here that if this is false, nothing happens. If this is zero or lower, nothing happens. So we use that to do early returns. By the time the code execution reaches the critical part of this function, which in this example is a simple loop, all the other stuff is already out of the way. This is, the critical code is a tiny block, which is at the beginning of the indentation. You can easily skip to the most important part of it. It's easy to find out, as a reader, of somebody say this is someone else's code, I can easily figure out, oh, this is like pre-condition check, pre-condition check. Oh, this is what I actually need to read to work with. So think about this. Make your code readable. Optimize for yourself and other people having to understand what you produce. Because that improves maintainability. Yeah. Maintainability is basically a combination of the cost involved in getting involved in code. Even like we work for Druid, we have multiple different teams. It happens that people from another team, or like the maintenance team come in on a project from a development team after it was launched, and they have to work with something they've never seen before. Same company, same people, a lot of the same workflows, but still you're reading someone else's code. We could walk to each other's desk, we could set up a conference call, or go on Slack, still you're reading someone else's code. So as close as you still are, there's still a lot of profit to be made here. So make it easy, make it easy for yourself as well. I've, there's a very notable example in my head of a couple of years ago, where I was working on one of my contributed modules. I'm like, I know I wrote this code, but geez, what was I thinking? This is so overly complex. It's really nice, it's a few lines, but this is like too much crammed into these few little blocks. And this is just an overly complex solution. And I started working it for about an hour and a half, and then this was before I was convinced of the gospel of automated testing. And I tested, and it breaks down, fires and dragons and volcanoes, and I realized that there was a very, very good reason that I had originally written that code like that, but it was never, I never published that code in such a way that future me or future anyone will be able to read and understand the intention behind that code, so that the maintainability of that particular block of code was terrible. I wasted an hour and a half on nothing. I could throw it out, that's the only thing that happened, an hour and a half of my life thrown away, except for the fact that I learned to document my stuff. To make it readable. So reading and understanding and fixing code is really important, not just the production of new code, but working with existing stuff. And that goes from maintenance, to fixing existing problems, to even refactoring. A lot of us use agile, which inherently means that sometimes you do need to refactor because you don't plan too much ahead. That comes at the cost of having to refactor. You can't refactor if you don't know what's already going on. Optimize for these situations in your code. Generally means sit down, slow down, plan what you're going to do. How can we build what we're trying to build in as simple a way as possible that's as easy to understand? And like I said, that does mean that it's allowed to have more code if that makes the rest of our work simpler. Especially with the migration from Drupal 7 to 8 using PSR4, so one class profile, we've gotten a lot of boilerplate. Every file had to have an app file doc block, not anymore, but used to it. A class with its own doc block. Implement an interface. A lot of people thought that was really annoying in the beginning, and there's a lot of code to write, and if you have an IDE, it can take a lot of this work for you. But again, it's not that much logical code. If any of that code goes wrong, you fix it once and generally doesn't break again. It's about the logical code, but that's most important. Like in this example, the for loop, this is the most important code, keep that simple. When things do go wrong, you get an error, or you get an exception, or whatever name your language uses, it tries to notify you of a situation that shouldn't really have happened. Again, if there's a problem, it's about how you deal with it. So the problem occurs. You're like, ah, shit, now what? First step, where'd it come from? What's it doing? Well, you can read the error message. In my long, not so long, but long enough experience with programming, error messages often don't tell you exactly where the problem occurred because they could have been chained. Maybe the language is completely bad at giving you useful error messages. Like BHP in the past had a few of these things where you're like, I don't know what this message means. It's definitely not on that line. What is going on here? So, let's say we have an example with a caching backend powered by the database. The call and code doesn't really carry. Any kind of caching backend is fine as long as it implements the right interface of null memory cache database. So the caching layer, if it can't cache something, for instance, throws a predefined exception. Fine, right. That's not the problem. If the problem is not in the caching layer, perhaps it's with the database. All right, so actually, there was a database connection exception that was hidden by the caching layer exception, but that was the root cause of the caching layer exception. But maybe the root cause of the database connection problem was something with a Linux demon. So that is one piece of art that we need to learn. How do we get to the root cause of our problem? And the way that we write our error messages, or with exceptions, the way that we chain them, helps people, like ourselves, discover the actual cause of the problem, get to the right location as soon as possible, and start inspecting what's going wrong as soon as possible. So you need information. Well, what is the environment? What does this go? So did it happen in PHP, or Python, or Java, whatever you're writing in? Did it happen in a piece of software that you're integrating with? Which part of the software did it come from? And like I said, exceptions can be chained. One exception can be caught. A new one can be thrown with a reference to the old one indicating like, all right, there's a new problem, but it is caused by the old problem. PHP is not always very good at displaying that. Python is excellent. Like if something goes wrong, if you look at the CLI output, you see exception, exception, exception, exception. Latest call first, something like that. So you know exactly in which order the problem started occurring. You know what consequences are. You can see exactly what the root cause was, but because you see all the following exceptions, you know exactly how big the problem is in your application and what the impact is and which other systems are broken because of this root problem. So we said chaining. How did the program get to the problem? Chaining is one way of indicating that. A stack trace is the other one. So what was the entry point of your program? In Drupal, that's usually index of PHP. And from that point on, what happened for the program to get to the part of the code that caused this problem or where the problem was first detected. Those are two different things. A stack trace is usually the right way to do this. With exceptions, most languages already provide this. They don't always print it by default. So the way that we handle our exceptions in Drupal, we catch them and we log them, but we don't always include an exception. Do you know that there is always a stack trace. There is always this last thing, is always there an exception. So if you don't see it, you can ask for it. In PHP, the exception class has a get stack or get stack trace method. They get you exactly this metadata. Really, really useful because a function may be called from different environments, different other scopes, and that is defining in what causes the problem. So once we found the original location of the problem, we need to understand what happened. What were we trying to do? What was the expected result? Same question as you asked your client. Ask them to yourself. Why could it have gone wrong? What might have been one of the circumstances? And again, this is the chaining part again. What are the underlying problems? Is this really, really the root cause of the problem? And this is especially important in deciding or whether or not you're gonna use a workaround. What are we fixing? What are we working against? Is it really about the symptom and is there no time? Patch it, document it, look at it later maybe. If it looks like something you might have patched before, maybe look for the underlying problem because if it's happened before, it might happen again and at some point the client's gonna run out of patience. Documentation is also really important. Comes in many, many different forms and shapes from a code comment to a well-written error message like you can see here and this is from Elm. Not that I write Elm but I do love the way that it throws errors. It has a line with a couple, it works with a couple of data types and it calls them patterns when it tries to parse the code and we're working with days of the week. Oh no, these are not days of the week. True and false was this but I was confused because it was about days of the week, not this line and it can't find true. It's like I have no idea what you mean but because it knows its scope and environment, like Elm analyzes what does exist, like it knows that true does not exist. So it's like all right, what can we find that maybe looks like what was typed so can we make a suggestion like oh, did you make a typo? Is this the other thing you wanted? It knows that we have a type true and it suggests that. If you're interested in this, look it up. Elm is extremely good at giving long verbose error including, and that's the most important part, the suggestion of where to look to solve the problem. If you write your own exceptions, include at the least a URL to documentation. Suggest, don't say, suggest that oh, this might be the problem if, look here, see if this matches what you're experiencing because it greatly reduces the time to inspect the problem to read and understand what's going on and to fix it, reducing stress levels, improving the accuracy of your project planning. And this is really important, especially in the public facing side of what you're building. If you build a library, for instance, even if it is for your co-workers, what are the few public functions that people are usually talking to from outside? I don't wanna have to dig through your internals, of the internals of your library to find out why that one public method that you documented I should use is not doing what it should be doing. I will dig through it and I will dislike your library and may not use it again. And that's not what you want because you want that solid, loyal user base because you want people to like your stuff. You want people to reuse it so there's a feedback loop. They will suggest features, they will report bugs instead of being grumpy, fixing it themselves and never coming back again. Make robust code. Don't make assumptions. If you've played around with strongly typed languages, for instance, those don't make too many assumptions. And even those can benefit from things like validation. You need to know that what you're dealing with is actually as expected. I'm a very big opponent of things like duck typing. It's great and fast and it requires you've got a lot of knowledge inside of your head and it relies on documentation. And when things go wrong, they will usually go wrong a little further than where the original problem occurred. If you pass on the wrong failure to a function, it goes wrong somewhere inside of that function but it's not the function's fault, it was the fault of the calling code giving you're passing on the wrong data type. And I've wasted so much time on silly mistakes like that that I forget it, I'm not doing it like that. I want my validation on my input and my output. I want to make sure that what I write, that it gets what it expects, what it should be getting, what it tells other developers to give to it. So function input, function output are objects. Do they guard their internal state? If an object says like entities in Drupal 8 they have all kinds of validation mechanisms but they're not used runtime. So we have entity validation in Drupal 8 core but an entity at any given time can be in a completely invalid state. In practice I think we only use it for form validation which is really useful in most cases but say you do a mass import and you forgot this tiny little processing thing or mapping thing to your entity type you will never know until things go wrong when you actually test them which might be after the launch to production. Different kinds of systems appear in different kinds of levels from a low level function to a whole network of applications and machines. When something comes from outside or goes outside you should try and validate it. It needs to be simple, match constraints and we usually do that with form input but web service responses are also really important. If you give me, if you have a nice web service you document, you've got a nice page that says it's got these fields it should have that data, that's great but my software doesn't read your documentation page. My software does validate your response using your schema file and can then decide whether or not it validates and whether it wants to process it. I've written clients, if they get JSON response or XML response they validate it, if they don't they'll log the problem and fail gracefully rather than traverse down the JSON tree and then find that one property missing and bam, PHP error. Why should PHP on your side fail if someone else gave you the wrong JSON response? It's like, no, my stuff shouldn't fail. Validation helps with that. Output is very similar, just the other way around. There is a saying, I think it says be liberal in what you accept, be strict in what you output or what you give. With validation I'd say it's the other way around because if someone else fails, again it's their problem, if I output the wrong JSON, it's the PHP application's responsibility to make sure that it doesn't trip over what I did wrong. So validate your input, make sure that once it is in your system it is reliable. One way to do that is using constraints. If you've done PHP in Drupal 8, you may have worked with the validation plugins based on symphonies constraints, constraints system where you basically build a data structure with descriptions of what should be contained in it. And then all at once you validate your data structure against that descriptive structure. You don't do a lot of individual checks, that's what the validation tool does for you under the hood. You just build your outline, your spec sheet basically, and you match it against what you actually have. And a static schema like a JSON, a JSON schema or XML, was it XSD or RelaxNG, they're all static schemas. It's not code that runs, does the same thing, PHP symphonies validation component, lets you build the constraints in PHP so it is dynamic, but eventually you get your schema and you map it or you match it against your live data. You can use white lists, black lists, enum types, dynamic lists, like if you write a form element, a selected element in Drupal like hash options is a dynamic white list. If it's not in the white list, it doesn't get through. Very, very simple way of checking whether it's allowed. But on a slightly more low level, we already mentioned it earlier, type checking is really important. It's like one of the fastest ways of ensuring that your data is at least in some way correct on a code level. You can document it as being an integer or as a string or as an object of a certain type, but if you don't actually check it, you're doing duck typing and if it goes wrong, it generally goes wrong a couple levels down. The moment you try and call a method on an object that and the method might not exist, the object might not be an object and then you gotta figure out where that came from. If you use type hinting, the language will do it. If it's a first language on the fly, it will do it on runtime. Like PHP will tell you, oh, ad was called with something that was not an integer fatal error. Ad returns something that is not an integer fatal error. So this is one way of doing it really fast. A compiled language really is compile time. Python, for instance, has a similar thing. It doesn't enforce it on runtime on the fly, but there are tools to do that. PHP also makes it optional. I think it's useful if you haven't tried it, try it once because it also makes you think about the simplicity of your code. It's impossible while using type hinting to make this return a combination of different values. Have you seen PHP functions in older Drupal code being like, oh, this returns an array if this is the case? If that's not the case, we'll return an object and in the case of an error, we'll return false. You've got three data types that may possibly be returned by a single function. So that means you call the function and then you've gotta have a bunch of S statements trying to figure out did that function really do what it needed to do? This is so much simpler. Because the validation happens once. If you need to use these if statements to figure out what the return value was, you need to do that every single time you call the function. If you put that responsibility inside the function itself, you've read it once and you're done. And nobody else has to even think about using it. It's not just the work you save, it's about the mental capacity that you save later on in people that are using your code. So type ending does ensure that on a low level, something is of the right type. If you make sure that something is an object and even if you use duck typing, you wanna call a method on it. If you ensure it's an object, you will never get an error that says calling method on non-object. That is already prevented. If you say it's a string because you wanna receive an email address, cool, at least you know it's a string. You can apply string length functions to it, sub string, you can hustle it around. But you don't know if it's a valid email address. That you don't know. You'd have to call your email validation function every time and again. So what you do is you can make a value object. And objects are really great because in most languages, they have the potential to guard their insights. You know, if you wanna check out my heart, I'm gonna fight you to the death. You could technically take my heart out, but I'm not responsible for the consequences. You could technically dig into the insides of an object, but then you're purposely diving into a big volcano you might not survive. So technically speaking, on a low level, you can do a lot of things wrong, but if you're just behaving like a normal developer, objects are great at guarding their internal consistency, their integrity. So if you create an object, for instance, for an email address, this is a little Python example. This is a constructor which takes an email address. It validates it. And if it doesn't validate, it raises an exception. If you raise an exception in an object or in a class's constructor, that means the object that you were trying to instantiate is never actually instantiated in the first place. So you get your exception. The instantiation phase is broken off, which means in case of an error, you will not ever have an object. If there were no errors, then you will have an object and then you'll know it guards its own integrity. You will know that it's always, it always contains a valid email address. And if you make the property private, which in Python I should have done using, a little underscore here, and in PHP you can actually mark your property as private so they can't access it from outside unless you gut the class, you take out its internals and you start messing around with it. But the runtime, you don't do that. So you know that if you get an email address object from somewhere, you know that it's always going to be valid. It's always going to have an at sign, it's always going to be domain at the very end. I can't say what else an email address looks like because that's a regular expression this long. So when you type in on this in any function, every function knows that, oh, I'm getting an email address object. I know that this thing guards its own internal state and that it makes sure that it's valid. So I won't have to call the validation function every single time I pass on an email address to some other code as I would have had to do had that email address been passed on as a simple string. So you centralize the validation in your value object and then you benefit from that elsewhere in your code. Who hates these? Who hates exceptions? Who has a terrible, terrible experience with exceptions? All right, we need to help you guys. They're amazing. Because if something goes wrong that seriously impacts your software, don't you want to know straight away rather than after the plowing to production? Exceptions are the most in-your-face error handling you can have in most languages. They are thrown immediately. They bubble up the stack. They're in-your-face, they have stack traces, information about which line and file they come from. They can have messages, they're objects. So for specific error situations they can be extended with all kinds of properties and other metadata to help you inspect the problem. I find them wonderful. They're annoying when you have to fix them, but on their own, they try to be helpful. So an exception is really another fancy word for an error that is fatal. Don't think about recoverable fatal errors in PHP. That's an identity crisis thing. These are actually fatal. This is, if a system throws an exception, it is a system telling you like, I give up. If something's happened that I just, I'll be back after the weekend. However, we mentioned the call stack, you know? This, an exception might be thrown in a function, call it from another function. So what happens is the last function to be called throws the exception, gives up. And then the exception passes through the function that was originally calling the code that threw the exception. That function might be like, oh, wait a minute, wait a minute. I know how to deal with this problem. Cool. So you're in a different scope. You're in a different function. And in that scope, the problem no longer is fatal. If we go back to the example of the caching back end we had earlier, the caching system, by kind of by its nature, a caching system, it's kind of like whatever. If it's not there, it's not there. That's what a cache is for. It's nice to have, but your system will run fine with no cache or with badly working cache. Badly working cache. Okay, I should not have said that. But if a cache pretends to save something, to cache something, and it doesn't, it's like, nah, nah, nah, nah, nah, nah, if your system does not care, because the next time the cache do you have is like, no, all right, I'll build it from scratch. It'll be a little slower, but it'll still work. So if you have a database cache back end, and my SQL is like, I'm not here, bye. I'm having coffee. Database connection error exception, fatal handler, whatever. And your database cache back end class is like, that's not a problem. The calling code doesn't care. We'll just log this problem. So a developer can look at the database connection, but from a caching point of view is like, if we can't cache it, that's not a big deal. So in the caching sense, a missing database connection is not fatal. So it bubbled up the scope, and then it was caught. We'll see two slides from now, what that's gonna do. This is how you raise or throw an exception. In most languages, it's just an object that inherits from a standard exception class. So it has things like an error code, message, and a stack trace. But you can subclass it whichever way you want. And that's how you do it in PHP and in Python. A bat function call exception can indicate something like, oh, arguments weren't really good. And a bat method call exception is just because it's a specific kind of function. Otherwise it's the same. And in Python, a Unicode error is a kind of a value error. And Python is like invalid argument exception. But the cool thing is that here we can see that when we cache exceptions, which is to handle them, we can type in on the individual exception classes. So the reason we would subclass exceptions is so we can handle the individual ones when we see fit. If we all use the same top level exception class whenever something goes wrong, it is impossible for another piece of code to distinguish one error from another and to say that it can handle some of them, but not all of them. The moment we use these custom class names, and it might just be for the class name, we might not want to add any metadata to these subclasses. We might just just want to use it to indicate an individual problem, but then we can handle this. So in the caching layer, the database cache backend, it will only cache an exception that relates to the database connection. Because everything else, it doesn't know how to handle. It doesn't pretend that it's not fatal. So if it doesn't cache it, it bubbles up further down the call stack all the way to where your code started working, all the way to in Drupal index.php. What happens then? It's a fatal uncaught exception error. Like we saw in the code, you have a try catch block. This is where the code goes that tries to perform the potentially fatal tasks. And you can have one or more of these catcher except blocks after that for handling individual problems. But when problems can't be handled, so when they remain fatal, that one, it goes all the way up to the top. And if at the top there's also no catch block or no handle block, the whole application stuff's working. Your PHP or Python parser doesn't crash. It's just like, oh, the program apparently said still, like even if it bubbles up, that means that the top part of the program sanctions the fact that there is an unrecoverable fatal error. It's like, there's nothing else to do, but to terminate the program and fail nicely. It outputs, most languages output the error, mostly in a very ugly way, which is why Drupal 8 Out of the Box gets you a nice white page with it. The website encountered an unexpected error. It's totally useless for anyone, there's one positive point about it. It doesn't tell a potentially important visitor of your software what exactly went wrong internally. That's why you get this cryptic error message. That's why we have this top level catch block of any exception, if it reaches this, yes, we'll terminate, but we'll make sure that we don't expose any of the internal information. So when do you catch an exception? We already discussed, you don't want to have to write a catch block that catches any exception. You don't want to have to type in on the top level exception class and be like, whatever goes wrong, I'll handle it. I don't believe that, you can't handle that. Do you believe that? Because if you don't, if you do that, there is the problem of the disappearing. And while disappearing problems are really nice in theory, it also means that if they're not really solved, they'll resurface or they will be hidden while you still need to look for them and inspect them and find out what went wrong. So, catch what you can catch. Catch what you can handle. Yeah, if you write code that is designed to recover from a specific problem, catch just that specific problem. If there is no subclass to identify just that problem, file a patch, create that subclass so you can catch any instance of that class and nothing else. The reason you could theoretically prevent anything from bubbling up further from not doing anything, but that's bad communication. If it is an exception that if you're writing some testing code, you're like, whatever, I don't really care, it doesn't do much, then you can catch an exception and not do anything. In any production code or any code that actually needs to go out there and other people need to look at, don't just let it disappear. Log it, clean things up, but then either re-throw it or throw a new exception or output the contents of that exception to an error page, just make sure that it doesn't disappear because then, again, it becomes too difficult to inspect the problem, to maintain your code and people's moods and the schedule goes to shit. Again, because it happens. A couple of the things you can do is clean things up. What we do in Drupal is in the database layer, when making a transaction, a couple of different database tasks are performed in the tri block of the code. We use the database transactions to prepare everything and databases allow you to prepare on multiple different statements and then execute them all at once. They provide a rollback functionality. If during the preparation of one of these statements, anything goes wrong, you can tell the database engine to roll back the entire transaction in the block in which you catch the exception. Cleanup, maybe you have half-processed files. You don't want anything to remain corrupt in case of an error situation. One of the very useful things you can do in a catch statement. You can throw a new one, exception chaining. If in the database, there's a database connection error, but on a caching level, the caching system is required to throw a generic caching level. We catch the database one, we create a new caching level exception. We chain the original database connection exception to it. So the caching exception bubbles up. That's what reaches the developer, but the developer can also see, oh, this is not the original problem. The original problem was with the database, including all the full stack traces. So how to catch? All right, if the problem requires developer action, make sure to notify them. Don't let things just go to a dark place to die in peace because there will not be peace until it's solved. Your developers will remain sobbing after 5 p.m., trying to solve the problem and curse at the mysterious developer who is you, but who will not name themselves for hiding the problem from plain sight. Transparency is a really, really important thing in this case. So this is not generally considered to be defensive programming, these items, although I have had a few questions about them and they are really, really important in preventing a lot of obvious problems. If you integrate different systems, code execution problems can occur. Evel is also an integration level problem because you get your PHP code from elsewhere or your Python code from elsewhere and then you evaluate it in your system. It's hard to sanitize, don't do it. You're using corruption authentication to make sure that even if you get input that is in the right format, it is actually from the right person or from the right server, are certain kinds of validation of making sure that your assumptions are correct, that your code does not feel internally because someone else messed up accidentally, not technically code level defensive programming. So the benefits of this, you're faster, even though you slow down and you think about things a lot more. You postpone your coding for a day or something while you design. In the end, you're gonna be faster because you waste less time on the reading but also on the bug hunting. It's less frustrating. I like people around me who smile, who love their job, we're gonna be like, hey, part. It is a great thing this morning at work. Yeah, lots of problems, but oh, we managed to solve it. We managed to ship it. Client happy, we're happy. We're going out for lunch. You wanna join? I will join those people for lunch. It's great. You do reduce the friction. Grumpy people are annoying as hell. I get annoying as hell when I'm grumpy. I work remotely, we have a lot of stuff on the chat and people are like, hey, how's it going? I'm like, what do you mean? You sound, your sentences are shorter than normal. I've been debugging this morning. Yeah, we figured as much. Bring that down as much as you can. It happens, but it makes the work so much more fun. And especially because you work, not just with a chain of exceptions, but also with a chain of people who depend on each other. You've got your development teams who depend on non-team member experts, like you need a designer on the team for a week, you need your server devil's person on the team for a week. And then it goes to the clients and the stakeholders that might be user testing involved. There's such a long chain. It is so easy for frustrations to grow in size because so many people are involved. Keep it down, keep it transparent, keep it predictable as much as you can by keeping it simple, anticipating problems. Fail early, failure is good. Repeat after me, failure is good. Cool, now fix it. That's why I love exceptions. They're in your face, type hints too. It's just, I like a machine to tell me that I did something wrong rather than a human being because I don't care about wasting the machine's time. If I can find some of the problems before I even push something to the repository, before I even create my PR, that's great. Saves a lot of their time. They won't have to look at it. It's annoying. When you review someone else's code, it's distracting. If all these little things are off, you're distracted by that and you're not looking at the logical code that you actually need to review. It's really annoying. If you can find these things early, have automated tools on your locally coding standards, static analysis, unit tests, because unit tests also run your code. They don't actually verify the behavior. They also run it so you know that your type hints are correct, et cetera. So a lot of the low-level stuff can fail early like that. You can fix it and pretend nothing ever happened. Fail often, have test builds really often, static analysis really often. The fastest stuff is and the easier it is to only run these low-level tests. We now do it within the company, pre-commit hooks, run all the static analysis tests and the unit tests. It's a, yeah, you'll get a few extra commits. Like if someone forgets to do this before they commit it, you'll get another commit. This is a fixed shit. Coding standards. Fine. It's fixed. I'd rather have someone pretend frustrated with it before they commit something than that things explode during a peer review. Harden your entry points. Whenever it comes into your system, that's an entry point. Index of PHP is an entry point. CLI command could be an entry point. You're a client that you're writing to consume a web service is an entry point. But also when you write a library and you publish it and you document it, you read me like, oh, you will likely have to use only these three functions out of the, I don't know, 100 that we have. Harden those. I'm sure the error messages there are good and solid because that's the first impression people are gonna have from your code. That's the first impression that might be the last people will be having. You don't want that. After a bit of delay and the door's opening now, we have two minutes left. Do you have any questions? You go super-calator. That's not a question, but thank you. I'll be around, if you really wanna ask, find me, I'm on Twitter, Bart Veenstra. If you can't find me on Twitter, which I doubt, find me at the druid booth today, tomorrow. I'll be mentoring on Friday. You can find me there as well. Think about this. That's the most thing. You don't have to do everything exactly as it was mentioned here. I want you to think about your code. I think the only thing that I really want you to do, as opposed to how I've been talking, is to slow down. Think about your code. Make sure it's gonna be fun. Thank you.