 Next up, we've got OptiGram, he's giving a talk called Confident Code. I think some of you are going to be pleased, this is a much more technical talk than a lot of what we had yesterday. So yeah, let's welcome OptiGram. What is Confident Code? The easiest way to find it is in terms of what it is not. So, in order to do that, I want to just tell a little story. There were these three guys in a boat out in the middle of the lake. A doctor, a lawyer, and a priest. Well, if you Jewish, you can be a rabbi. Anyway, a doctor, a lawyer, a priest, or a rabbi. And they're out in the middle of the lake. Wait a minute. They were climbing the mountain. No, I think it was the lake. Pretty sure it was the lake anyway. So they're out in the middle of this lake and the doctor says to the lawyer, oh, before I tell you that, you have to understand that they've been out in the wilderness for two weeks at this point. And so they haven't seen, this story isn't going very well, is it? What is wrong with this story? There's a few things wrong with it. For one thing, it keeps on going on these tangents and digressions. There's a certain lack of certainty to it. And code can have these qualities as well. Timid code is code that lives in fear. It is completely unsure of itself. It's also, as a result, prone to digressions. It's constantly second guessing itself. It tends to randomly mix together input gathering, error handling, and the business logic. And the result of all this is that it imposes an extra cognitive load on the reader. So confident code, by contrast, is a style of method construction that says exactly what it intends to do without any provisos, digressions. And like a good story, confident code consists of methods that tell a consistent narrative story. They have a narrative structure. The demo code for this talk is online. It's something I kind of put together for this talk. But it's all built out of stuff that I've seen or written in the real world. It is an interface to the very important cow say utility in Unix. Put some text in. You get a little cow graphic out, ASCII art graphic. And it's got various options. You can change the eyeballs. You can change the whole animal. You can make it a thought bubble. Various options. So you can install and play with it if you want. And this is, there's a real basic example of how the cow say.rv works. It's just a thin wrapper for this where you call the instantiated cow object. And you call say and you give it what you want the cow to say. Now this is the sort of the main top level method, the say method. You can all read that, right? What I actually want you to look at here is not what the code says, but I want you to look at more of the structure and the flow of the code. And in order to help you do that, I've marked it up a little bit. So I've marked it up in terms of four parts of a method. You've got the input gathering, you've got the business logic, the work of the method, error handling, and then delivery results. And what you can see here is that it's all kind of jumbled together. There is no real flow to this. I want to, over the course of this talk, I want to show you a structure of method construction that's based on this narrative idea. And where the method consists of gathering input, performing work, delivering results, and necessarily handling failure in that order rather than all mixed together. And over the next, almost 25 minutes, I'm just going to go over those four parts and demonstrate a few ways to make those parts a little bit more confident straightforward. So the first step is input gathering. And in order to have competent code, we need to be sure of our inputs. So when you talk about input in Ruby, you have to talk about duck typing. And duck typing, at its best, duck typing is a very confident style of code construction. You know, you don't ask objects about themselves. You don't ask, are you a duck? You don't say, can you act? You just treat the object like a duck if it is not, you assume that it will. The opposite of duck typing is something called type casing where you're switching on the type of an object. And this could be as simple as a case statement that has a list of types in it. Or it could be checking the existence of a method. That is a type check as well. You're checking the type, the kind of that object. It could be a check for nil. Nil is a type, nil class. And when you're checking for nil, you are doing a type check. You're doing this type casing. And there's actually a code-smell name for this. It's called the switch-smell. And in my experience, in Ruby, the most common kind of type casing that you see is checks for nil. So there are, I want to show you three strategies for dealing with uncertainties in input. First of all, when in doubt, coerce your arguments into what you want them to be. So Ruby has these great conventional methods like 2S, 2I, and the erratification operator, as I call array, which will coerce most objects, support them, and it will coerce the object into what you want it to be. And if you look through the Ruby standard libraries, they use this stuff liberally. They use it everywhere. It works out really well because you can do stuff like this. Right here, I'm creating a path name object, which is kind of a wrapper around a system path name. And I'm passing it to open. And open is defined in terms of string. Open expects a, if you look in the documentation, it expects a string, which is the name of the file open. But you can just, but I can just pass this path name in instead. And it just works because internally what open does, one of the first things it does is it calls 2S on that argument. So if you know what you want, don't be shy to just throw these everywhere. You know you need a string, put a 2S on it. You know you need an array, put a 2A on it. Or a 2I for integer. If you know what you want, ask for it and let the object give it to you. Array is really cool. Like I said, I think as the arrayification operator you can give it pretty much anything and it will give you a sensible array in return. So if you give it an array, it'll give you that same array back. It won't wrap it in another layer of array. It'll just give you the same array back. If you give it a singular object, it'll wrap that in an array so you get one element array. If you give it nil, it won't give you a nil inside an array. It'll give you an empty array, which is generally what you want. So very useful coercion. Especially in 1.9, a bit more general than 2A. So here's an example of using array. Here's some code from Cal State. It's taking the list of messages. You can pass either a single message or a list of messages in for the Cal State. And the original code has this case statement where we're checking the type of the message argument. And it's this big sort of interruption in the flow of the code where we try to figure out what somebody passed in and do the right thing with it. Apply array to it and it becomes a one line. Sometimes the default coercions don't give you what you need. Sometimes you need a coercion to a type that's a bit more specific. And in this case, the decorator pattern can be very helpful. Decorator is a pattern that consists of an object which just wraps around another object, passes most of its calls through that other object, but also adds a little bit of extra. So here's some candidate code for that. In the Cal State method, we want to record for debugging purposes. We want to be able to log where we put the output. And then there's this option, this out option that you can pass in that by default it's going to return the output as the return value. You could also pass in like a file object and then it would write the output to that file. Or you can pass in like an IO object or anything that supports the append operator and it would just put the output there. But we want to be able to log where we put it for debugging purposes. And we've got this huge chunk of code right in the middle of that method whose sole purpose is to record the name of where we put that output. Because none of those, all those different types of objects, they don't all support a common interface. So we've got a path, if it's a file name or if it's a file object we've got a dot path on it. You know if it's nil we can just say it's the return value. If it's something else well we can call inspect on it. You know there's no common interface there. So here's the code that kind of wraps those in objects that will give all those different inputs a common interface. And what you see here is this is still type casing. Often you can't completely get away from type casing but what you can do is kind of isolate it to one place. So what this is doing is it's taking those different input types and it's returning some things with common interface. So file goes through un-belested because we're just going to use the dot path method, other things we're returning some special objects for. And the null one isn't very interesting but what's interesting here is this generic sync which inherits from symbol delegator. That's a Ruby standard library, it's in the delegate library. And what it does is just gives you a very, very simple delegator, very simple wrapper object that will pass every method call through. But then you can also define your own method calls in there to override things or to add things. And so what we're doing is we're defining the path method in terms of the wrapped objects inspect method in that case. So when we apply this to the code it becomes a one-liner. We just pass our out option to that cousing method which is going to give us something with a dependably consistent interface in return. And then we just confidently call dot path on that because we know no matter what we put in there we're going to get something out that behaves like the sort of doc that we're looking for. The second way to deal with input uncertainty is to reject unexpected values. There are some values that you cannot coerce into something you want. They are just wrong, there is nothing good that you can do with them. And eventually, if you left them alone, eventually they'd probably cause an error somewhere down in that method. They'd probably cause a no method error or something. But it might not be immediate. It might be somewhere down in the method. It might not be clear why you're getting that error by the time it actually raises an exception. And it might do some damage before it gets there. So to avoid this, confident code needs to be assertive about what it accepts and what it doesn't accept. It needs the statements needs upfront. What we're talking about here is preconditioned. That's part of the design by contract discipline of programming. But you don't need a whole design by contract framework for this. You don't even need an assert method. Ruby doesn't give us an assert method out of the box. A simple precondition is something that looks like this. I'm saying there's this cal say option that defines sort of the template of the cal. And there's all these different template files. I mean, there's like a stegosaurus with a top hat and stuff like that. And seriously, you guys have got to play with this while I'm talking. And it defines this. So we've got this cal file option. And this is just a check at the very top and saying, if it's defined but it's blank, we can't do anything with it. There's like nothing. It would just break things. There's nothing we can do with that. So we're going to reject it outright by raising an argument here. So that's fine. I mean, that's a precondition. It's a little bit verbose. So if we want to make it a little clearer, we can define our assert method of our own. And that becomes more of a one-liner. Third way to deal with unacceptable values is to simply ignore them. So one way to do this is with a guard clause. The guard clause is very similar to a precondition except it doesn't raise an exception. A guard clause simply exits the method early, which just returns early. So we've got this guard clause at the top of the same method which says if the message is nil. Yeah, we're type checking. But if the message is nil, we can't do anything with that. There's nothing we can do with that. And if we let it go, we're just going to have to keep putting special cases further on down the method that are checking for nil. So we put this on the outskirts at the top of the method, at the top level of our API. We don't let it go any further. We just return an empty string. Some arguments have to be special. In any reasonably complex method, there are often cases, there are often some kind of special case that you can't avoid. And an object-oriented way to deal with special cases like special flags is rather than putting a lot of these statements in to create a method, or not a method, an object which represents that special case. And when you do this, you find you can avoid a lot of if statements. You can avoid things like try, which are basically compressive statements. So here's an example of some code that is somewhat uncertain of itself. This code, what it does, it checks the process status variable after the subordinate, how say, process has finished and checks to see if there was an error in running that process. And it's using the dollar question mark variable in Ruby, which is the exit status of the last process executed. And the trouble here is that variable may contain a process status object, or it may be nil. And so you've got a couple of things here. You've got, let's see, up here, we're using and and. We're saying status and and status.exit status. Exit status is the method that we're actually interested in. But we have to first check to see if that status is even fine. Down here, we just do it a little bit differently. It's the same thing. We're just using try for macro support to do it. But in both cases, we're uncertain about the existence of this object. We can replace that with a special case object. All we care about is .exit status. We don't care about anything else on that object. And so here's a little special case, which I'm just using an open struct to just create a little ad hoc object. Not even creating a class for it or anything. Which, if that status variable is nil, we'll just replace it with this little ad hoc special case object, which represents the exit status as zero. And that way, we can now confidently call status.exit status without worrying about the case where it's nil. No more tries and worries. The special case that you see most often is the special case for nil. Nil flag, some special handling. And the special case handling for nil is more often than not, do nothing. So there's this special case of the special case pattern called the null object pattern. A null object is an object that responds to any message, any method call, depending on the implementation, either nil or with just itself. So it's a very simple pattern to implement. Here's a basic null object. It's just a basic object which defines method missing to return self. So any call you make on it is going to return self. It also reports itself as being nil. And then it's handy to define a little helper that I'm calling maybe here, which is just going to check its input value. See if its input value is nil. It's just going to return a null object instead, otherwise it's just going to return whatever you're passed in. So this is a way of converting nils into null objects and leaving other values on the list. This is what's known as a black hole null object because it returns itself from any unknown calls. You can safely make arbitrary chains on one of these. So if you can start out with a, grab the original object that might be nil with a maybe, and then you can just chain a bunch of calls on that. And if it turns out to be nil, that'll be a null object and each successive call will return the same null object and so it just nullifies the entire call chain. We can use this in the CalSE code. So we've got this if statement where we say if the out option is set, then write the output to it. If it's not set, we don't want to do that. We can replace that with our null object and then confidently write the output to that because we know that even if it's nil, it'll silently do nothing. You may have noticed that a lot of these patterns are sort of tackling reducing the number of nils in code. I think nil is kind of overused in Ruby code. It means a whole lot of different things. It's the default return value for a lot of Ruby constructs. It's, you know, the value of uninitialized variables. It often flags certain cases and you see these nil checks all through Ruby code and they tend to just sort of interrupt the flow with this minute shift. So I try to get rid of these where I can. One thing that's helpful for getting rid of these nils is to use the hash fetch method instead of the square brackets for getting values out of passes. I'm just familiar with fetching. The number goes up every time I get this talk. This is great. So it's very simple. It's similar to the square brackets except you have this fallback action in the block. So you can say, give me this key. If the key isn't there, then do this thing. You can use that a couple of ways. You can use that as a very simple assertion. So if you don't give it a block, it'll raise an exception if that key isn't there or you can define a key raise if that required key is not there. You can also use this for defaulting. So instead of raising an exception in that block, you can just set up the default value in that block and it's a bit more, I think, a bit more explicit than using a word and reduces the number of conditionals in your code. A nil is often used as just sort of the generic default value for things. It's not defined, given a nil, whatever. And then you get these error messages that look like this. You know, method error on nil. Now, what if we, instead of using, instead of just relying on the nil default value, what if we supplied a symbol as the default, as the default fallback value for something? And anybody tell me what is better about this error message? You could search for where it came from. Exactly. It's got this string in it, so you can search for it. It's got this string in it, no log or set. You can grep for that. And you can find where that default value was and you can see where you failed to set something. So I really like to avoid using nil as the default value for any kind of option or variable in my code, and I think it helps a lot. Another thing you can use, that's a good example of something that, you know, you want to break when it's not set. Sometimes you don't want it to break when it's not set. Use a nil object, instead of using a nil, use a nil object. This is great for loggers because, you know, you want logging to happen sometimes, but sometimes you don't care. And so you can just use a nil object for your logger and, you know, nothing will happen whenever those log statements occur. And then you can pass in a logger and it will start logging. So the second step, the second of these four parts of method is performing work. And here I just like to keep the focus on the work and not on the other parts of the method. There are a couple of styles of work that I find kind of lend themselves to confident code. The first is chaining work. We saw that a little bit earlier with the nil object. And that's why it's nice. It gives you a way to very easily say, do all this stuff if this initial value is here. If it's not here, turn all that whole thing into a nil object. And that's when you chain stuff together and you can very easily do that with a nil object pattern. And another sort of related pattern is the iterative style. If you're familiar with jQuery, you use this all the time. If you have a jQuery selector that returns no results and then you try to do something with that collection, what happens? Nothing happens. It's just a nil. And the thing about single object operations is that they are sort of implicitly one or error. So it's either there it works or it's not there and there's a problem. The iterative style methods are implicitly zero or more. They don't have that error case. If there are zero things, then zero things happen. Also, it uses this iterative style. You can pass in a list of messages and it'll return a cal for each one. If you pass in no messages, it'll return no cals. And there's no, there doesn't need to be any special case checking for that. Step three, delivering results. I don't have a lot to say about this except be nice to your callers. Try to think about a way to give them something if they're depending on your return values. Try to think of a way to give them something other than a nil. Give them something a little bit more expressive than a nil. The final step in these four parts of a method is handling failure. And here I just want to say, if you can, try to put the happy path of the expected business logic first and then put the error handling at the end or try to extract it out to other methods. So a few ways to extract your error handling out. You can use a, actually this is an example of what I don't like to see in code. This is a failure checking digression right in the middle of code. So we saw this code a little bit earlier and it's just right smack in the middle of the code. We run this process and then immediately we have to check for an error status before we move on. And it just sort of, it interrupts the narrative flow of that method to talk about something that actually doesn't happen that often, an issue that doesn't really come up that often. So it's very disruptive to the reader. You can extract that out into something called a bouncer method whose sole purpose in life is to either raise an exception or not, so it just checks a value and based on that it raises an exception or it just does nothing. So here's an example of applying the bouncer method to that code. We just wrap up the code that needs to be checked in this bouncer method and it's much less disruptive to the flow of code. Another thing you can use is something called a check method and this is a great way of sort of getting rid of the begin, rescue, end blocks in code. I really don't like the begin, rescue, end idiom. I actually kind of think the begin is kind of a code smell. What you tend to see is these blocks of error handling right in the middle of some business logic because there's some case that we have to handle and it just completely throws off the story of the method. This is a great example. There's this rare case where you get an e-pipe error out of that process and so we have to wrap and we have to check for that every time we interact with the sub-process and the check method is just a method that takes one of the library methods and adds a little bit of error check into it but otherwise behaves exactly the same way as the library method and then we can apply that to the code and have it be much less disruptive. When we apply all of these refactorings we get a result that looks like this. What you can see is it's now in that narrative order it's got the gathering input and it's got the doing work and then it's got code to take care of returning results and they're in that order and all of the error handling code has been extracted and isolated out into separate methods. A few observations about this so it does have that more coherent narrative structure it has a lower complexity and I'm using that in the computer science sense of psychomatic complexity fewer branches, fewer ifs and case statements equals lower complexity and now, so that's nice but it's not, one thing you may notice that it's not necessarily shorter what you just saw was a little bit shorter but that was because we extracted all this stuff how we extracted all of the patterns and check methods and stuff like that so this is not a strategy for making your code as short as possible but it is, I think, a strategy for making your code a lot more readable. Why do we care about this? Why do we care about confident code? Why do we care about readability? What we know from research that when you have lower complexity when you have fewer paths through code it is correlated with fewer bugs in the code so that's a good thing it also tends to be easier to debug these methods structure this way tend to either fail early or work and finally, and most importantly it's self-documenting you're creating a method that reads more in the way you would explain the method to somebody and there's this old saying write code for people first that code may need to communicate intent to somebody six months later who may be you coming back and having no idea what you're doing back then so be kind to yourself be kind to the maintenance programmers coming after write your code more confidently that's all I've got I wrote a book if you use that discount code if you don't have it already that's it thank you very much