 So, Coby, I'm sorry, I'm a baser. You're gonna have to follow me. My name is Nick. I'm N-Means on Twitter, if that's your thing. This is my city, and it's the one you're in. Like you said, I build stuff at the WellMatch. I get to pair all day long, remotely, with some really smart people working on really hard problems to try to have a health care a little bit better. And I'm gonna be talking really fast to have a lot to cover, so I apologize. If you'll think back with me a few months, there was a big kerfuffle in the Ruby community. DHH got up at RailsConf and told us all how bad TVB was. But if you were listening, his talk was actually about more than that. The title of his talk was actually Writing Software. And he spent a considerable amount of time talking about how he'd spent most of his career not feeling much like a computer engineer, but like a software writer. So I got me thinking, what if we took that way too literally? Well, we were hoping to replace our rulebook with something that it pertains to writing. And there's no better choice than Strunk and White's Elements of Style, written by Cornell Professor William Strunk in 1918 and expanded by his former student, E.B. White in 1959. Yes, that E.B. White. There's been several editions of it over the years, including this gorgeous black leather, gold embossed 50th anniversary edition. There's an illustrated version if you like your grammar rules with pictures. There's even a book about the book. That's how important this book is. People have written about the process of writing the book. If you're looking for a book of rules on English prose, Elements of Style is your book. But what does that have to do with code? I'm glad you asked. So keep Ruby weird. I want to introduce you to a long lost friend of mine, shippingutilities.rb. It's part of an e-commerce platform that I wrote. It was my very first Ruby and Rails project. It shipped in Rails 1.1. And there's something that you should think anytime you see a class with utilities in the name. So take a look at this thing. That is two point font, for reference. Like I said. Some statistics. It's 427 source lines of code, which is lines that are neither comments nor whitespace. It's got a flage score, which is a measure of relative complexity of 561.1, which is pretty considerable. It's got a flage score of 70, which is a measure of duplication. Not terrible, but it could be better. And code climate rightfully gives it an F. And if you look at this first method in the flawed response, it's a 120.7. That is 21% of the complexity in this entire class rolled out of the one method right here. So what should we do with it? We should refactor it, right? Except that we can't. We have to use strong and white, because some idiot decided it would be a good idea to give a talk based on refactoring from strong and white. The thing about this is it's false on the cover. Because refactoring is not a prose concept. You don't refactor text. So we have to dig into strong and white and find a basis to start working on this at all. Rule 55, revise and rewrite. Revising is a part of writing. Few writers are so expert that they can produce what they are after on the first try. Quite often you will discover on examining the completed work that there are serious flaws in the arrangement material, calling for transpositions. But that doesn't really tell us how to get into this code. It doesn't give us any work practical to start. So let's find a practical rule. 5-3, work from suitable design. Before beginning to compose something, gauge the nature and the extent of the enterprise and work from suitable design. Well, in order to do that, we kind of need to know what this code does. So let's talk about what build shipments does. Let's say you got a shot in the cart. We've got a jacket in there that's in stock in our warehouse from Vendor. We've got a jacket in there that's in stock in our warehouse from Vendor. We've got a glove that's a drop ship from Vendor food. Now drop ship just means that your vendor ships and drive to your customer. It means you never handle it in your warehouse. And then we've got a helmet that drops ships from Vendor bar, a different vendor. So we could take these three items and we could break them up into three shipments, but that means that our customer pays three shipping fees, which they're not happy about. So instead let's consolidate the first two since they're from the same vendor. Let's just drop ship them. Well, but what if our customer's a real cheeskate and just doesn't want to pay for two shipments? They don't want to pay for one. Well, we can order all the things into our warehouse and ship it that way. That's what this mess does. All of the logic for all of that that it just took me about a minute to explain isn't this code. So we need to get it to a suitable design so we can even work on it. Right now it's buried in that 427 lines of code where we cannot possibly reason about it. So we need to take it out of that environment, put it in its own class, give it an initializer and unfortunately we have to bring its friend with it because it doesn't know how to do its thing without great group if necessary and insert. Great method, right? And then let's add a test because this code, like any good code written by a PHP developer writing this first review, I had no tests. And of course it fails. Why does it fail? Our initial likes constant Rails default logger. How many of you remember using that? So let's look at this thing. Where are we using Rails default logger? Well, everywhere. Our production code is riddled with trace debug statements. So let's go. All right, so let's check our test now so you can see it's running. No, still exploding because we have uninitialized constant ship and builder item which brings me to my favorite line in this whole class. And it is a line so nice that I used it twice. So let's look at what it's doing. It's two, not one, but two nested active record queries inside a ternary predicated on a de facto object type check. Okay, real tall. All right, so let's see what Strong and White tell us about that. Do not take shortcuts at the cost of clarity. Many shortcuts are self-defeating. They waste the reader's time instead of conserving it. The one truly reliable shortcut in writing is to choose words that are strong and sure-footed and carry rears on the way. So let's see what this line is actually giving us. Let's see where we're using the output in the class. There's kind of two clusters of it. There's a good reason for that because it's in two legs of a conditional. So let's take that top block and zoom in on that where we can actually do something with it. First thing I'm gonna do is take this line, change it from a ternary to an if then else, because that lets me do this, you're welcome. Now let's turn down the noise and only look at the lines that mention item. Those ship status symbol lines of their lookable suspicious. So I'm gonna ignore them for now and move on to easier things to deal with. The story D line again has an object type check and a ternary because I really liked that construct apparently. And we need to get rid of that. So what's it doing? It's checking to see if the item is an instance of the item model and if it is, we wanna reach through item into product and get the product store ID. Otherwise for some reason we wanna return one. The good news for us is that we don't have to care about this. We can take our double down here, teach it how to respond to store ID and that lets us take this line and call it directly on our line. Now if this was the real world, I would have to reach down into that object and teach it how to actually do that, but this is not the real world. This is a refactoring talk so I'm gonna pretend that other class doesn't exist. And we can move on to the next line. It's the exact same change. Now we've got another line and you may be noticing a pattern here to the changes that I'm making. Well it turns out Strunk and White has some advice on that as well. Rule 219 tells us we should express coordinate ideas in similar form and pay attention because this is a rule that we will be falling back on repeatedly in this refactoring. This principle, that of parallel construction requires that expression similar in content and function be outwardly similar. The likeness of form enables the reader to recognize more readily the likeness of content and function. So things that we express similarly are easier for us to understand because you understand it one time, you understand it every time. So let's take this line and it turns out we can apply the exact same change. Go down here, teach our double how to respond to that message, go back up here, put the call on the order line item and we're good to go. Now let's look at ship status symbol. Let me tell you what this is doing. This first line we call ship status symbol for quantity on item because say the customer wants two of something. Well we may only have one in our warehouse. So if they order one, great it's an in-stock shipment but if they order two then we've got to drop ship it to them because we don't have one. So once we figure out the appropriate ship status symbol we have to look and see if it's either of these specialized in-stock statuses either close out or something that we only ship from our warehouse stock. If it is we want to change that to be the main in-stock symbol to simplify. Then we go down here and we hang it on the same open structures everything else and shove it into the line item, right? Turns out we can apply the exact same refactoring again. We can teach our double to respond to that message, go back up here, make a change and that lets us get rid of those lines, right? And so now if you look at this block of code we're no longer referring to item anywhere in this block. So that lets us hop up here and get rid of that too. No longer need that, put it back in our class, look at our tests, hey we're passing but how can that be because we still have this other line that references the item class? Well clearly we're not testing the other leg of our conditional, we've got to go back in our tests and add a test for the other leg of the conditional there's the failure we expect. Well the good news is the only message that we're sending to item here is shore ID and we're sending it twice for some reason but we're not going to worry about that for now. Because we've taught order line item how to respond to that so we'll call it directly an order line item we can delete this code now and that gets our test passing. Now we're close to a suitable design but we're not quite where I would like to be and let me tell you why that is. Let's say we instantiate ourselves, new ship and build and we pass it a couple of order line items. It does its thing and immediately returns two shipments to us. We didn't ask for any shipments, we just instantiated the class that seems kind of odd to me, it feels funny. Not quite obvious enough. So let's hop on our test and change our API and our test so we can let that direct our class refactoring. While we're here we're going to grow up this test and make a check to see if we're actually getting a shipment array back with the right number of shipments and the right type of shipment but most importantly we're changing the API of our class where we instantiate the class and then call build shipment directly. Do the same thing with this test, two failures just like we would expect. So now we can go into our class and make that change. We want to take order line item and consolidate and make the matters. We want to instantiate them in our initializer. We no longer need to pass those arcs in because they're stayed on our class now. We don't have to pass them around. So we get rid of them here, get rid of the build ship that's called here, that fixes our test and our API is where we want it to be. Now I have to ask you to forgive me for what I'm about to do here. I'm going to wave my hands and finish up our tests. And the surprise, they're all passing, great. If you would like to see how to do this, I would highly recommend that you take the time to watch Katrina Owen's therapeutic refactoring talk. She has a very patient explanation of how to take legacy code, wrap characterization tests around it and then refactor it. I on the other hand, what little hair I have on my head is on fire right now because I have a lot of code to get through and I don't have time to show you. So let's look at our class again and figure out what to do next. We've, we're to the point that I think we're to a suitable design. We're to a design that we've got tests around us we can actually go in and change the code confidently. So we need another shrunken white pool to help us figure out where to go next. This is probably the most famous of them all, 217 omit needless words. Bigger's writing is concise. A sentence should contain no unnecessary words. A paragraph, no unnecessary sentences. For the same reason that a drawing should have no unnecessary lines and a machine no unnecessary parts. So let's hop back in the code and see if we can find anything unnecessary. Well, turns out we were working with something that's pretty verbose just of how we don't even realize it. It feels pretty verbose to be creating an inline object here that we're only using in one method. We've created this line item, open the struct type thingy and we're using it to drive this class. Oh, we don't want to do that. The problem is one of these lines is still not like the others. We want to express coordinate ideas in similar forms so we need to take that line and change it to a call on order line item. Now we've got to notice that something interesting has happened. Our tests are still passing, but if you look at the left, all the attributes we're setting up on this open struct and then look on the right, all the things we're calling on order line item, they all match verbatim. Except for this one line that lets us reach through and access the original object, but we can refactor that. We're not worried about it. So now that that's the case, now that we've taught our object to match the duct type of this weird open struct thingy that we're using in this method for some reason, when you're rid of it, we don't need it anymore. So now line items just equals order line items, simple as that. And we'll change this one call and let's see if our tests are still passing. They aren't. So we just got rid of seven lines of completely unnecessary code, but we've still got more unnecessary words. We've got two variables here with the same value. There's no reason for that. So let's take them. Let's take it, move it up here, change order line item to line item on our class adders. We can get rid of this line altogether. This block down here is still referring to order line items, but it's just reading the adder. It's no big deal. It's just the name change, simple switch, and we're still passing. All right. Now I know you can't see this. So take my word that there's nothing obviously needless left. Everything's doing a job. A lot of it's doing it far too verbosely, but it's all still, it's all doing a job. So what's next? 213, make the paragraph the unit of composition. This is where we get into sort of the meat of the refactoring. A subject requires division into topics, each of which should be dealt with in a paragraph. The object of treating each topic in a paragraph by itself is of course to A3. So if our methods are paragraphs, build shipments is a really long paragraph. So let's hop into it. Let's see what's going on here. If the consolidate flag is false, we do this stuff. If it's true, we do this stuff. That's the entirety of the method and those two branches of the condition. Well, those are two obvious paragraphs that we have to work with. So let's start moving them out. Let's take the bottom conditional leg and make it its own paragraph called build consolidated shipment. We'll replace it with the method call up here, check our tests, oh, we broke something. I'm defining local variable or method shipments. So we're calling a local variable and build consolidated shipment that we define of in build shipments. Well, the easy way to fix that is just to elevate it to the class. We'll take it up here, make it an adder, assign it the initializer, and we're good to go. So now let's take the top leg of the conditional and make it its own paragraph as well. So we'll do that. And you'll notice that all of a sudden, build shipments is looking a lot clearer. It's a lot easier to understand what's going on. Our tests are still passing. We've got a much clearer story, but we've sort of just taken all the junk off the cabinet but the junk warrior. We still have to address this. We can't just shove it out of you. So let's zoom in on this code and see if we can figure out what in the world it's got going on. There's three paragraphs embedded in here, three separate topics. First off, we built this line items by symbol hash. Which essentially gives us all of the line items we've got divided up by shipment type. The second giant block actually has a helpful comment in it for a change. Thanks, past me. If we've got us some end stocks and some drop ships, let's see if we can do some consolidating. It only makes sense to consolidate if we can completely get rid of end stocks. So if we can take all of our end stock items, assign them all to a drop ship shipment that we're already gonna have to make and then we wanna consolidate. If not, we don't wanna do it. And then we've got this third block that just takes all the line items and assigns them to shipments. So let's take this first block and throw it to a new paragraph. So that's great. Our test passed. We didn't have to do anything to change it at all. We just had to move it. But it's still pretty gross. What is that? Shrunk and White tell us about that. Rule 216 uses a definite specific concrete language for the specific to the general, the definite to the vague, the concrete to the abstract. So let's see if we can make this more concrete. Well, the first thing we can do is take it and make it only return while we're asking it for not an entire hash of junk we don't need. And then once we do that, we can go up top and we can change this hash access to a method call. But that's still not that much clear, not that much more concrete. What if we could do that instead? Well, try to know that we got this method down below. It's trivially easy to do that. We just set up an in stock items method. We can change all these calls to in stock items. We do the same with drop ship items. And we've had a little herbosity at the bottom, but our main method is more easy to read. It's continuing to get better and our tests still pass. So now let's take this block and see if we can figure out what in the world's going on here. This is the gatekeeper line. This line checks to see if every in stock item that we've got has the consolidatable flag set on it. And if we do, then we want to take those, all our in stock items and change their ship status to drop ship. While we're here, let's take this bottom line and make it a little bit better. Skipped a few, running short on time, sorry about that. So this top block, as it turns out, is just a conditional to see if we can convert all the in stock items to drop ships. All that junk is just a conditional. So let's move it to its own paragraph. Move it back up top. If consolidate to drop ship, let's change all of our ship's statuses to drop ship. Much more readable, much better, tests still pass. But we can still make this better. It's still not obvious what's going on. Let's take this bottom line and let's change it. It's checking to see if all of our in stock items have a consolidatable flag set. Well, let's reach into the Ruby standard library and see if we can find a better way to express this. This is a really terrible way to express this. The Ruby standard library is the software developer, the Ruby developer is equivalent of a vocabulary. So the more of the Ruby standard library that you know, the more expressively you can write Ruby. And there's a much better way to write this. If all of our in stock items are consolidatable, return true, if not return false. So let's take this line and do the same thing. We can use any up here instead. So we're no longer checking to see if this count is greater than zero. We can just check to see if we have any drop ship items that have the same vendor ID as the in stock item we're currently looking at. So let's continue refactoring. All right, so we don't want to do anything unless all of our in stock items are drop ship. We've got that condition buried in the method. Let's take it and move it up to the very top. It seems important. And we want to say what's important to us at the very top of our methods. So let's elevate that up. And if all of our in stock items are drop shipable, we'll carry on. If not, we'll return false. Let's hop in here and we can get rid of the condition out here. We don't need it any longer. In fact, we can take this all and make it a one liner. But can we do it once instead of iterating over all of our items? Let's see. If we take the vendor IDs around stock items, subtract out the vendor IDs of our drop ship items and that set is empty, well then we've proven to ourselves that the vendor IDs of in stock items are a subset of the vendor IDs of drop ship items. And that one line replaces this five lines of junk. So we've got a much clearer method there now in our test pass. So there's still more work to be done here. There's still more that we can do. We've done something very unintentional here. We've made this method quite a bit more confident as it turns out. So we can go up here to the top and look at this conditional. We don't need it anymore because our method down below is not gonna do anything unless all the conditions are met. So we can actually get rid of that conditional all together. It just goes away and our test run and pass. So now let's take this line. It takes our in stock items and converts them all to drop ship if consolidates drop ship is true. But it's not obvious what's going on just from looking at that line. Let's give it a more descriptive name so we can figure out what's going on. Stick with that concrete language. So if we give it that name, we can go back up top, make that a one liner. Now we wanna consolidate the drop ships if the condition is true. Easy, much more understandable. This still sticks out like a sore thumb to me. It still looks terrible. So we'll take it down, move it down below, make it its own method, assign items to shipments, move the method name up above and suddenly build shipments by ship status as a two line method that consolidates to drop ship. If consolidates drop ships is true and assigns items to shipments. Our test still pass. So that's great. We still have a couple of methods here though that stick out to me. If you do the squint test, you can see a couple of methods up top that don't quite look like the rest. So we still have some work to do to express everything in coordinate form. Let's change them. We'll zoom in on them to start with. Let's look at build consolidated shipments. It's doing the same thing as build shipments by ship status, the exact same thing but it's doing it very differently. What's it doing? It's assigning every item to a shipment but it's hard coding the consolidated flag instead of iterating over the items, changing all the ship statuses and assigning to shipments. So let's make these do the same thing. Let's express them in coordinate ideas. Let's express the coordinate ideas in similar form. We're just gonna iterate over all of the line items, set the ship status of consolidated, call our assign items to shipments method. Then we'll take this, continuing to make the coordinate ideas expressed in similar form. Take that, move it down to a method and now these two are starting to look more and more similar. But you'll notice we've got a conditional down here. We only do this if consolidating to dropships is true. We don't have a conditional up here, why not? Well, it's up top, so we wanna make, we wanna make build consolidated shipment look the same as build shipment by dropship status. So we need to move that conditional down there and let's go ahead and change it to single shipment. We have to consolidate all over this class, right? It's a word that doesn't mean a lot to us anymore. So we wanna change it to something that means more to us and single shipment, it means a lot more to us. What we're doing when that flag is set is consolidating all our items into a single shipment. That lets us get rid of this conditional and now build shipments is a lot easier to understand except that we've broken a couple things. Now why is that? Well, as it turns out, we assume that both of these methods are now confident and we can call them without any side effects. The problem is that they both call assigned items to shipments unconditionally. So what we need to do is take that, move it up top to build shipments and then we're passing. Still not quite as clear as we want it to be though because as it turns out, these two methods are not building any shipments at all. They're updating ship statuses. So we need to make that more clear. We need to express that more clearly. So we'll move them down into their own method called optimized consolidation and then we call that up above. Suddenly build shipments is telling a much better story but those two methods are still named incorrectly. Like I said just a second ago, they're not building anything, they're just updating ship statuses. Well, as it turns out, those two lines are pretty expressive on their own. We don't really need a method name to tell us what they're doing. So let's just end the line. And our class is getting more and more compact and easier to read and our test still pass. So now let's hop over to the main class and if you take a look at everything that we've refactored here, it all lines up on the left. It passes the squint test. We've expressed all our ideas in very similar form. All these methods are structured the same. We have the same type of paragraph throughout our class except for this jump at the bottom. And I would love to show you how to refactor that but we're sort of running out of time here. So I'll just show you the end result. We took create group if necessary and made it its own inline class, a value object called shipment list that has all the tools that we need to interact with it and create a list of shipments by shipment time. So there's our final class. There's our refactor. And how do we do? Well, let's look at some scores. The play score for our class, which again measures duplication. Our original was 36 and it's down to zero. The original after I extracted it out of the rails out. Our class itself, the flog score, the original was 180. We've got it down to 77.2. So it's one third as complex as the original class. And the flog score for built shipments. If you remember, the flog score for that was 120.7. We've got it down to three. Well, as it turns out, the flog score is a pretty effective measurement of how readable code is because complexity and readability are one and the same. So there's where we started. There's where we finished. And that's sort of the crux of the talk. We've got a built shipments method up top that is an absolute rambling manifesto of a paragraph. There's no way you can make any sense out of what's going on up there. Built shipments down below actually follows a pretty clear narrative arc. We want to optimize consolidation, assign items to shipments and return shipments. That's it. Easy to understand. So when did it work? It was a pretty textbook refactoring, but I didn't reference any of the normal techniques you would reference when you refactor. Well, as it turns out, the rules in strung and white coordinate pretty well with the aphorisms of programming. Five, five, revise and rewrite. Red, green, refactor. Five, three, work from a suitable design. There are plenty of people that want to teach you good object-oriented design. Gang of four, pooter. Russ Olson, who's gonna be speaking to you in just a minute, gave us the Ruby version of the Gang of Four book. Five, 19, do not take shortcuts at the cost of clarity. This is the log-to-meter, and in case DHH ever sees this talk. 213, make the paragraph theme of the composition clearly the single responsibility principle. We want all of our methods to only do one thing. I could keep going through, I could keep giving you the relationships between strung and white and programming aphorisms, but we can actually ask the guy who wrote the book on refactoring the way this works. Martin Fowler's happy to tell us any fool can write code a computer can understand. Good programmers write code that humans can understand, and that's the thing. I mean, the code that we started with ran in production for many years and had a really low defect rate. It worked just fine, and then I tried to change it, and it was terrible. It fought me every step of the way. The easier your code is to read, the easier it is to change, and you really don't know how good your code is until you try to refactor it. You don't know how good it is until you try to change it. Good code helps you change it. Bad code fights you every step of the way. So that's the first part that I want you to take away. The second part, we need to reach into some famous literature. This is From On The Road by Jack Kerouac. We stopped along the road for a bite to eat. The cowboy went off to have a spare tire patch and Eddie and I sat down in a kind of homemade diner. I heard a great laugh, the greatest laugh in the world, and here came this raw hide old timer in Nebraska farmer with a bunch of other boys into the diner. This whole book carries on like this. It's this rambling monologue, and yet it evokes this wonder less than everybody who reads it. It's a fantastic book. Infinite Jest by David Foster Wallace. All of that text on the screen? One sentence. That's a single sentence. Good code by Cormac McCarthy. Wanna pull a surprise? Look at the dialogue here. There's no quotes, there's no attribution. You have to read it and try to figure out who's saying what, and it's not a strip back and forth. That's the interesting thing. All these rules that we follow are means to an end. They're not what make us good writers or good developers. Evie White in fact says, I felt uneasy posing as an expert on rhetoric, but the truth is, I write by ear, always with difficulty, and seldom with any exact notion of what is taking place under the bud. So it makes us good soccer developers. It's not how well we follow the rules. It's how well we learn to feel the rules, it's how well we learn to feel code, develop an eye for code, spend time reading code, spend time refactoring code, learn to spot smelly code, learn to respond to it and fix it. Thanks.