WEBVTT 00:00:17.300 --> 00:00:19.410 MARK MENARD: So, many thanks to the organizers 00:00:19.410 --> 00:00:21.349 here at RailsConf. This is my first time 00:00:21.349 --> 00:00:23.890 talking at RailsConf. It's, frankly, 00:00:23.890 --> 00:00:25.250 kind of intimidating to be up here 00:00:25.250 --> 00:00:27.750 and see so many people out there. 00:00:27.750 --> 00:00:29.550 My name is Mark Menard. I'm gonna be talking 00:00:29.550 --> 00:00:32.619 about small code today. And I've got a lot 00:00:32.619 --> 00:00:35.159 of code. About seventy-nine slides. A hundred and thirty-seven 00:00:35.159 --> 00:00:37.940 transitions. Not quite as much as Sandi had, but 00:00:37.940 --> 00:00:40.480 it's a lot to get through. So let's get 00:00:40.480 --> 00:00:41.680 going. 00:00:41.680 --> 00:00:44.250 So, I'm just gonna let this quote up there 00:00:44.250 --> 00:00:44.809 sink in. 00:00:44.809 --> 00:00:51.809 So. All of us have that file filled with 00:00:53.250 --> 00:00:56.609 code that you just don't want to open. As 00:00:56.609 --> 00:01:00.559 you heard earlier, maybe it's your User class. That 00:01:00.559 --> 00:01:02.539 class that has comments like, woe to ye who 00:01:02.539 --> 00:01:05.880 edit here. The problem with this code is that 00:01:05.880 --> 00:01:10.860 it does live forever. It encapsulates business logic that 00:01:10.860 --> 00:01:13.039 ends up getting duplicated elsewhere, cause no one wants 00:01:13.039 --> 00:01:15.450 to go in there and look at that code. 00:01:15.450 --> 00:01:17.520 It's also very hard to understand. 00:01:17.520 --> 00:01:19.970 I'm gonna be talking about ways to avoid this 00:01:19.970 --> 00:01:24.580 situation. I'm gonna be talking about code. Code at 00:01:24.580 --> 00:01:28.000 the class level and the method level. Having small 00:01:28.000 --> 00:01:31.020 code at the class and method level is fundamental 00:01:31.020 --> 00:01:34.069 to being able to create systems that are composed, 00:01:34.069 --> 00:01:37.599 composed of small, understandable parts. 00:01:37.599 --> 00:01:39.110 I'm gonna lay out a few base concepts so 00:01:39.110 --> 00:01:41.739 that we can start with a clean sheet and 00:01:41.739 --> 00:01:44.340 on the same page. I think there's a lot 00:01:44.340 --> 00:01:46.420 of problems with what people conceive of as small 00:01:46.420 --> 00:01:49.179 or well-designed code. 00:01:49.179 --> 00:01:50.929 It's not about the actual amount of code you 00:01:50.929 --> 00:01:54.640 write, but how the code is organized and the 00:01:54.640 --> 00:01:59.220 size of the units of code. Fundamentally, writing small 00:01:59.220 --> 00:02:01.890 code is really design discipline, because the only way 00:02:01.890 --> 00:02:04.729 you can write small code is use good design 00:02:04.729 --> 00:02:07.569 and refactoring. 00:02:07.569 --> 00:02:09.840 Design and refactoring the way we write small code. 00:02:09.840 --> 00:02:12.250 You can't just sit down and write small code, 00:02:12.250 --> 00:02:14.750 perfectly well-designed code on the first draft. It doesn't 00:02:14.750 --> 00:02:18.450 work that way. It's iterative process. 00:02:18.450 --> 00:02:23.260 So what do I mean by small? It's not 00:02:23.260 --> 00:02:27.610 about total line count. Well-designed code will typically have 00:02:27.610 --> 00:02:30.750 more lines of code than bad code. Just the 00:02:30.750 --> 00:02:33.700 overhead of declaring methods and classes is gonna increase 00:02:33.700 --> 00:02:36.230 your line count. 00:02:36.230 --> 00:02:39.480 It's not about method count. Well-factored code's gonna have 00:02:39.480 --> 00:02:46.480 more, smaller methods. It's not about class count. Well-designed 00:02:47.500 --> 00:02:51.240 code is almost definitely going to have more classes 00:02:51.240 --> 00:02:53.380 than what I call undesigned code. 00:02:53.380 --> 00:02:57.530 Although I've seen some cases of over-abstraction, I find 00:02:57.530 --> 00:03:01.390 that's pretty rare unless someone goes pattern crazy. So 00:03:01.390 --> 00:03:03.360 small code is definitely not about decreasing the number 00:03:03.360 --> 00:03:08.460 of classes in your system. It's about well-designed, it's 00:03:08.460 --> 00:03:12.340 about well-designed classes that aren't poorly designed. 00:03:12.340 --> 00:03:14.460 So what do I mean by small? Small methods, 00:03:14.460 --> 00:03:17.290 small classes. Small methods are the foundation of writing 00:03:17.290 --> 00:03:21.450 small code. Without the ability to decompose large methods 00:03:21.450 --> 00:03:25.180 into small methods, we cannot write small code. And 00:03:25.180 --> 00:03:28.180 without small methods, we can't raise the level of 00:03:28.180 --> 00:03:30.740 abstraction. 00:03:30.740 --> 00:03:32.709 To write small code, we have to be able 00:03:32.709 --> 00:03:36.350 to decompose large classes into smaller classes, and abstract 00:03:36.350 --> 00:03:40.400 responsibilities out of them and separate them on higher-level, 00:03:40.400 --> 00:03:44.180 and base them on higher-level abstractions. 00:03:44.180 --> 00:03:46.459 It's important that our classes are small, because small 00:03:46.459 --> 00:03:52.530 classes are what lead to reusability and composability. 00:03:52.530 --> 00:03:59.530 So, why should we strive for small code? Why 00:04:00.640 --> 00:04:05.020 is it important? 00:04:05.020 --> 00:04:08.880 We don't know what the future is going to 00:04:08.880 --> 00:04:15.680 bring. Your software requirements are going to change. Software 00:04:15.680 --> 00:04:19.339 must be amenable to change. Any system of software 00:04:19.339 --> 00:04:23.370 that's going to have a long, successful life, is 00:04:23.370 --> 00:04:28.250 going to change significantly. Small code is simply easier 00:04:28.250 --> 00:04:32.160 to work with than large, complex code. If the 00:04:32.160 --> 00:04:34.990 requirements of your software are never gonna change, you 00:04:34.990 --> 00:04:38.090 can ignore everything that I have to say here. 00:04:38.090 --> 00:04:45.090 But I doubt that that's the case. 00:04:45.409 --> 00:04:47.729 We should write small code because it helps us 00:04:47.729 --> 00:04:52.199 raise the level of abstraction in our code. It's 00:04:52.199 --> 00:04:54.029 one of the most important things we do to 00:04:54.029 --> 00:05:00.289 create readable, understandable code. All good design is really 00:05:00.289 --> 00:05:05.340 driving toward expressing ubiquitous language of our problem domain 00:05:05.340 --> 00:05:07.960 in our code. 00:05:07.960 --> 00:05:10.879 The combination of small methods and small classes is 00:05:10.879 --> 00:05:13.870 going to help us raise that level of abstraction 00:05:13.870 --> 00:05:20.870 and express those higher-level domain concepts. 00:05:21.229 --> 00:05:22.870 We should also write small code so we can 00:05:22.870 --> 00:05:27.960 effectively use composition. Small classes and small methods compose 00:05:27.960 --> 00:05:31.819 together well. As we compose instances of small objects 00:05:31.819 --> 00:05:36.930 together, our systems will become message-based. In order to 00:05:36.930 --> 00:05:39.909 build systems that are message-based, we have to use 00:05:39.909 --> 00:05:44.569 delegation. And small, composable parts. Small code makes small 00:05:44.569 --> 00:05:47.779 composable parts. It's gonna help our software have flexibility 00:05:47.779 --> 00:05:50.110 and lead to a suppleness over time, and allow 00:05:50.110 --> 00:05:52.900 us to follow those messages. And eventually we're gonna 00:05:52.900 --> 00:05:57.699 see, find our duck types. 00:05:57.699 --> 00:06:01.430 And all this is about enabling future change. And 00:06:01.430 --> 00:06:06.509 accommodate the future requirements without a forklift replacement. 00:06:06.509 --> 00:06:12.490 So the goal: small units of understandable code that 00:06:12.490 --> 00:06:18.099 are amenable to change. 00:06:18.099 --> 00:06:22.300 Our primary tools are extract method and extract class. 00:06:22.300 --> 00:06:25.349 Longer methods are harder to understand than short methods. 00:06:25.349 --> 00:06:26.719 And most of the time, we can shorten a 00:06:26.719 --> 00:06:30.869 method simply by using the abstract method refactoring. I 00:06:30.869 --> 00:06:35.069 use this thing all the time when I'm coding. 00:06:35.069 --> 00:06:36.240 And once we have a set of methods that 00:06:36.240 --> 00:06:39.680 are coherent around a concept, then we can look 00:06:39.680 --> 00:06:43.059 to abstract those into a separate class and move 00:06:43.059 --> 00:06:47.289 the methods to that new class. 00:06:47.289 --> 00:06:50.129 So, I'm gonna be using the example of a 00:06:50.129 --> 00:06:53.680 command line option parser that handles booleans to start 00:06:53.680 --> 00:06:55.650 with, and then we're gonna see where the future 00:06:55.650 --> 00:06:57.020 takes us. 00:06:57.020 --> 00:07:01.770 So, with the command line, I want to be 00:07:01.770 --> 00:07:05.749 able to run some Ruby program dash v. And 00:07:05.749 --> 00:07:11.580 handle boolean options. That's where we're gonna start. 00:07:11.580 --> 00:07:13.550 In my Ruby program, I want to define what 00:07:13.550 --> 00:07:20.550 options I'm looking for, using this simple DSL. And 00:07:21.330 --> 00:07:22.669 then I want to be able to consume it 00:07:22.669 --> 00:07:26.839 like this. If options.has and then a particular option, 00:07:26.839 --> 00:07:28.180 I do something. 00:07:28.180 --> 00:07:34.499 Putting it all together. The DSL, the program at 00:07:34.499 --> 00:07:36.389 the top, the DSL, and then how we actually 00:07:36.389 --> 00:07:42.749 consume that options object. Pretty simple. 00:07:42.749 --> 00:07:47.939 Here's my spec. It's pretty simple. It's true if 00:07:47.939 --> 00:07:49.899 the option is defined and it's present on the 00:07:49.899 --> 00:07:54.300 command line. And it's false if it's not. 00:07:54.300 --> 00:07:56.469 So I run my specs and I get two 00:07:56.469 --> 00:08:03.469 failures. Yes, I used TDD. So, here's my implementation 00:08:04.719 --> 00:08:09.439 that fits on one slide. Pretty simply, I store 00:08:09.439 --> 00:08:12.550 the defined options in an array, and I store 00:08:12.550 --> 00:08:17.339 the arguments, the argv for later reference. Then I 00:08:17.339 --> 00:08:18.800 have a has method that checks to see if 00:08:18.800 --> 00:08:20.860 the option is defined. If it's present in the 00:08:20.860 --> 00:08:21.629 argv. 00:08:21.629 --> 00:08:25.270 And then I've got my option method, which implements 00:08:25.270 --> 00:08:30.770 my simple DSL. Nice and readable. Fits on one 00:08:30.770 --> 00:08:35.440 slide. Probably very comprehendable. 00:08:35.440 --> 00:08:40.360 So I run my tests. Zero failures. They pass. 00:08:40.360 --> 00:08:45.649 I'm done. I get to go home until the 00:08:45.649 --> 00:08:48.660 future comes along. And my workmate comes along and 00:08:48.660 --> 00:08:51.500 says, hey, I really like that library. But, could 00:08:51.500 --> 00:08:54.500 we handle string options? 00:08:54.500 --> 00:08:59.610 Sounds pretty simple. Pretty straightforward. So I think about 00:08:59.610 --> 00:09:01.110 that, and I come up with a small extension 00:09:01.110 --> 00:09:04.110 to the DSL, to just pass a second argument 00:09:04.110 --> 00:09:06.300 as an option with a symbol representation of the 00:09:06.300 --> 00:09:08.019 option type. String, in this case. 00:09:08.019 --> 00:09:10.190 I also default to being a boolean so I 00:09:10.190 --> 00:09:12.110 don't have to change the code that other people 00:09:12.110 --> 00:09:15.149 have done. 00:09:15.149 --> 00:09:18.639 So, a string option. It's a little different than 00:09:18.639 --> 00:09:23.310 a boolean. It actually requires content. So now I 00:09:23.310 --> 00:09:26.519 need the concept of validation. If the string option 00:09:26.519 --> 00:09:28.430 is missing the content, it's not valid. There's no 00:09:28.430 --> 00:09:30.589 string there. 00:09:30.589 --> 00:09:33.430 So, then I'm gonna normalize how I get the 00:09:33.430 --> 00:09:35.519 values out of both those string options and those 00:09:35.519 --> 00:09:38.300 boolean options. You know, that value. This is gonna 00:09:38.300 --> 00:09:40.880 change the API, but sometimes you actually need to 00:09:40.880 --> 00:09:43.750 break the API to enable the future. 00:09:43.750 --> 00:09:46.690 And I'm doing it pretty early. I've only got 00:09:46.690 --> 00:09:50.970 one guy in my office using the library at 00:09:50.970 --> 00:09:53.209 the moment. So, again, putting it all together. I 00:09:53.209 --> 00:09:55.329 can pass the options on the command line. I 00:09:55.329 --> 00:09:58.050 define the options with the DSL, and here's how 00:09:58.050 --> 00:10:00.360 I use my valid? and my value methods to 00:10:00.360 --> 00:10:02.680 find out, get, find out if it's valued and 00:10:02.680 --> 00:10:05.639 get my values out. 00:10:05.639 --> 00:10:09.959 So, now here's the class that implements it. Again, 00:10:09.959 --> 00:10:12.949 on one slide. Probably not as readable. Probably not 00:10:12.949 --> 00:10:16.399 as comprehensible. We're going down what I call kind 00:10:16.399 --> 00:10:20.240 of the undesigned path. It's not too big. Thirty-one 00:10:20.240 --> 00:10:22.089 lines. But it's got issues. 00:10:22.089 --> 00:10:24.389 It's got a method that's definitely large. One that's 00:10:24.389 --> 00:10:27.800 looking on the verge of being large. It's got, 00:10:27.800 --> 00:10:29.980 for only handling booleans and strings, it has quite 00:10:29.980 --> 00:10:34.300 a bit of, of conditional complexity in it already. 00:10:34.300 --> 00:10:36.110 And as we're soon gonna see, it's not very 00:10:36.110 --> 00:10:37.690 amenable to change. 00:10:37.690 --> 00:10:39.639 So we'll look at the pieces and how they 00:10:39.639 --> 00:10:40.850 work, just so yo understand it. 00:10:40.850 --> 00:10:42.839 That's my initialize method. It creates a hash to 00:10:42.839 --> 00:10:44.819 store the options. Because we have to store the 00:10:44.819 --> 00:10:47.430 type now, not just that we have an option. 00:10:47.430 --> 00:10:50.500 It's either boolean or string. And the rest of 00:10:50.500 --> 00:10:54.560 the initialization is the same as it was before. 00:10:54.560 --> 00:10:57.259 And the valid method, we gotta iterate over the 00:10:57.259 --> 00:10:59.949 options, looking to see which ones are strings. So 00:10:59.949 --> 00:11:03.420 we're doing checking on type here. And, and trying 00:11:03.420 --> 00:11:06.190 to see whether they're present and they actually have 00:11:06.190 --> 00:11:06.589 content. 00:11:06.589 --> 00:11:08.720 Currently, string options are the only ones that need 00:11:08.720 --> 00:11:12.610 to validate. Boolean options, there's nothing really to validate. 00:11:12.610 --> 00:11:14.680 Either it's there or it's not. No validation. But 00:11:14.680 --> 00:11:15.920 strings, we have to. 00:11:15.920 --> 00:11:18.209 And the value method, it does a lot of 00:11:18.209 --> 00:11:21.410 stuff. Let's just pretend for a moment this method 00:11:21.410 --> 00:11:23.100 is a black box. We're gonna come back to 00:11:23.100 --> 00:11:26.089 it later. Cause this is, by far, the worst 00:11:26.089 --> 00:11:29.279 code in this current example. 00:11:29.279 --> 00:11:34.300 But, everything is spec'd. And all my specs are 00:11:34.300 --> 00:11:36.399 green. 00:11:36.399 --> 00:11:40.120 So let's talk about methods. Cause we've got some 00:11:40.120 --> 00:11:42.420 big ones and we need to clean them up. 00:11:42.420 --> 00:11:47.629 I call it the first rule of method, of 00:11:47.629 --> 00:11:51.589 methods. Do one thing. Do it well. Do only 00:11:51.589 --> 00:11:52.480 one thing. 00:11:52.480 --> 00:11:55.440 Harkens back to that Unix philosophy of tools that 00:11:55.440 --> 00:11:59.290 you string together with standard in, standard out. But 00:11:59.290 --> 00:12:01.149 how do we determine if a method is actually 00:12:01.149 --> 00:12:04.610 only doing one thing? This is where your level 00:12:04.610 --> 00:12:08.000 of abstraction and the abstract, abstractions in your code 00:12:08.000 --> 00:12:10.579 come into play. And you need to develop a 00:12:10.579 --> 00:12:13.399 feel for this over time. That you want one 00:12:13.399 --> 00:12:16.129 level of abstraction per method. 00:12:16.129 --> 00:12:18.629 If all of our statements are the same level 00:12:18.629 --> 00:12:22.529 of abstraction, and they're coherent around a purpose, then 00:12:22.529 --> 00:12:25.810 I consider that to be doing one thing. Doesn't 00:12:25.810 --> 00:12:27.620 mean it has to be one line in a 00:12:27.620 --> 00:12:30.220 method. 00:12:30.220 --> 00:12:32.399 I can't tell you how many times I've looked 00:12:32.399 --> 00:12:36.009 at code and seen a comment on a method 00:12:36.009 --> 00:12:37.730 that was, like, an excellent description of what the 00:12:37.730 --> 00:12:39.839 method did, and if you just took those words, 00:12:39.839 --> 00:12:42.160 bound together, they'd make a fantastic method name. But 00:12:42.160 --> 00:12:45.310 yet the method is named something else that isn't 00:12:45.310 --> 00:12:50.980 that descriptive. So use descriptive names. It's really critical. 00:12:50.980 --> 00:12:53.149 And the fewer arguments, the better. My personal goal 00:12:53.149 --> 00:12:57.670 is zero arguments on methods. One is OK. Two 00:12:57.670 --> 00:12:59.790 or three. That's when I start to think I've 00:12:59.790 --> 00:13:02.589 probably missed an abstraction in my code and I 00:13:02.589 --> 00:13:06.699 should go back and look at it. 00:13:06.699 --> 00:13:09.819 Separate queries from commands. If you query something and 00:13:09.819 --> 00:13:11.519 it looks like a query method and it changes 00:13:11.519 --> 00:13:14.170 the state of your object, it's hard to reason 00:13:14.170 --> 00:13:16.600 about, and people who consume your library will be 00:13:16.600 --> 00:13:20.569 confused by that. So separate those. 00:13:20.569 --> 00:13:24.589 And, don't repeat yourself. I know Sandi talked about 00:13:24.589 --> 00:13:27.810 this earlier, and it does take some judgment to 00:13:27.810 --> 00:13:31.089 know when it is time to remove the repetition. 00:13:31.089 --> 00:13:32.949 But you don't want to leave repetition over the 00:13:32.949 --> 00:13:35.750 long term, because it will come back to bite 00:13:35.750 --> 00:13:37.379 you. 00:13:37.379 --> 00:13:42.009 So, let's look at our methods. We've got repetition 00:13:42.009 --> 00:13:44.069 here. Both valid? and value are digging through the 00:13:44.069 --> 00:13:46.660 argv array to find the options from the command 00:13:46.660 --> 00:13:49.639 line. This is the perfect candidate for an extract 00:13:49.639 --> 00:13:53.779 method, abstraction. Refactoring. 00:13:53.779 --> 00:13:56.910 We have magic constants scattered around, and those are 00:13:56.910 --> 00:14:02.120 a strong indication that we've missed something. An abstraction. 00:14:02.120 --> 00:14:06.139 We're violating some other rules. It's hard to say 00:14:06.139 --> 00:14:11.149 either of these methods is really doing one thing. 00:14:11.149 --> 00:14:13.829 The code is definitely not at the same level 00:14:13.829 --> 00:14:17.930 of abstraction. Values digging, valid? is digging into the 00:14:17.930 --> 00:14:20.759 argv array and value is figuring out different divergent 00:14:20.759 --> 00:14:23.670 types and how to return their values. So we're 00:14:23.670 --> 00:14:25.800 gonna eliminate some of the repetition with the extract 00:14:25.800 --> 00:14:28.629 method refactoring. 00:14:28.629 --> 00:14:33.160 The extract method refactoring entails moving a part of 00:14:33.160 --> 00:14:35.420 a method into a new method, with a descriptive 00:14:35.420 --> 00:14:38.449 name, that's the naming part. And then calling the 00:14:38.449 --> 00:14:39.529 new method. 00:14:39.529 --> 00:14:42.310 This, this refactoring helps us keep the level of 00:14:42.310 --> 00:14:47.819 abstraction consistent in the method we're abstracting from. Here 00:14:47.819 --> 00:14:49.459 we have one expression on a method that's a 00:14:49.459 --> 00:14:52.839 high level of abstraction, and two statements that are 00:14:52.839 --> 00:14:56.529 a low level of abstraction. So we move the 00:14:56.529 --> 00:14:58.509 less abstract code to a new method with a 00:14:58.509 --> 00:15:01.930 descriptive name, and then we call the new method. 00:15:01.930 --> 00:15:04.920 And this results in the old method, method having 00:15:04.920 --> 00:15:09.990 a consistent level of abstraction. So back to our 00:15:09.990 --> 00:15:14.160 CommandLineOptions class, both valid? and value are digging through 00:15:14.160 --> 00:15:16.689 the argv collection to find the option value. So 00:15:16.689 --> 00:15:18.740 we're gonna abstract that code and get the raw 00:15:18.740 --> 00:15:22.110 value out of argv. 00:15:22.110 --> 00:15:23.749 Then we call the method from where the original 00:15:23.749 --> 00:15:27.519 logic was abstracted. Pretty simple. But now the code 00:15:27.519 --> 00:15:30.089 left behind in valid? and value says what I 00:15:30.089 --> 00:15:34.439 want. Not how to do it. 00:15:34.439 --> 00:15:37.759 The how has been moved to the abstracted method, 00:15:37.759 --> 00:15:40.470 raising the level of abstraction just a little bit 00:15:40.470 --> 00:15:44.240 in valid? and value. 00:15:44.240 --> 00:15:47.279 I'm going to do two more abstractions. I've abstracted 00:15:47.279 --> 00:15:51.540 the string option value method and the abstract content 00:15:51.540 --> 00:15:55.389 method. The naming of the abstracted methods is very 00:15:55.389 --> 00:16:00.540 important. They say what they do. 00:16:00.540 --> 00:16:03.339 But overall, I'm not happy with this code. It 00:16:03.339 --> 00:16:06.329 is more explanatory, but it's fairly complex and hard 00:16:06.329 --> 00:16:09.809 to understand. It's also not as small as it 00:16:09.809 --> 00:16:12.629 could be. The methods are large because I missed 00:16:12.629 --> 00:16:17.499 an abstraction. And we're gonna go find that now. 00:16:17.499 --> 00:16:19.730 I'm referencing the option type symbol to see if 00:16:19.730 --> 00:16:24.519 it's a string, which, that's a big smell. Then 00:16:24.519 --> 00:16:26.290 there are the magic constants used to dig into 00:16:26.290 --> 00:16:28.959 the argv element to find the constant within that 00:16:28.959 --> 00:16:34.240 particular string, the substring. If I was confident that 00:16:34.240 --> 00:16:36.709 I'd have no future added requirements for this class, 00:16:36.709 --> 00:16:40.639 I might leave this alone. It works. It's tested. 00:16:40.639 --> 00:16:43.220 Until my buddy comes to me and says, hey, 00:16:43.220 --> 00:16:45.300 I really like that library, but could we handle 00:16:45.300 --> 00:16:48.149 integers now? 00:16:48.149 --> 00:16:50.959 I could keep driving down this undesigned path I've 00:16:50.959 --> 00:16:53.910 been following, and complicate the valid? and value methods 00:16:53.910 --> 00:16:56.420 by switching on the type of the option and 00:16:56.420 --> 00:16:59.790 digging into those argv elements to find the value. 00:16:59.790 --> 00:17:03.689 But, this is our chance to make a break. 00:17:03.689 --> 00:17:07.380 And make our code more amenable to change. 00:17:07.380 --> 00:17:11.329 But, to illustrate the point, I'm gonna show you 00:17:11.329 --> 00:17:14.939 that undesign method, to show you the OO design 00:17:14.939 --> 00:17:18.199 actually matters. So we're gonna look at this. 00:17:18.199 --> 00:17:20.400 This is the undesigned, non OO version of this 00:17:20.400 --> 00:17:23.869 code. Is it horrible? I'll leave that to you 00:17:23.869 --> 00:17:27.119 to decide. Is it small? In my opinion, definitely 00:17:27.119 --> 00:17:30.120 not. It is not small, by any measure. The 00:17:30.120 --> 00:17:32.090 class is growing due to changes in specification. The 00:17:32.090 --> 00:17:36.490 valid? and value methods are being changed in lock 00:17:36.490 --> 00:17:39.170 step. That's a sure sign we've missed an abstraction 00:17:39.170 --> 00:17:41.850 or a duck type. And those methods are getting 00:17:41.850 --> 00:17:45.900 big and complicated. And now they're doing even more 00:17:45.900 --> 00:17:47.340 things. 00:17:47.340 --> 00:17:52.380 And we're just doing booleans, strings, and integers. Not 00:17:52.380 --> 00:17:53.340 that much. 00:17:53.340 --> 00:18:00.270 The code has tests. They all pass. That's good. 00:18:00.270 --> 00:18:03.670 But it's not satisfying. We've got those large methods 00:18:03.670 --> 00:18:07.880 and complex conditional logic. It's time to refactor now. 00:18:07.880 --> 00:18:10.570 To make the change easy. 00:18:10.570 --> 00:18:12.340 And now we've got the tests that are back, 00:18:12.340 --> 00:18:14.680 so we can do it without fear. 00:18:14.680 --> 00:18:18.420 And, I want to call your attention to a 00:18:18.420 --> 00:18:21.740 pattern that clearly emerges when we go down the 00:18:21.740 --> 00:18:25.720 non OO path here. We see checking the option 00:18:25.720 --> 00:18:30.940 type and divergent behavior based on the type. Don't 00:18:30.940 --> 00:18:36.270 reinvent the type system. If you have ducks, let 00:18:36.270 --> 00:18:37.930 them quack. 00:18:37.930 --> 00:18:40.710 In this example, the option types of boolean, string, 00:18:40.710 --> 00:18:44.080 and integer, those are our ducks. And I'll bet 00:18:44.080 --> 00:18:47.830 there's ducks in your code yearning to be free. 00:18:47.830 --> 00:18:51.410 And just a further confirmation that we're dealing with 00:18:51.410 --> 00:18:55.300 an abstraction or a duck, we see the testing 00:18:55.300 --> 00:18:59.190 option type again in the value method. Hidden inside 00:18:59.190 --> 00:19:02.130 the valid? and value method, there's a case statement 00:19:02.130 --> 00:19:04.700 here. It just didn't evolve that way as I 00:19:04.700 --> 00:19:05.560 was writing the code. 00:19:05.560 --> 00:19:08.720 I'm gonna show you that. You're gonna see that 00:19:08.720 --> 00:19:12.840 it's really clear now. 00:19:12.840 --> 00:19:14.650 Now it should be really obvious what the duck 00:19:14.650 --> 00:19:17.280 type is. If you have case statements like this 00:19:17.280 --> 00:19:24.280 in your code, you've missed an abstraction. Here, again, 00:19:24.540 --> 00:19:26.830 we clearly see the duck type. 00:19:26.830 --> 00:19:30.070 Now, I would guess, if I was writing this, 00:19:30.070 --> 00:19:32.420 as soon as I had the string type, I 00:19:32.420 --> 00:19:35.360 would have gone down the OO path. I just 00:19:35.360 --> 00:19:38.690 wanted to illustrate to you what an undesigned, non 00:19:38.690 --> 00:19:41.700 OO mess you can get yourself into if you 00:19:41.700 --> 00:19:45.570 keep riding the horse until it's dead. 00:19:45.570 --> 00:19:47.290 My dad had a saying hanging on his wall 00:19:47.290 --> 00:19:49.930 in his office. When the horse is dead, get 00:19:49.930 --> 00:19:52.060 off. 00:19:52.060 --> 00:19:54.810 But sometimes we don't realize the horse is dead 00:19:54.810 --> 00:19:58.160 and we just keep trying to go. Now it's 00:19:58.160 --> 00:20:01.440 time to take a fresh look at this. So, 00:20:01.440 --> 00:20:04.280 since class is the fundamental organizational unit we have 00:20:04.280 --> 00:20:07.120 to work with, it's time to work at what 00:20:07.120 --> 00:20:10.820 constitutes a good class. Which principles are gonna lead 00:20:10.820 --> 00:20:14.290 us to be able to write small classes. 00:20:14.290 --> 00:20:19.930 So, how do we write small classes? To make 00:20:19.930 --> 00:20:23.540 small classes, I think, and this is not just 00:20:23.540 --> 00:20:25.060 my opinion. It's a lot of peoples' opinion. The 00:20:25.060 --> 00:20:28.710 most important thing we should assure is that our 00:20:28.710 --> 00:20:35.080 class has one responsibility. And that it has small 00:20:35.080 --> 00:20:38.130 methods. 00:20:38.130 --> 00:20:41.180 All the properties of a class should be cohesive 00:20:41.180 --> 00:20:44.740 to the abstraction that the class is modeling. If 00:20:44.740 --> 00:20:46.500 you have properties that you only use in one 00:20:46.500 --> 00:20:50.370 or two methods, that's probably something else that shouldn't 00:20:50.370 --> 00:20:53.430 be in there. 00:20:53.430 --> 00:20:55.170 Finding a good name for a class will also 00:20:55.170 --> 00:20:59.030 help us keep it focused on a single responsibility. 00:20:59.030 --> 00:21:01.480 I sometimes talk to the class. Have you ever 00:21:01.480 --> 00:21:03.870 heard the concept of talking to the rubber duck? 00:21:03.870 --> 00:21:05.770 Or just explaining your problem to someone? They don't 00:21:05.770 --> 00:21:08.220 even have to respond, and it helps you figure 00:21:08.220 --> 00:21:09.180 it out. 00:21:09.180 --> 00:21:11.060 Sometimes I just ask my class, hey class, what 00:21:11.060 --> 00:21:13.610 do you do? And if it comes out with 00:21:13.610 --> 00:21:16.300 a long list, you've got a problem. 00:21:16.300 --> 00:21:18.540 So, the main tools we're gonna use to create 00:21:18.540 --> 00:21:23.520 new classes from existing code, not from scratch, but 00:21:23.520 --> 00:21:26.720 from existing code, is the extract class and move 00:21:26.720 --> 00:21:29.950 method refactorings, which we're gonna go through here. 00:21:29.950 --> 00:21:35.810 So, those characteristics of well-designed class. Single responsibility. Cohesive 00:21:35.810 --> 00:21:38.860 around a set of properties. Additionally, it has a 00:21:38.860 --> 00:21:43.460 small public interface that, preferably, handles a handful of 00:21:43.460 --> 00:21:46.550 methods at the most. That it implements a single 00:21:46.550 --> 00:21:49.770 use-case, if possible, and that the primary logic is 00:21:49.770 --> 00:21:52.610 expressed in a composed method. 00:21:52.610 --> 00:21:53.870 That last one, I'm not gonna be covering the 00:21:53.870 --> 00:21:56.870 composed method. That's a whole nother talk. But you 00:21:56.870 --> 00:21:59.390 should check that practice out. It can really clarify 00:21:59.390 --> 00:22:02.650 code and make it much, much more understandable. 00:22:02.650 --> 00:22:04.690 So, let's look at the code we should have 00:22:04.690 --> 00:22:06.970 been driving towards as soon as the string option 00:22:06.970 --> 00:22:10.650 type showed up. We're gonna imagine right now that 00:22:10.650 --> 00:22:12.870 we have a string sheet, and we can write 00:22:12.870 --> 00:22:16.320 CommandLineOptions the way we would have with the knowledge 00:22:16.320 --> 00:22:19.250 that we have now. 00:22:19.250 --> 00:22:23.230 That needs to support boolean, string, and integer options. 00:22:23.230 --> 00:22:26.850 And remember, we have our tests at our back, 00:22:26.850 --> 00:22:30.220 making sure that we don't break anything. 00:22:30.220 --> 00:22:33.720 And, here was my first take at it on 00:22:33.720 --> 00:22:38.090 what I'd write. The class is twenty-eight lines long. 00:22:38.090 --> 00:22:40.800 It is cohesive around the properties. When we're done, 00:22:40.800 --> 00:22:43.190 most of the methods are gonna deal with the, 00:22:43.190 --> 00:22:46.830 the hash of options and the array of args. 00:22:46.830 --> 00:22:51.270 It has a single primary responsibility. Manage a collection 00:22:51.270 --> 00:22:54.180 of option objects. 00:22:54.180 --> 00:22:57.600 So now we've introduced a collaborator. It also manufactures 00:22:57.600 --> 00:23:00.750 the option objects, which I could abstract to another 00:23:00.750 --> 00:23:02.970 class. But for the moment, I'm gonna leave it. 00:23:02.970 --> 00:23:05.570 If I find it hurts in the future, then 00:23:05.570 --> 00:23:09.330 I'll change it. That's my general rule. My guideline. 00:23:09.330 --> 00:23:12.790 Is I refactor when it hurts. When making a 00:23:12.790 --> 00:23:15.520 change hurts, that's the time to refactor. 00:23:15.520 --> 00:23:20.250 My CommandLineOptions class has a small public interface. Just 00:23:20.250 --> 00:23:23.220 two methods, valid? and value. And it has no 00:23:23.220 --> 00:23:27.000 hard-coded external dependencies yet. I could mess that up 00:23:27.000 --> 00:23:29.960 and introduce those, but we're gonna avoid that. 00:23:29.960 --> 00:23:32.490 Another interesting characteristic is that, is that there are 00:23:32.490 --> 00:23:36.450 no conditional statements in this class, and we're gonna 00:23:36.450 --> 00:23:41.200 keep it that way. In Sandi Metz's 2009 Gerupo?? 00:23:41.200 --> 00:23:44.760 talk, on the Solid Principles, she said something along 00:23:44.760 --> 00:23:48.170 the lines of, a conditional in an OO language 00:23:48.170 --> 00:23:49.920 is a smell. 00:23:49.920 --> 00:23:53.820 And that's a really powerful statement. I don't think 00:23:53.820 --> 00:23:56.550 Sandi's saying that we can't use conditionals in our 00:23:56.550 --> 00:24:01.880 code, but that we use conditionals to hide abstractions. 00:24:01.880 --> 00:24:05.230 To hide our ducks. 00:24:05.230 --> 00:24:06.610 The first time I saw that talk, I don't 00:24:06.610 --> 00:24:08.020 even know if I heard her say it. It 00:24:08.020 --> 00:24:11.050 was when I went back and rewatched it. I 00:24:11.050 --> 00:24:14.210 thought, really? Then, as the years have gone on 00:24:14.210 --> 00:24:16.400 and I've been working, I've gotten to the point 00:24:16.400 --> 00:24:18.540 where I agree with her. 00:24:18.540 --> 00:24:20.960 If you have a lot of conditionals in a 00:24:20.960 --> 00:24:24.420 class, you have probably missed a concept that should 00:24:24.420 --> 00:24:28.850 be abstracted out of it. 00:24:28.850 --> 00:24:31.710 So the initialize and option method from our previous 00:24:31.710 --> 00:24:35.230 implementation carry over unchanged. Except that we're gonna store 00:24:35.230 --> 00:24:37.270 the options in a hash instead of just the 00:24:37.270 --> 00:24:39.690 type. 00:24:39.690 --> 00:24:42.080 My valid? method now simply asks all the options 00:24:42.080 --> 00:24:45.250 if they're valid, and the value method simply looks 00:24:45.250 --> 00:24:48.250 up the option hash and asks it for its 00:24:48.250 --> 00:24:51.580 value. So, now we need to build the options. 00:24:51.580 --> 00:24:53.830 We have to implement this. And this is where 00:24:53.830 --> 00:24:56.760 we're gonna instantiate the objects that represent the boolean, 00:24:56.760 --> 00:24:59.300 string, and integer options. 00:24:59.300 --> 00:25:04.340 So, now we have the CommandLineOption class, we need 00:25:04.340 --> 00:25:07.160 collaborators. In order to get anything done, CommandLineOption needs 00:25:07.160 --> 00:25:10.970 option classes to manage. It's gonna have those objects. 00:25:10.970 --> 00:25:13.670 So this is creating a dependency. And if we're 00:25:13.670 --> 00:25:15.760 gonna create a dependency in our code, we can 00:25:15.760 --> 00:25:18.020 do it in a way that's amenable to change, 00:25:18.020 --> 00:25:18.860 or we can do it in a way that's 00:25:18.860 --> 00:25:25.500 gonna make it hurt in the future. 00:25:25.500 --> 00:25:28.020 You don't want to depend, or, excuse me, you 00:25:28.020 --> 00:25:33.020 want to depend on abstractions, ot concretions. Depend on 00:25:33.020 --> 00:25:36.500 the duck type, not the concrete type. In our 00:25:36.500 --> 00:25:39.380 case, depend on the concept, the concept of an 00:25:39.380 --> 00:25:43.770 option. Not on the concrete types that implement that 00:25:43.770 --> 00:25:46.630 abstraction. 00:25:46.630 --> 00:25:49.900 In our case, option is the duck type. This 00:25:49.900 --> 00:25:52.340 is the abstraction that I missed earlier, when I 00:25:52.340 --> 00:25:57.310 just kept going down the conditional logic path. 00:25:57.310 --> 00:26:00.420 It's really simple. It has a valid? method and 00:26:00.420 --> 00:26:07.380 a value method. String option, integer option, and boolean 00:26:07.380 --> 00:26:11.450 option, those are the concrete implementation of the option 00:26:11.450 --> 00:26:15.080 abstraction. All they need is a valid? and a 00:26:15.080 --> 00:26:18.890 value method, and a consistent method of construction, and 00:26:18.890 --> 00:26:24.720 I can depend on the abstraction, not on the 00:26:24.720 --> 00:26:26.260 concretions. 00:26:26.260 --> 00:26:32.280 So, how do I do that? I could go 00:26:32.280 --> 00:26:34.830 down the case statement road again and check the 00:26:34.830 --> 00:26:37.980 option type, instantiating the correct type of the option 00:26:37.980 --> 00:26:40.970 based upon the symbol. But I'm not gonna do 00:26:40.970 --> 00:26:44.200 that, cause that would tie CommandLineClass to those concrete 00:26:44.200 --> 00:26:47.000 types, which is what we're trying to avoid. 00:26:47.000 --> 00:26:50.250 That creates a hard dependency between CommandLineOptions class and 00:26:50.250 --> 00:26:54.880 those various classes. Instead, I'm gonna use the dynamic 00:26:54.880 --> 00:26:58.310 capabilities of Ruby to instantiate those objects for us 00:26:58.310 --> 00:27:01.130 using naming conventions. For string, we're going to have 00:27:01.130 --> 00:27:06.290 a string option. For booleans, boolean option. Et cetera. 00:27:06.290 --> 00:27:09.110 I could do this even in many static languages. 00:27:09.110 --> 00:27:13.190 So this isn't something that's specific to Ruby. And 00:27:13.190 --> 00:27:15.330 this is a very. This very simple change takes 00:27:15.330 --> 00:27:19.330 out CommandLineOption class from depending on those concrete implementations 00:27:19.330 --> 00:27:23.490 and flips it to depending on the abstraction. 00:27:23.490 --> 00:27:27.510 This is dependency inversion from the Solid Principles, in 00:27:27.510 --> 00:27:33.430 practice. Alternately, some other people have suggested, you could 00:27:33.430 --> 00:27:36.190 use a hash and map from the string, boolean, 00:27:36.190 --> 00:27:39.230 and integer symbols to the concrete classes, kind of 00:27:39.230 --> 00:27:41.460 like what Sandi did in her Gilded Rose Coda?? 00:27:41.460 --> 00:27:44.320 solution earlier. 00:27:44.320 --> 00:27:47.020 That's OK. But, it is an additional thing that 00:27:47.020 --> 00:27:50.910 I have to maintain over time. It's a reason 00:27:50.910 --> 00:27:54.900 to open the CommandLineOptions and change it if I 00:27:54.900 --> 00:27:58.420 have to add a new type of option. If 00:27:58.420 --> 00:28:00.460 using the dynamic ability of Ruby bothers you, then 00:28:00.460 --> 00:28:03.420 make a hash. Personally, I'm fine with using the 00:28:03.420 --> 00:28:06.100 dynamic capabilities of my language. 00:28:06.100 --> 00:28:10.780 So, in my case, I've inoculated CommandLineOptions class from 00:28:10.780 --> 00:28:14.340 needing to change to support new option types. And 00:28:14.340 --> 00:28:16.540 at this point, this class should be closed for 00:28:16.540 --> 00:28:20.980 modification, but open for extension. 00:28:20.980 --> 00:28:23.360 So, now we need to move the logic for 00:28:23.360 --> 00:28:27.340 the various option types to the appropriate option classes. 00:28:27.340 --> 00:28:29.400 I decided to make a base class of option 00:28:29.400 --> 00:28:32.030 for my concrete types to inherit from, because the 00:28:32.030 --> 00:28:34.140 manner of initialization needs to be the same for 00:28:34.140 --> 00:28:38.000 all of them. No sense of repeating that code. 00:28:38.000 --> 00:28:40.730 And the subtypes have a cohesion around the flag 00:28:40.730 --> 00:28:43.090 attribute, and the wrong, excuse me, the flag and 00:28:43.090 --> 00:28:47.590 the raw value properties that in the code. 00:28:47.590 --> 00:28:50.030 Here's the boolean option. This one I just wrote 00:28:50.030 --> 00:28:53.040 because the requirements are so simple. Booleans are always 00:28:53.040 --> 00:28:55.460 valid, and they just return the raw_value from the 00:28:55.460 --> 00:28:57.290 command line. If it's present, it's truthy. If it's 00:28:57.290 --> 00:29:01.560 nil it's falsey. Very simple. 00:29:01.560 --> 00:29:03.190 But now we need to implement string option and 00:29:03.190 --> 00:29:06.200 integer option. And the logic for their validation and 00:29:06.200 --> 00:29:11.390 value extraction is in the old CommandLineOptions class. So, 00:29:11.390 --> 00:29:13.870 on the left are the original CommandLineOptions' valid? and 00:29:13.870 --> 00:29:17.230 value methods. On the right are those new string 00:29:17.230 --> 00:29:19.120 option and integer option classes. 00:29:19.120 --> 00:29:22.250 As you can see, the process of creating the 00:29:22.250 --> 00:29:25.830 option class was simply picking apart and disassembling the 00:29:25.830 --> 00:29:29.590 old command line option class. Moving the logic to 00:29:29.590 --> 00:29:33.790 where it belongs, using a combination of extract class 00:29:33.790 --> 00:29:37.820 and move method refactorings, we've really cleaned up the 00:29:37.820 --> 00:29:39.520 command option, CommandLineOptions. 00:29:39.520 --> 00:29:45.190 Frankly, there's not much code left there anymore. So, 00:29:45.190 --> 00:29:48.100 now we can replace that nasty, hard to understand 00:29:48.100 --> 00:29:53.340 valid? method with this. And the large value method 00:29:53.340 --> 00:29:57.510 with this. 00:29:57.510 --> 00:30:03.130 To create the specs for the various option classes, 00:30:03.130 --> 00:30:05.940 I moved the corresponding section from the CommandLineOptions spec 00:30:05.940 --> 00:30:08.460 to the corresponding area for the particular type of 00:30:08.460 --> 00:30:12.040 option, and then lightly reworked them and then I 00:30:12.040 --> 00:30:14.120 worked them from red to green, as I went 00:30:14.120 --> 00:30:17.440 through the process of extracting those classes and moving 00:30:17.440 --> 00:30:22.970 the code to those methods. 00:30:22.970 --> 00:30:25.540 We've isolated abstractions here. And how do we do 00:30:25.540 --> 00:30:30.200 that? We separate the what from the how, like 00:30:30.200 --> 00:30:35.090 we've done in CommandLineOptions. We want to move from 00:30:35.090 --> 00:30:39.130 code that looks like this to code that looks 00:30:39.130 --> 00:30:43.610 like this. 00:30:43.610 --> 00:30:48.190 The original CommandLineOptions' valid? method contained all of the 00:30:48.190 --> 00:30:51.600 how. The refactored valid? method says what we want 00:30:51.600 --> 00:30:55.230 done for us. That's it. All of the how 00:30:55.230 --> 00:30:58.030 has moved to the collaborators of our main class, 00:30:58.030 --> 00:31:02.330 in this case, StringOption, boolean option, and IntegerOption. 00:31:02.330 --> 00:31:06.220 We want to move from that looks like this 00:31:06.220 --> 00:31:08.860 to code that looks like this. Move the nitty 00:31:08.860 --> 00:31:12.220 gritty details of your code out to the leaves 00:31:12.220 --> 00:31:16.470 of your system. And let the center be a 00:31:16.470 --> 00:31:17.390 coordinator. 00:31:17.390 --> 00:31:21.320 So, when we're done with this, this is what 00:31:21.320 --> 00:31:25.580 our CommandLineOptions class looks like. These are our public 00:31:25.580 --> 00:31:30.010 methods. It provides a very small surface. And it 00:31:30.010 --> 00:31:32.730 fulfills the use case. And these are the private 00:31:32.730 --> 00:31:35.900 implementation cruft. It's necessary, but no one really needs 00:31:35.900 --> 00:31:37.480 to go poking around in here, and I've made 00:31:37.480 --> 00:31:41.360 it obvious by declaring these methods private. 00:31:41.360 --> 00:31:44.350 They're for me. Not for you. 00:31:44.350 --> 00:31:46.740 So in the end, the sum total of the 00:31:46.740 --> 00:31:50.360 implementation of the public interface, and it's all delegated. 00:31:50.360 --> 00:31:54.330 All delegated. 00:31:54.330 --> 00:31:56.040 So in the process of making the specs pass, 00:31:56.040 --> 00:31:58.850 I commented out that dreamed up code as I 00:31:58.850 --> 00:32:01.280 went through the process, and then one by one 00:32:01.280 --> 00:32:04.150 I wrote the examples and uncommented the code and 00:32:04.150 --> 00:32:09.250 made them pass, working from red to green. 00:32:09.250 --> 00:32:16.250 Then, because nothing is ever really done, my buddy 00:32:17.000 --> 00:32:20.880 says, hey. Any chance you could add the ability 00:32:20.880 --> 00:32:22.740 for me to pass an array of values for 00:32:22.740 --> 00:32:24.160 an option? 00:32:24.160 --> 00:32:28.320 So, to implement this new requirement, I only need 00:32:28.320 --> 00:32:31.640 the new array option class. So I write a 00:32:31.640 --> 00:32:37.780 spec example. Make it fail. Then create the ArrayOption 00:32:37.780 --> 00:32:43.260 class, and I'm done. And this particular example, my 00:32:43.260 --> 00:32:48.600 OptionClass is inheriting from the OptionWithContent superclass. And, cause 00:32:48.600 --> 00:32:50.650 I actually went through this and realized that strings, 00:32:50.650 --> 00:32:54.200 integers, and arrays all have content, so I abstracted 00:32:54.200 --> 00:32:56.390 that superclass and, in this case, all I have 00:32:56.390 --> 00:32:58.960 to do is write the value method of that 00:32:58.960 --> 00:33:03.330 particular type and I'm done. 00:33:03.330 --> 00:33:07.200 And it works. 00:33:07.200 --> 00:33:12.820 So, we now have a CommandLineOption class that's closed 00:33:12.820 --> 00:33:17.520 for modification, but open for extension. I could all 00:33:17.520 --> 00:33:22.350 float types, decimal types, other types of options, and 00:33:22.350 --> 00:33:24.230 I don't have to go back and touch that 00:33:24.230 --> 00:33:25.920 class again. 00:33:25.920 --> 00:33:29.390 We have small, easy to understand option classes that 00:33:29.390 --> 00:33:36.390 have a single responsibility. Oops, excuse me. 00:33:43.560 --> 00:33:46.910 We can. So, we have a easy to understand 00:33:46.910 --> 00:33:50.120 option classes that have a single responsibility, and easy 00:33:50.120 --> 00:33:54.100 to compose together with that CommandLineOption class. And we 00:33:54.100 --> 00:33:56.350 can simply create new option types and have them 00:33:56.350 --> 00:34:00.890 instantiated by convention. 00:34:00.890 --> 00:34:04.210 My name is Mark Menard. My company's Enable Labs. 00:34:04.210 --> 00:34:07.030 We do full lifecycle business productivity and sass app 00:34:07.030 --> 00:34:10.329 development, from napkin to production, as I say. And, 00:34:10.329 --> 00:34:12.079 I'm gonna be around the conference, so let's get 00:34:12.079 --> 00:34:13.440 together and talk about some code. 00:34:13.440 --> 00:34:14.668 And we can do some questions.