 not remember ever not knowing how to program. You ate binary for breakfast before you knew how to tie your shoelaces. You convert hex into decimal without even realizing that you're doing it. To you programming is like breathing. Code is not an incomprehensible jumble of symbols when you see code patterns emerge and your brain interprets them as ideas, poetry, beauty. I'm not like that. I remember being a beginner. Everything is hard. Syntax is hard. Logic is hard. Configuration is hard. Installing stuff is a nightmare. In programming, the path to mastery is not well known. It's not a tidy slog of simulations and drills and case studies. Swimmers and chess players and musicians, while they may not have it easy, they at least have a clear path to mastery. Tony Plogg, an internationally renowned trumpet virtuoso, runs a master class for professional soloists. I was told about a class that he gave a while back and went like this. The soloists played really, really hard pieces, breathtakingly complex pieces, and then Tony would provide suggestions for improvement. It's very typical stuff. Right up until the moment where he asked them to play a simple warm-up exercise, something that any beginner trumpet student might be given. And compared to the dramatic pieces that they just played, this sounded childish. And then Tony played the piece. And it didn't sound childish. When he played it, it sounded exquisite. Every note was deep and rich and beautiful. He had taken this handful of notes and turned it into something elegant and graceful. The contrast between his performance and that of the musicians in the class was astonishing. There's a profound difference between the true master and the skilled practitioner, and this difference occurs at a fundamental level that has nothing to do with intricate and sophisticated complexity. Tony suggested that the advanced student should spend far more time focusing on practicing simple pieces intensely. And so I'd like to bring Tony's lesson back into our industry using a completely spurious exercise that I made up. So meet Bob. He's 15 years old. He's not very bright, and he has an attitude problem. No matter what you say to him, he will respond in one of four ways, and this has been encoded into a simple test suite. Now, if you've been programming for, oh, I don't know, say 10 days or so, you will have no problem getting the test suite to pass. Now, because this is natural language, there are ambiguities. For example, just because something contains a question mark, it doesn't mean that it's a question. You can shout without an exclamation mark, and the presence of an exclamation mark doesn't make anything shouting. So questions can be shouted. Numbers are particularly interesting because they don't have uppercase and lowercase variants. And it goes on and on and on. And as the test suite, the complexity of the test suite increased, so did the complexity of the solutions. And I found myself adding more and more edge cases trying to force the solutions to become simpler, more generic and more correct. Now, in the past 18 months or so, over 2,000 people have made this test suite pass. They've submitted 6,500 iterations of this exercise. Some of these people are complete beginners. People who have been programming for a few days to a few months, they stumble on syntax. They're not really sure quite what a string is. Others are programmers, people who write code for a living, write books, they've been doing this for years. And so it begins. This is a fairly classic starting point for someone who has never programmed before. And let me just say this, it passes all the tests. Sandy Metz, the author of Practical Object Oriented Design in Ruby, talks about something that she calls the squint test. And it works like this. You lean back, and you squint your eyes, and you look at it. It changes in shape and changes in color. Here, just focus on shape. Look at the vertical white space. Look at the pattern created by the indentation. Look at the inline horizontal white space. Cleaning this up gives the code a clear rhythm. Beginners are often baffled at this insistence on uniformity. It's hard enough to get the thing to compile in the first place if we're not also going to get huffy about a little bit of white space. Humans are pattern seekers. White space is used to make like things alike and different things stand out. Recognizing patterns requires two things. First, consistency so that the patterns become apparent. And then second, enough experience to recognize the patterns. Beginners don't recognize patterns. A missing brace can throw them off for hours, leading them so deep into a rabbit hole that you have to pump in daylight. Consistent patterns help us understand code more quickly. Instead of having to read each piece of syntax separately, we recognize larger chunks, and these chunks become the building blocks of our thoughts. They become the ideas rather than mere punctuation. Consistency gives the code a cadence, a rhythm, squint at it. Notice the repeating patterns. The return keyword is repeated. The comparison to input happens over and over. Each of Bob's responses are duplicated. We can minimize the boilerplate by removing the explicit return and by using a case statement, especially since case statements can match multiple values. And all of this can be rearranged to fit on a single slide. So this code passes all the tests, it's consistent, and it has no duplication. The code is surprisingly easy to read and reason about, maybe even for beginners. Now, what could possibly go wrong? Well, for one, someone could say something that isn't exactly one of the things that are listed. You take one known input, you make an itty-bitty change to it, and Bob is at a loss for words. The current solution treats the test suite as though it's a complete definition of everything Bob will ever have to know. But the read me suggests that Bob can respond to any kind of natural language inputs. And instead of solving the problem described here, the code simply echoes the tests back at them. Now, unit tests can help you explore the boundaries of a problem, but they are not a logical proof of the correctness of your problem. They can't tell you when you're right. They can only tell you when you're wrong. Robert C. Martin tweeted a few years ago that as the tests get more specific, the code gets more generic. And in the case of Bob's admittedly riveting conversations, generic means detecting the patterns in each of the different groups of input. So for questions, it's all about punctuation. We should be able to split apart the string in order to verify the contents of the last character. And yelling is not about punctuation, it's all about all caps. And then there are all those pesky edge cases where strings that contain only numbers or only punctuation are not seen as yelling. And I hesitate to call this the obvious solution. But this sure is a problem that smells like regex. Now silence is defined as white space. We can easily remove all the leading and trailing white space characters and then compare what's left to an empty string. And then finally there's everything else, which we actually have syntax for in the language. So when we replace the concrete inputs with these generic rules, we get a syntax error, which is confusing, because the internet clearly states that this is what a case statement looks like in Ruby. And after a bit of flailing, you might accidentally add parentheses, which totally makes it work, despite the fact that the internet says that in Ruby you don't need parentheses, especially not here. Now it's really tempting to just accept that you need parentheses and move on. Instead, let's look at what's really going on. This is the offending line. And a lot of Rubyists use the keyword and because it feels like English. Like there's a strong culture of optimizing for happiness in Ruby and this keyword is just one of those little details that makes the language feel so natural and expressive. And this operator is a lot like the double ampersand, which is a short circuit boolean operator that exists in a lot of languages, including Ruby. And even though and and double ampersand behave similarly and in many cases can be used in place of each other, they are not the same thing. Double ampersand answers yes and no questions. And is for control flow, which is an imperative thing. Do this, do that. Pass the salt. Pseudo make me a sandwich. And if and only if the first operation succeeds, go ahead and do the next thing. A boolean expression asks a question and there are only two possible outcomes, true and false. Logical operators are used to combine very many little yes or no questions into one higher level big yes or no question. So if you're both hungry and thirsty, then go ahead and have a meal. And the meal is all about control flow. If you fail to eat, don't bother to drink. If you eat but don't drink, you can apparently forget about being merry. Now sometimes if an operation fails, you need to have a plan B. And these can be chained though eventually all the fall backs fail, we're all screwed. So in Bob's case, we're asking a yes or no question. We're not telling him what to do. So and is not the key word that we're looking for. We need a double ampersand, which then allows us to get rid of the parentheses. When code blows up, it's so easy to just poke a stick at it, throw on some extra parentheses, go on Stack Overflow, copy some code and hope that somehow the error will just go away. Slow down. Smell the error messages. Errors are windows into your unknown unknowns. Treating them like some annoying thing that you just want to get rid of and sweep under a rug is a lost opportunity. Because we learn best when something is relevant to us. Theory can feel so distant, so abstract, so relevant. Errors, an error that you are experiencing right now, right here, it bridges the gap, making theory real and immediate. So go ahead and squint at the code again and notice that it's kind of irregular. The three conditions are a little bit method calls, a little bit comparisons, a little bit of Boolean logic, some hard-coded data, a whole lot of regex. There's a faint smell here, reminiscent of primitive obsession. So this is a term that's used when you're passing numbers and strings and arrays and hashes around in your program and then operating on that data everywhere. This program is treating input like a dumb piece of data. The hay method is in charge of dissecting it and examining it, making decisions based on its findings. There's a cliche in Ruby that says that everything is an object. And it's kind of true. In Ruby, core types are objects. They know things about themselves. And this means that instead of operating on an object's data, you can let the object itself handle the fiddly details. And so if input is a string and strings are objects, what work could we foist onto the string? First, a string knows when it's empty. For the next one, we can use the uppercase and lowercase variants of the string without even knowing what's in the string. So if a string doesn't change when you uppercase it, it was already uppercase. And then lowercasing it weeds out all the edge cases because numbers and punctuation have no case. And so for a question, we don't even need to explicitly break the string apart. We can just ask the string to compare its own ending against something specific. And this gives us a much more regular program. Squint at it. The shapes and colors are all consistent. The pink stuff is pretty much all Bob's responses. The black stuff is all conditionals. And they're all at the same level of abstraction. This is actually kind of nice. Now imagine that you've never seen the readme. You haven't read the test suite. And you know nothing of Bob. And that you're kind of drunk. What does the upcase downstay case stuff mean again? Extracting a well-named method does a lot to clarify what's going on. And unfortunately this doesn't actually work yet. The all caps method doesn't know anything about input. But we can get the test passing by storing input as state. So that works. The conditions feel kind of lopsided. Extracting one condition into a method introduced in consistency. And in order to be consistent, let's extract the other conditions as well. So this adds a few more lines of code. But it's probably worth it if that makes it so that someone else can come and understand this code without having to go read a readme or test suite. Also you don't even really have to go read the implementation of the helper methods since the method names are fairly self-explanatory. Now unfortunately this code is now prone to one of the most infuriating kinds of bugs ever. Race conditions. And this is the thing where it sometimes happens. But of course never when you're trying to reproduce it all you know that sometimes in some cases Bob responds something when he should have been responding something completely different. And then you add a unit test for exactly that case that went went wrong in production and the test passes. It's maddening. And this is all about state. And so here is how it works. We have one instance of Bob. He's got his input variable where he's going to store what people say to him. Then we have two threads. And in the first Alice loudly questions Bob's sanity. Bob dutifully tucks away this string into his input. Meanwhile in thread two at practically the same instant Charlie complains about Bob's punctuality and Bob stores his complaint into input overriding Alice's remark. You can see where this is going. Back in thread one Bob is now ready to respond to Alice. Runs input through his algorithm sees Charlie's statement and responds and then he repeats the operation in thread two. So is this a problem? Well no. Nobody's going to die. Nobody's losing money. Most likely nobody is going to be the least bit inconvenienced by this. Not even Alice and Charlie. And you could even argue that Bob who is not very bright would legitimately be confused if two people are talking to him at the same time. So you could say that this is the expected behavior. On the other hand race conditions are just a really bad time all around so we should just fix it. So the problem is that we used an instance variable to store fleeting data. Instance variables are not to be confused with temporary variables. They stick around for the lifetime of your object. When people say did you try turning it off and turning it on again this is what that's about. There's some state somewhere that got out of whack and it's not entirely unavoidable but the problem can be minimized by using instance variables for data that's pertinent to the object throughout its lifetime. So we either need to inline the helper methods back to where we started or we could give the helper methods access to the input without saving it anywhere and it turns out case statements can switch on procs. You pass input to the case statement for each one clause it passes that to the proc. Now input is named s and then it evaluates the block of the proc and if that returns true it evaluates the block of the when or whatever. So we can still keep the helper methods to explain the code and when we have these helper methods they now all return procs. They also all end with a question mark and this in Ruby is not syntax. It's just a bargain that we made meaning that the method can be trusted to return true or false. We stretch the truth a little bit in Ruby. We have your usual garden variety true and false and cross my heart and hope to die and in addition to true and false we also have truthiness and falsiness. We can take any object and then negate it and you now have a Boolean false and then you negate it again and you basically invented casting for Ruby. As it turns out 42 is truthy as a 0 nil is falsy a string is truthy even if it's empty and Bob is truthy in fact the only objects that are false when cast to a Boolean are false and nil and that's it. All other objects are true including procs which are just another fancy kind of object. This is the case even if the block of the proc evaluates to false the proc itself is still truthy. In other words we have broken the bargain in our predicate methods because there's no way that they can ever return false. So it's not a bug at least not quite yet but we're going to we're going against a very strongly established idiom and inevitably someone someday is going to call the question method in a different context. Now we can fix it by explicitly passing the argument to each of the helper methods but this introduces a new smell. Notice how the body of the methods care only about the argument. The return value of the method depends only on the value of the string not on anything to do with Bob and this is called feature envy. So the methods are defined in Bob but they are all about some other object in this case string and according to the literature the solution is to move the methods onto the object that they're about. In other words the academics say that we need to reopen the string class and add more methods to it. Now it seems legit so this is the original code that suffers from feature envy and here's the version that fixes that smell. It's not a huge change. The amount of code is pretty much the same but the behavior is now spread across two classes. So this has a lot going for it. Bob is definitely a lot nicer. He no longer has to know anything about the dirty details about things like all caps and how it compares to upper and lower case variants. There's still something inconsistent about the three conditions in the case statement. This time it's not about failing the squint test in any way. It's more about literature. Blank and all caps are one kind of thing and question is a very different kind of thing. These are two different levels of abstraction. Now going back to the read me this problem isn't technically about all caps or blank. Bob is having a conversation. There's a narrative implicit in the code and that's about Bob interacting with the people around him and he's likely to have these conversations in person and out loud and in person you don't actually all caps at someone. You shout at them. Renaming the methods to be at a consistent level of abstraction adds some dramatic tension. This code now tells a story. The method names used to be about the underlying implementation. Now they name ideas. Kent Beck talks about in small talk best practice patterns that implementation details should be hidden behind a method with an intention revealing name even if that implementation is a single line long. Now all caps is a perfectly good intention revealing name provided that your domain is orthography or netiquette just like array and hash and list and dict are all great names if your domain is data structures. If your domain is circus or pastries or sound engineering you're going to want names that reflect the domain rather than the names that explain the flow of data through your program. Turns out implementation details make terrible narratives. So Bob is better. What about string? It looks very neat and tidy. It's very easy to forget that there are 109 other methods already defined on this object. Things like reverse and hex and scan. All of the core string methods are about stringish things and now we've added three methods that are all about conversation. This is a violation of the single responsibility principle. There are a lot of guidelines out there about how long certain units of code should be. Popular guidelines suggest that a class should be no more than 100 lines long. We split Bob up when he was barely 25. Should we have waited? According to certain automated code analysis tools a method is too long if it exceeds eight lines. Why eight? Why not seven or nine? Then there's another guideline that says a method should not be longer than five lines. And then I was reading, working my way through a refactoring workbook and they pointed to a three line method and claimed that it was too long. If three is too long then eight is much much much too long. And turned out that Wake and Rutherford in their refactoring workbook they were absolutely right. That particular three line method was too long. Not because three lines are are too long but because those particular three lines were doing too many disparate things. An object or a method should have a single purpose. A highly cohesive object will have very few reasons to change. It will be a whole lot easier to understand and therefore easier to use. The public API of an object should tell a consistent story. So ask yourself if this were two different things what would they be? And if you can come up with an answer that's not too far-fetched then maybe your object or your method has too many responsibilities. Okay so string, it's not that we tipped over some limit that 109 methods is okay and 112 is too many. It's too big since it now deals with both conversation type things and stringy type things. We need an object that is about the thing that Bob deals with, which is represented as a string, but which isn't really stringish. So going back to the Bob class we've been calling it input all this time. Perhaps we should turn that concept input into an object. But what is even input? That depends on context. If you're in a board meeting input means opinions and rhetoric and if you're eating dessert input means cupcakes that you put into your face and in a program it's input to a method. Any method. Wedding planning, input, zombie apocalypse, input, celebrity sightings, input. And clearly input doesn't name the concept. It's more like naming the role of the data in some bit of computation. That's not a very compelling narrative. We started out with string and that's more or less the lowest level of abstraction in Ruby defining the thing in terms of the underlying data structure and then we moved up a level of abstraction to input which is the role of this thing in the computation itself. And both of these things are about the solution. They're not about the problem. If you go up one more level of abstraction you end up talking about this thing in terms of its job. It analyzes or it parses or it classifies and none of these things tell you why we're analyzing or parsing or classifying. And if you go to an even higher level of abstraction you start talking about Bob's conversation itself. In Bob's interaction the object is a message or a comment or a statement or if you ask the thesaurus it's an utterance. And these have a fairly neutral feel to them. But you could really get into telling Bob's story and say that well to him those feel like a confrontation or drivel or blathering. Another take on this would to be that this say that this doesn't really represent the actual thing but Bob's interpretation of what is being said or what he thinks the tone is or his perception of the interaction. If you get into these point of view names then you might also rename the methods themselves to better reflect Bob's point of view. It's all about the story that you want to tell. I kind of like remark for this. It doesn't have all the drama that some of the other names have but it's conversational without trying too hard. So the code has become readable and more than that it's expressive. It tells a cohesive story. While some of this has to do with syntax and language most of it is about naming and sadly there aren't really any good rules that tell you how to name things. If you know the idea you're trying to express you can go to a thesaurus and try to find the right nuance or if you're working in an unfamiliar domain you can use Wikipedia to figure out what the terminology is that's relevant to you. If you step back a bit and you look at the narrative voice in your code you can try to tell a cohesive story but really there is no trick. It's all about practice and ultimately this is about finding those subtle gray areas where your understanding is fuzzy and you didn't even realize it. It's about asking questions to challenge yourself. Assume that you've got it wrong and then ask yourself what you got wrong or instead of trying to guess the future look at the code that you have in front of you and ask yourself what bothers you about it and then try to articulate why it bothers you and then try to fix it. The process of trying to articulate something that is vague and ease or just some gut feeling can be very enlightening or do guess the future in fact guess three possible conflicting futures and then writes one solution for each of them. I don't know any recipes for practicing to gain mastery in programming. These lessons are just the ones that I happen to learn from a warm-up that was intended to teach nothing at all. Thank you. Thank you Katrina. I think I may have rushed because there are like three minutes left over from when I practiced. Which means more time for questions. More time for questions. Who's up? Really wonderful. Thank you. What sources do you turn to to help you practice becoming a better namer? Do you stay in the technical realm or do you go to fiction or how do you become a better namer? I've I've I've done a number of things. I've read books about that's been more about storytelling so I've read books about screenwriting and I'm never ever gonna write screen stuff or novel stuff or anything but but I did try to figure out what makes a good narrative. For naming I just try it a lot like I'll chew on a name. I've got this one toy problem that I've been working on for probably over a year now and like ten months into it I was like it took me ten months. So just keep keep trying and keep thinking is sort of my has been my strategy it's not always very successful. I was just wondering if this presentation would be available in any other form. I have lots of other team members I would love to see this as well. I see a camera so it will be eventually so while we're here I also teach a practical object oriented design course with Sandy Metz that's three days long that comes out of a lot of the work that was done that we did when we started playing with toy problems. It's a lot of fun we use the 99 bottles of beer song to teach everything you need to know an object oriented programming. It's it's really a great time you should definitely get your boss to send you. I have totally prepared the slides. We're doing a public course in Durham in October and the sign up is on Sandy Metz's website. Hi thank you for your presentation it was really interesting can you comment on really long names with lots of underscores because that's that's what I like. I'm going to disappoint you. I have very strong opinions about this. Here's the thing whenever I see a really long name with lots of pieces I ask myself for every single piece of that name does it make a meaningful distinction and if it doesn't I try to drop it. Often what I find when I have really really long names is that I've named it at the wrong level abstraction. It does the right thing but I'm thinking of it as like digging a ditch and planting a seed and blah blah blah and it's actually called gardening and if you take take a step back you can often find sort of a higher level abstraction that describes everything that's in your method or or split it apart. If you don't have more I've got I'm going to have one more slide that's going. I made a website that's all about practicing toy problems and I started out a year and a half ago by accident just to fix some workflow issues that I was having at work and then someone tweeted about it and people started using it now like there are thousands of people who are doing toy problems on the internet and it's great fun. So I've discovered that there's the one side of actually doing the practice can be really fun but even more interesting is looking at other people's code and then doing the thing where you're like well there's got to be something wrong with this. What what is that? And then trying to figure out how to articulate why you don't like something. It's made me a much better program or much better a factor. Okay. That's all I got. Thank you very much.