1 00:00:05,652 --> 00:00:09,324 Few things draw my attention like a looming deadline. 2 00:00:10,965 --> 00:00:14,558 Some part of my brain watches in morbid fascination 3 00:00:14,558 --> 00:00:16,121 as the deadline approaches 4 00:00:16,121 --> 00:00:20,121 wondering whether to call emergency services; 5 00:00:20,121 --> 00:00:21,993 or maybe just settle in, 6 00:00:21,993 --> 00:00:23,703 make popcorn, and watch the show. 7 00:00:24,866 --> 00:00:28,753 I can't help but keep a wary eye on it. 8 00:00:28,753 --> 00:00:33,973 A wary eye that is no longer paying attention to the code at hand. 9 00:00:34,303 --> 00:00:37,144 I need all the wary eyes I can get. 10 00:00:37,144 --> 00:00:39,081 Without them, forget best practices. 11 00:00:39,769 --> 00:00:43,041 I revert to less successful strategies 12 00:00:43,047 --> 00:00:44,493 like guessing; 13 00:00:44,696 --> 00:00:47,993 and desperately copying code off of Stack Overflow. 14 00:00:51,853 --> 00:00:53,863 Sometimes I'm happy. 15 00:00:54,527 --> 00:00:57,298 There are moments when the world melts away. 16 00:00:58,326 --> 00:01:00,834 Your sense of self dissipates. 17 00:01:01,762 --> 00:01:05,323 You become completely absorbed in what you're doing. 18 00:01:06,313 --> 00:01:11,252 Time appears to slow down and speed up simultaneously. 19 00:01:12,141 --> 00:01:15,085 Being awesome feels effortless. 20 00:01:16,264 --> 00:01:19,469 I've taken to coming into the office early in the morning 21 00:01:19,484 --> 00:01:22,287 and committing random acts of refactoring. 22 00:01:22,287 --> 00:01:25,379 Some people are calling this guilt-driven development. 23 00:01:27,854 --> 00:01:29,302 But really it's not. 24 00:01:29,302 --> 00:01:31,134 Refactoring just makes me happy. 25 00:01:31,701 --> 00:01:33,619 More refactoring is an optimization. 26 00:01:34,389 --> 00:01:37,730 So today I am going to tell you a story. 27 00:01:43,866 --> 00:01:47,978 The arrow needs to point the other way because this is a refactoring story. 28 00:01:49,858 --> 00:01:52,350 Anyway it has a middle... 29 00:01:52,350 --> 00:01:55,680 Actually it has a beginning, two middles and an end. 30 00:01:55,697 --> 00:01:57,127 And a moral. 31 00:01:57,835 --> 00:02:00,710 So to be clear, before I get started, 32 00:02:00,729 --> 00:02:02,527 when I say refactoring, I mean 33 00:02:02,527 --> 00:02:07,525 small careful steps that improve the structure and the readability of the code 34 00:02:07,525 --> 00:02:09,705 without changing its behavior. 35 00:02:10,277 --> 00:02:11,930 Tests are implied. 36 00:02:14,150 --> 00:02:15,280 So. 37 00:02:16,691 --> 00:02:18,233 Once upon a time 38 00:02:18,914 --> 00:02:23,126 there was an application that performed some incredibly dirty hacks 39 00:02:23,126 --> 00:02:26,549 in order to deliver data straight into a number of real-world, 40 00:02:26,556 --> 00:02:29,775 old-school, hardcopy publishing systems. 41 00:02:30,559 --> 00:02:34,999 I found this particular specimen in the dark recesses of that codebase. 42 00:02:36,462 --> 00:02:39,415 It was in a module that was 300-or-so lines long 43 00:02:39,418 --> 00:02:41,695 most of which was in a single method. 44 00:02:45,504 --> 00:02:48,297 This module talked to code all over the application. 45 00:02:48,299 --> 00:02:52,467 It dealt in temporary files, FTP; it shelled out; 46 00:02:52,467 --> 00:02:55,162 used Exif 2 to write metadata into JPEG files; 47 00:02:55,162 --> 00:02:57,383 it handled encoding. 48 00:02:58,155 --> 00:03:00,092 It had a comment in it that read: 49 00:03:00,092 --> 00:03:03,466 "A kitten dies every time this code is run." 50 00:03:07,150 --> 00:03:09,463 We had it running on a cron job. 51 00:03:15,565 --> 00:03:17,891 It had no tests 52 00:03:17,891 --> 00:03:20,170 and there was no documentation. 53 00:03:22,092 --> 00:03:24,841 This is essentially a bucket of business logic. 54 00:03:24,841 --> 00:03:27,126 It jumps through hoops in order to name files 55 00:03:27,126 --> 00:03:29,518 so that they end up where they need to be. 56 00:03:29,714 --> 00:03:32,049 Now there's a comment 57 00:03:32,049 --> 00:03:34,606 but it's almost as bad as the code. 58 00:03:35,335 --> 00:03:36,745 Also it's wrong. 59 00:03:42,752 --> 00:03:44,995 The input to the method is a target, 60 00:03:44,995 --> 00:03:47,611 which is the God object in this application. 61 00:03:47,611 --> 00:03:49,507 It's an ActiveRecord model. 62 00:03:49,507 --> 00:03:52,099 That class is 500 lines long. 63 00:03:53,311 --> 00:03:56,498 The big picture here is that information is being shoved 64 00:03:56,498 --> 00:03:59,190 onto a string in order to build up the file name. 65 00:03:59,647 --> 00:04:03,819 There is a bit of code that's not actually shovelling things onto the string. 66 00:04:04,578 --> 00:04:06,154 This draws the eye. 67 00:04:06,154 --> 00:04:09,235 It appears to be a chunk of something. 68 00:04:09,675 --> 00:04:12,750 And it's probably a good candidate for extraction. 69 00:04:13,503 --> 00:04:16,607 Another striking aspect of the code is that it's littered 70 00:04:16,607 --> 00:04:18,325 with a bunch of low-level details. 71 00:04:18,763 --> 00:04:21,475 It makes it harder to see what's going on. 72 00:04:21,475 --> 00:04:23,970 But amidst all the clutter 73 00:04:23,970 --> 00:04:27,482 there does seem to be things... 74 00:04:27,482 --> 00:04:30,862 chunks that could probably be named. 75 00:04:30,862 --> 00:04:32,649 So what are we looking at? 76 00:04:33,845 --> 00:04:35,956 We've got a very large method. 77 00:04:35,956 --> 00:04:39,387 It appears to be doing things at 2 different levels of abstraction. 78 00:04:39,387 --> 00:04:42,977 And nothing at the lower level of abstraction is being named. 79 00:04:44,084 --> 00:04:46,836 These are not characteristics of good code. 80 00:04:48,110 --> 00:04:53,634 The thing to do with big ugly code is to break it apart into small ugly code. 81 00:04:55,858 --> 00:04:59,510 There's a critical difference between breaking code down, 82 00:04:59,510 --> 00:05:01,088 and breaking code. 83 00:05:02,140 --> 00:05:06,777 The first middle of this story is gonna be about adding 84 00:05:06,777 --> 00:05:09,350 characterization tests to the method. 85 00:05:09,350 --> 00:05:12,123 And then the second middle is the actual refactoring. 86 00:05:17,810 --> 00:05:20,559 Where do you even begin testing something like this? 87 00:05:22,408 --> 00:05:26,301 We don't really know what the inputs look like. 88 00:05:27,109 --> 00:05:29,671 We certainly don't know what the output looks like. 89 00:05:29,939 --> 00:05:31,846 We do know that it's in production, 90 00:05:31,846 --> 00:05:33,539 and it appears to be working 91 00:05:33,539 --> 00:05:37,038 because we haven't had any complaints from the customer about it. 92 00:05:39,063 --> 00:05:42,501 The easiest way to discover inputs is to just send something— 93 00:05:42,501 --> 00:05:44,432 anything really—into the method 94 00:05:44,432 --> 00:05:46,463 and then see what comes back out. 95 00:05:47,098 --> 00:05:49,182 We need an assertion to work against. 96 00:05:49,182 --> 00:05:51,621 And again, it doesn't matter what that assertion is. 97 00:05:51,621 --> 00:05:54,075 It is going to be wrong. 98 00:05:55,105 --> 00:05:58,550 The nice thing about being wrong is it tells you what 'right' looks like. 99 00:05:58,931 --> 00:06:01,164 Running this fails, obviously. 100 00:06:01,164 --> 00:06:04,472 The error message tells us that we're missing an input. 101 00:06:05,309 --> 00:06:08,908 It also points us to the exact spot in the code where this is happening. 102 00:06:09,710 --> 00:06:13,981 What we find on line 6— it comes as no surprise at this point— 103 00:06:13,981 --> 00:06:18,510 the message publish_on is being sent to target 104 00:06:18,510 --> 00:06:20,073 and it returns a date. 105 00:06:20,892 --> 00:06:22,437 Meanwhile back in our tests, 106 00:06:22,437 --> 00:06:24,722 we know that the stub represents a target. 107 00:06:25,113 --> 00:06:28,713 And we are ready to start fleshing out those inputs. 108 00:06:28,713 --> 00:06:31,538 The one we need right now is publish_on, 109 00:06:31,538 --> 00:06:32,910 which we know is a date. 110 00:06:33,442 --> 00:06:36,214 And the failure tells us what our next input is. 111 00:06:37,001 --> 00:06:39,012 Digging around in the code reveals 112 00:06:39,012 --> 00:06:42,132 that xyz_category_prefix is a string. 113 00:06:42,700 --> 00:06:45,324 Next up is kind, which is also a string. 114 00:06:45,749 --> 00:06:48,677 personal is a boolean. 115 00:06:48,677 --> 00:06:51,354 id is an integer. 116 00:06:51,354 --> 00:06:54,132 And title is a string. 117 00:06:54,649 --> 00:06:58,724 And this gives us our first real failure. 118 00:06:58,724 --> 00:07:01,266 Meaning that we've gotten all the inputs right. 119 00:07:03,085 --> 00:07:04,939 These are the inputs. 120 00:07:04,939 --> 00:07:07,121 The messages that we need to send to target 121 00:07:07,121 --> 00:07:08,938 in order to build up a file name. 122 00:07:09,238 --> 00:07:10,739 And with the inputs in place, 123 00:07:10,739 --> 00:07:12,176 we're also given the output, 124 00:07:12,176 --> 00:07:13,401 which looks like this. 125 00:07:14,744 --> 00:07:18,008 And now all that practice copying-and-pasting from Stack Overflow 126 00:07:18,008 --> 00:07:19,242 comes in handy. 127 00:07:19,242 --> 00:07:23,432 Copy that string from the failure into the assertion and the test should pass. 128 00:07:25,801 --> 00:07:27,516 It doesn't. 129 00:07:30,493 --> 00:07:32,974 The fix for this is to use a regex. 130 00:07:32,974 --> 00:07:35,085 This gives us our first passing test. 131 00:07:35,907 --> 00:07:37,769 We're not quite done yet. 132 00:07:38,609 --> 00:07:42,430 There are a bunch of low-level details that aren't being exercised. 133 00:07:42,344 --> 00:07:44,747 And there are alternate paths through the method. 134 00:07:45,009 --> 00:07:48,168 We need to improve the inputs for these 3 lines of code 135 00:07:48,168 --> 00:07:52,138 and add test cases for the lines that have conditionals in them. 136 00:07:54,514 --> 00:07:57,671 At every step, failing tests tell us how to tweak our assertions. 137 00:07:57,671 --> 00:08:00,591 These are trivial to fix; boring to watch. 138 00:08:00,591 --> 00:08:02,585 So I'll show you some highlights. 139 00:08:03,415 --> 00:08:06,135 publish_on gets zero-padded, 140 00:08:06,135 --> 00:08:08,557 and pi doesn't actually get affected by this 141 00:08:08,557 --> 00:08:09,919 so we'll fall back on e. 142 00:08:11,001 --> 00:08:13,398 kind gsubs out underscores. 143 00:08:13,398 --> 00:08:15,975 We need an underscore so we can see it being removed. 144 00:08:16,555 --> 00:08:19,878 title appears to strip out everything. 145 00:08:19,878 --> 00:08:22,758 And the existing input doesn't have very much cruft in it. 146 00:08:22,758 --> 00:08:25,247 So we need some numbers and some funky characters 147 00:08:25,247 --> 00:08:27,785 and a couple of upper-case letters. 148 00:08:31,547 --> 00:08:34,011 There's something fishy about that regex. 149 00:08:40,759 --> 00:08:42,301 What's with the square brackets? 150 00:08:44,091 --> 00:08:46,084 I'm too lazy to reason about this 151 00:08:46,084 --> 00:08:47,632 so I'm adding a test case, 152 00:08:47,632 --> 00:08:49,687 overriding the stub's input for title, 153 00:08:49,687 --> 00:08:51,302 giving it some square brackets. 154 00:08:51,302 --> 00:08:53,681 And that proves that brackets get left in place. 155 00:08:54,425 --> 00:08:56,592 This is probably not the intended behavior. 156 00:08:57,682 --> 00:09:00,255 This test now serves as documentation 157 00:09:00,255 --> 00:09:02,443 until we can clarify this with the customer. 158 00:09:03,538 --> 00:09:05,969 The first conditional deals in personalization 159 00:09:05,969 --> 00:09:07,803 and our stub doesn't personalize. 160 00:09:07,803 --> 00:09:11,722 So we need a test for the case that actually does. 161 00:09:13,970 --> 00:09:17,055 We're informed about yet another input that's missing. 162 00:09:17,055 --> 00:09:18,567 It's trivial to supply. 163 00:09:18,567 --> 00:09:23,349 The other conditional on that line of code provides a fallback in case age is nil. 164 00:09:23,349 --> 00:09:26,589 And the test for this stubs out the same inputs as the previous one. 165 00:09:27,568 --> 00:09:30,627 The final conditional is a ternary statement 166 00:09:30,627 --> 00:09:34,191 that is trying to determine where to truncate the title. 167 00:09:36,057 --> 00:09:38,940 Our default input for the title is neither long nor short. 168 00:09:38,940 --> 00:09:42,048 So by making it longer we can make sure that it gets truncated. 169 00:09:42,048 --> 00:09:46,970 And then the only case left to test for is a title that is too short 170 00:09:46,970 --> 00:09:48,115 to get truncated. 171 00:09:50,205 --> 00:09:54,008 At this point we have protection against regressions. 172 00:09:57,854 --> 00:09:59,870 What have we actually accomplished so far? 173 00:10:01,706 --> 00:10:05,651 We took a piece of undocumented, untested code. 174 00:10:05,651 --> 00:10:07,345 And with a bit of hand-waving, 175 00:10:07,345 --> 00:10:10,660 we got fake assertions to give us the inputs. 176 00:10:10,660 --> 00:10:13,261 The inputs gave us the outputs. 177 00:10:13,261 --> 00:10:16,860 And the outputs gave us the real assertions. 178 00:10:17,350 --> 00:10:19,233 It's almost karmic. 179 00:10:20,059 --> 00:10:21,856 Then we had to reason about the code. 180 00:10:21,856 --> 00:10:23,319 We inspected every line. 181 00:10:23,319 --> 00:10:28,874 Made sure that we had inputs that would actually exercise those lines 182 00:10:28,874 --> 00:10:31,678 and then we made sure that every branch of every conditional 183 00:10:31,678 --> 00:10:33,101 was called from a test. 184 00:10:37,622 --> 00:10:40,895 There's no specification here. 185 00:10:40,895 --> 00:10:42,474 There's no design being done. 186 00:10:43,224 --> 00:10:46,385 We're getting a regression test suite. 187 00:10:47,196 --> 00:10:50,672 We are also getting documentation up to and including the fact 188 00:10:50,672 --> 00:10:52,545 that we probably have a bug. 189 00:10:53,385 --> 00:10:56,996 The biggest win at this point is that we can start changing things 190 00:10:56,996 --> 00:10:58,846 without breaking into a cold sweat. 191 00:11:00,376 --> 00:11:01,914 So here's what we're gonna do. 192 00:11:01,914 --> 00:11:03,558 We're gonna isolate the method. 193 00:11:03,558 --> 00:11:08,492 And then we're going to extract a bunch of smaller methods. 194 00:11:08,492 --> 00:11:12,236 I'm gonna borrow a detailed prescription from Martin Fowler's book Refactoring, 195 00:11:12,236 --> 00:11:15,138 called Replace Method with Method Object. 196 00:11:15,595 --> 00:11:18,863 Traditionally you do this refactoring when you have big computation 197 00:11:18,863 --> 00:11:20,481 and a bunch of temporary variables 198 00:11:20,481 --> 00:11:23,541 and you don't want to be passing those temporary variables around 199 00:11:23,541 --> 00:11:25,525 as you extract methods. 200 00:11:25,525 --> 00:11:29,487 Here, we pretty much only have this one target to worry about. 201 00:11:30,408 --> 00:11:33,733 So first of all, we need to make a home for this code. 202 00:11:33,733 --> 00:11:36,614 Add an initializer that takes the target. 203 00:11:36,614 --> 00:11:37,868 Add an attribute for it. 204 00:11:37,868 --> 00:11:40,062 And just copy-paste the whole thing in there. 205 00:11:41,971 --> 00:11:44,028 The method shouldn't be called on the class. 206 00:11:44,028 --> 00:11:45,671 It needs a better name. 207 00:11:45,671 --> 00:11:47,845 And it no longer takes any arguments. 208 00:11:47,845 --> 00:11:49,716 Back in the old module, 209 00:11:49,716 --> 00:11:52,207 we need to reference the new method from the old one, 210 00:11:52,207 --> 00:11:54,300 so just delete the whole body of the method, 211 00:11:54,300 --> 00:11:55,625 reference the new file, 212 00:11:55,625 --> 00:11:56,797 instantiate the class, 213 00:11:56,797 --> 00:11:58,461 pass in the target, call name on it, 214 00:11:58,461 --> 00:11:59,501 and we're green. 215 00:11:59,501 --> 00:12:02,492 Which means that we have a license to go to town on this code. 216 00:12:04,680 --> 00:12:05,817 Where to begin? 217 00:12:06,982 --> 00:12:08,695 You could start anywhere. 218 00:12:08,695 --> 00:12:11,070 I always like to delete something. 219 00:12:11,070 --> 00:12:13,804 Let's go ahead and get rid of that comment. 220 00:12:14,905 --> 00:12:16,554 And now somewhat arbitrarily, 221 00:12:16,554 --> 00:12:18,639 I've chosen to go from biggest to smallest. 222 00:12:18,639 --> 00:12:20,882 The biggest chunk that we've seen for extraction 223 00:12:20,882 --> 00:12:22,178 is the truncated title bit. 224 00:12:22,178 --> 00:12:26,939 The actual mechanics of a method extraction is as follows: 225 00:12:27,720 --> 00:12:31,609 Create an empty method and name it by what it does 226 00:12:31,609 --> 00:12:34,311 or what it is, 227 00:12:34,311 --> 00:12:35,944 not by how it does it. 228 00:12:36,631 --> 00:12:39,104 Coming up with a good name is often the hardest thing 229 00:12:39,104 --> 00:12:40,719 you'll ever do in a refactoring. 230 00:12:41,997 --> 00:12:46,039 The next step is to copy the lines of code from the source method into the new one. 231 00:12:46,039 --> 00:12:48,875 Scan the new method for local variables. 232 00:12:48,875 --> 00:12:52,249 filename here is declared in the source method, 233 00:12:52,249 --> 00:12:54,114 so we have a choice to make. 234 00:12:54,114 --> 00:12:55,785 We can either do the mutation here, 235 00:12:55,785 --> 00:12:57,497 which means passing the filename in, 236 00:12:57,497 --> 00:12:59,581 or making it an instance variable. 237 00:12:59,581 --> 00:13:02,153 Or we could make this a query method. 238 00:13:02,153 --> 00:13:06,159 Now the whole point of the refactoring is to separate the 2 levels of abstraction— 239 00:13:06,159 --> 00:13:07,920 building up the filename in one place 240 00:13:07,920 --> 00:13:10,461 and putting together all the little pieces in another. 241 00:13:10,461 --> 00:13:12,877 So we're gonna just return the value that we need. 242 00:13:13,542 --> 00:13:16,644 The other temporary variables here are all local to this method. 243 00:13:18,054 --> 00:13:20,537 We need to assign the result of our new query method 244 00:13:20,537 --> 00:13:23,263 and delete the temporary variables that are no longer used 245 00:13:23,263 --> 00:13:24,610 in the source method. 246 00:13:24,610 --> 00:13:26,275 And then we're green again. 247 00:13:27,303 --> 00:13:28,146 Before we move on 248 00:13:28,146 --> 00:13:29,983 I'd like to tidy this up a bit. 249 00:13:30,211 --> 00:13:33,495 There's some unnecessary work going on in the regex. 250 00:13:33,495 --> 00:13:36,124 We're doing a case-insensitive match, 251 00:13:36,124 --> 00:13:38,324 and then we're downcasing afterwards. 252 00:13:40,620 --> 00:13:42,466 That's kinda backwards. 253 00:13:42,801 --> 00:13:46,000 There's also something in the match-if brackets that drives me nuts. 254 00:13:46,870 --> 00:13:49,761 The spurious parentheses have got to go. 255 00:13:50,102 --> 00:13:53,437 Another thing that bugs me is the fact that we have a ternary statement 256 00:13:53,437 --> 00:13:55,535 to decide the range for the match-if. 257 00:13:56,175 --> 00:13:58,973 A match-if is not going to raise an exception. 258 00:13:58,973 --> 00:14:01,856 If you try to truncate a 4-character string to 9 characters, 259 00:14:01,856 --> 00:14:03,792 it'll just return 4 characters. 260 00:14:04,612 --> 00:14:06,280 The ternary statement can go. 261 00:14:06,870 --> 00:14:08,760 Since we no longer need truncate_to, 262 00:14:08,760 --> 00:14:11,232 we don't need to worry about the length of the title. 263 00:14:11,232 --> 00:14:12,158 So that can go too. 264 00:14:12,759 --> 00:14:15,098 And with those 2 lines gone, 265 00:14:15,098 --> 00:14:19,100 there seems very little point in having a temporary variable, 266 00:14:19,100 --> 00:14:20,637 so we can ditch that as well 267 00:14:20,637 --> 00:14:24,115 effectively leaving us with a single line of code in truncated_title. 268 00:14:25,771 --> 00:14:29,014 The longest line of code is now the hexdigest stuff. 269 00:14:29,014 --> 00:14:32,636 Now, hexdigest is a terrible name in this context. 270 00:14:37,796 --> 00:14:39,033 Copy stuff in. 271 00:14:39,033 --> 00:14:40,386 Assign the result. 272 00:14:40,386 --> 00:14:41,719 And we're green again. 273 00:14:41,719 --> 00:14:43,917 The personalization line is now the longest. 274 00:14:45,157 --> 00:14:46,605 Perform a quick extraction. 275 00:14:47,640 --> 00:14:49,227 publication_day 276 00:14:51,537 --> 00:14:54,235 and xyz_category_prefix. 277 00:14:54,235 --> 00:14:56,736 Inside this class, xyz is redundant. 278 00:14:56,736 --> 00:14:59,558 The fact that it's a prefix is irrelevant. 279 00:14:59,558 --> 00:15:02,099 So we can drop the xyz prefix 280 00:15:02,099 --> 00:15:04,297 as well as the prefix suffix 281 00:15:04,297 --> 00:15:06,573 and just call the new method category. 282 00:15:08,650 --> 00:15:10,120 And then we've got kind. 283 00:15:10,120 --> 00:15:12,555 This leaves us with a fairly readable method. 284 00:15:12,555 --> 00:15:14,858 We could totally leave it at this 285 00:15:14,858 --> 00:15:17,056 but there's still some spurious stuff in here. 286 00:15:17,056 --> 00:15:19,578 publication_day for instance. 287 00:15:21,252 --> 00:15:23,032 publication_day returns a string. 288 00:15:23,801 --> 00:15:26,059 So, why are we interpolating it? 289 00:15:27,919 --> 00:15:29,458 Same goes for category 290 00:15:29,458 --> 00:15:30,822 and kind. 291 00:15:32,162 --> 00:15:33,576 target.id is an integer 292 00:15:33,576 --> 00:15:36,255 but string interpolation implicitly calls to_s on it. 293 00:15:38,345 --> 00:15:41,086 This next move might seem a little bit subtle 294 00:15:41,086 --> 00:15:43,616 until you recognize that kids' game Five Differences. 295 00:15:43,616 --> 00:15:47,830 Which line of code here is different from all of the other pieces? 296 00:15:48,957 --> 00:15:54,247 The first line was the only one that's not being shovelled onto the string, 297 00:15:54,247 --> 00:15:57,789 so we can start off with an empty string 298 00:15:57,789 --> 00:16:00,402 and then make the first piece just like all the others. 299 00:16:00,899 --> 00:16:02,169 Up to a point. 300 00:16:02,169 --> 00:16:04,478 The extension is also different. 301 00:16:04,478 --> 00:16:07,659 If you conceptually separate this into 2 jobs— 302 00:16:07,659 --> 00:16:09,062 building up the filename, 303 00:16:09,062 --> 00:16:10,831 and then slapping the extension on it 304 00:16:10,831 --> 00:16:12,643 —we're left with 2 distinct sections: 305 00:16:12,643 --> 00:16:15,011 one where everything is smooshed together, 306 00:16:15,011 --> 00:16:18,832 and another where the pieces are separated by underscores. 307 00:16:19,333 --> 00:16:22,043 If we extract the smooshed together pieces, 308 00:16:22,043 --> 00:16:26,735 then all the remaining pieces are separated by an underscore. 309 00:16:27,660 --> 00:16:30,485 We can now shovel them into an array rather than a string. 310 00:16:32,065 --> 00:16:35,103 This gets rid of a bunch more of the interpolation syntax, 311 00:16:35,103 --> 00:16:37,246 joining them with an underscore. 312 00:16:40,761 --> 00:16:41,711 Done. 313 00:16:43,282 --> 00:16:44,600 Is it perfect? 314 00:16:44,600 --> 00:16:45,706 Of course not. 315 00:16:46,065 --> 00:16:47,321 Is it better? 316 00:16:47,321 --> 00:16:48,454 Hell yeah! 317 00:16:49,485 --> 00:16:52,855 We went from this to this in less than 30 steps. 318 00:16:54,105 --> 00:16:55,655 Let me just show you that again. 319 00:16:56,367 --> 00:16:57,461 Before. 320 00:16:58,309 --> 00:16:59,532 After. 321 00:17:01,297 --> 00:17:02,450 Before. 322 00:17:02,450 --> 00:17:03,760 After. 323 00:17:06,609 --> 00:17:08,353 So first we quarantined the method. 324 00:17:08,353 --> 00:17:10,256 Then we extracted stuff. 325 00:17:11,044 --> 00:17:13,704 Extracting stuff basically boils down to 326 00:17:13,704 --> 00:17:16,818 identifying a piece of code that performs a subtask 327 00:17:16,818 --> 00:17:18,523 and then giving that code a name. 328 00:17:19,778 --> 00:17:21,385 Back when we started out, 329 00:17:21,385 --> 00:17:23,026 we identified 3 code smells: 330 00:17:23,026 --> 00:17:24,264 a large method, 331 00:17:24,264 --> 00:17:25,918 2 different levels of abstraction, 332 00:17:25,918 --> 00:17:27,785 unnamed abstractions. 333 00:17:27,785 --> 00:17:31,015 Abstracting methods addressed all 3 of those issues. 334 00:17:32,889 --> 00:17:35,571 At that point we still had some ragged edges though. 335 00:17:35,571 --> 00:17:36,951 So we kinda picked through it 336 00:17:36,951 --> 00:17:38,829 and removed pointless cruft. 337 00:17:42,071 --> 00:17:44,739 This concludes the second middle of the refactoring story 338 00:17:44,739 --> 00:17:47,796 and I'm gonna end by taking a closer look at pointless cruft. 339 00:17:51,007 --> 00:17:54,015 In the field of information design, 340 00:17:54,015 --> 00:17:57,339 chartjunk refers to visual elements in charts and graphs 341 00:17:57,339 --> 00:17:59,483 that add noise. 342 00:17:59,631 --> 00:18:01,874 They don't help you understand the data. 343 00:18:01,874 --> 00:18:04,880 They often get in the way of comprehension. 344 00:18:04,880 --> 00:18:07,183 To give you an idea of what we're talking about 345 00:18:07,183 --> 00:18:09,430 this graph gets just about everything wrong. 346 00:18:12,487 --> 00:18:15,455 This is the exact same data presented without chartjunk. 347 00:18:15,455 --> 00:18:18,313 Codejunk is the Ruby equivalent of chartjunk. 348 00:18:18,313 --> 00:18:20,609 This is not about coding practices. 349 00:18:20,609 --> 00:18:24,681 It's not about naming and SRP and small methods and DRY. 350 00:18:24,681 --> 00:18:26,120 It's about noise. 351 00:18:26,889 --> 00:18:30,123 I've tried to classify codejunk and I came up with this list. 352 00:18:31,143 --> 00:18:32,216 #10 353 00:18:35,493 --> 00:18:37,878 Does a bear shit in the woods? 354 00:18:40,069 --> 00:18:42,863 Comments shouldn't echo the implementation. 355 00:18:43,524 --> 00:18:44,911 They shouldn't be wrong. 356 00:18:48,906 --> 00:18:50,545 They shouldn't be imprecise. 357 00:18:51,473 --> 00:18:53,155 And they shouldn't be misspelled. 358 00:18:55,870 --> 00:18:56,864 #9 359 00:18:56,864 --> 00:18:58,039 It is my conviction 360 00:18:58,039 --> 00:19:01,699 that everyone should make their editor show them trailing whitespace. 361 00:19:06,618 --> 00:19:08,860 If you leave it in, you have noise in your code. 362 00:19:08,860 --> 00:19:11,399 If you then take it out, you have noise in your diffs, 363 00:19:11,399 --> 00:19:13,441 which is just wrong on so many levels. 364 00:19:14,374 --> 00:19:17,681 #8 If it's dead, let it rest in peace. 365 00:19:20,688 --> 00:19:22,744 What were you saving it for? 366 00:19:25,076 --> 00:19:26,805 You need to learn to let go. 367 00:19:34,170 --> 00:19:36,905 What is this doing in source control? 368 00:19:38,542 --> 00:19:39,466 #7 369 00:19:50,490 --> 00:19:52,124 More parentheses. 370 00:19:52,124 --> 00:19:53,327 This is vile. 371 00:19:55,697 --> 00:19:57,630 When I see this, I secretly wonder if you 372 00:19:57,630 --> 00:20:00,260 wash your hands after you go to the bathroom. 373 00:20:04,011 --> 00:20:06,691 #6 Intelligent defaults. 374 00:20:07,792 --> 00:20:14,293 Yes. 4 characters saved are 4 characters that you can spend another day. 375 00:20:15,186 --> 00:20:16,832 Or something. 376 00:20:17,451 --> 00:20:19,116 Let's talk about dependencies. 377 00:20:19,116 --> 00:20:23,167 In particular, dependencies that you're not actually depending on. 378 00:20:24,380 --> 00:20:26,461 This may or may not impact performance. 379 00:20:26,461 --> 00:20:29,343 It probably will impact the performance of your tests. 380 00:20:29,343 --> 00:20:30,844 A whole different story. 381 00:20:30,844 --> 00:20:33,777 Mostly you just added noise to the code. 382 00:20:34,646 --> 00:20:36,760 #4 This is familiar territory. 383 00:20:38,036 --> 00:20:39,463 Recognize this? 384 00:20:39,463 --> 00:20:41,156 Not to beat a dead horse, 385 00:20:41,156 --> 00:20:43,571 but ew. 386 00:20:44,741 --> 00:20:48,663 #3 Don't do work that the computer is already doing for you. 387 00:20:51,247 --> 00:20:52,506 More string interpolation. 388 00:20:52,506 --> 00:20:55,179 I could've technicallly folded this into the previous one 389 00:20:55,179 --> 00:20:56,657 but I'm trying to get to 10. 390 00:20:59,092 --> 00:21:00,222 This is the same thing. 391 00:21:00,200 --> 00:21:02,376 The call to map is superfluous here. 392 00:21:02,749 --> 00:21:04,907 The match-if brackets don't need your help. 393 00:21:04,907 --> 00:21:05,965 We've seen that one. 394 00:21:06,417 --> 00:21:10,140 In this example the computer isn't doing the work for you but should be. 395 00:21:10,950 --> 00:21:13,226 #2 Test suites. 396 00:21:13,226 --> 00:21:20,709 They should be curated, maintained, fed, watered, coddled and minimized. 397 00:21:22,257 --> 00:21:26,459 The only exception I can think of is if documenting behavior outweighs the cost. 398 00:21:26,459 --> 00:21:28,292 Choose wisely. 399 00:21:29,333 --> 00:21:31,832 And finally, the compounding sin. 400 00:21:31,832 --> 00:21:36,012 You know the old saying, "1 + 1 = 3 for very large values of 1"? 401 00:21:39,905 --> 00:21:41,144 This is revolting. 402 00:21:41,680 --> 00:21:44,624 In fact, it bears a horrifying resemblance to the code that 403 00:21:44,624 --> 00:21:45,871 we started out with today. 404 00:21:46,400 --> 00:21:49,056 When people say that software is grown, not built, 405 00:21:49,056 --> 00:21:51,684 I'm pretty sure that this is what they're talking about. 406 00:21:52,024 --> 00:21:53,186 It's kinda like fungus. 407 00:21:56,154 --> 00:21:59,355 Earlier I said about the refactoring that after we extracted methods 408 00:21:59,355 --> 00:22:02,174 we kinda polished up the code a little. 409 00:22:02,174 --> 00:22:05,380 I'm gonna restate that in light of the previous section. 410 00:22:05,380 --> 00:22:07,615 First, we extracted methods. 411 00:22:07,615 --> 00:22:09,425 Then we eliminated codejunk. 412 00:22:11,146 --> 00:22:13,448 That was the ending of the refactoring story. 413 00:22:13,679 --> 00:22:15,819 Before I get to the moral, I'd like to mention 414 00:22:15,819 --> 00:22:17,649 the whole thing lives on Github. 415 00:22:18,254 --> 00:22:19,690 It was committed step-by-step, 416 00:22:19,690 --> 00:22:21,740 so if you wanna look at any of the mechanics 417 00:22:21,740 --> 00:22:23,133 or run the tests or anything, 418 00:22:23,133 --> 00:22:25,633 just dig through the commit history. It's all there. 419 00:22:29,884 --> 00:22:36,989 I have a vague recollection of the night when that code was written. 420 00:22:38,369 --> 00:22:39,717 Let me rephrase that: 421 00:22:40,547 --> 00:22:42,517 I vaguely recall writing it. 422 00:22:44,213 --> 00:22:45,023 Yeah. 423 00:22:47,452 --> 00:22:49,959 When I panic, I write godawful code. 424 00:22:50,645 --> 00:22:55,157 Other people do yoga, or meditate, or go white water rafting. 425 00:22:55,814 --> 00:22:57,221 I refactor. 426 00:22:57,221 --> 00:22:59,497 It soothes me. 427 00:23:00,311 --> 00:23:03,980 Science—if you believe that sorta thing— 428 00:23:03,980 --> 00:23:06,382 actually explains why. 429 00:23:06,382 --> 00:23:08,630 The key player in the game is working memory. 430 00:23:08,630 --> 00:23:11,911 Working memory is the thing that allows you to do mental arithmetic. 431 00:23:11,911 --> 00:23:16,198 You store temporary results off in memory as you work through 432 00:23:16,198 --> 00:23:17,509 other parts of the problem. 433 00:23:18,478 --> 00:23:23,745 The main differentiator in performance of tasks that rely on working memory, 434 00:23:23,745 --> 00:23:27,877 is the type of strategy you're able to apply to a problem. 435 00:23:27,877 --> 00:23:29,694 If you have better working memory 436 00:23:29,694 --> 00:23:33,827 you can apply more complex, more successful strategies. 437 00:23:34,056 --> 00:23:35,443 Here's the deal: 438 00:23:36,663 --> 00:23:39,566 when you worry in ways that involve mental chatter, 439 00:23:39,566 --> 00:23:43,204 you use up available slots in your working memory. 440 00:23:43,204 --> 00:23:48,600 You no longer have the swap space necessary to use complex strategies 441 00:23:48,600 --> 00:23:52,972 and you revert to less successful ones like copying stuff off of Stack Overflow. 442 00:23:53,640 --> 00:23:55,769 This is where refactoring comes in. 443 00:23:55,929 --> 00:23:57,523 Refactoring makes you smarter. 444 00:23:58,525 --> 00:24:01,799 Refactoring basically gives you an exobrain. 445 00:24:01,799 --> 00:24:05,720 You offload a bunch of those little details—that 446 00:24:05,720 --> 00:24:08,013 under normal circumstances go into working memory 447 00:24:08,013 --> 00:24:09,809 —into your tests. 448 00:24:10,257 --> 00:24:13,388 Once you start refactoring, you start reclaiming your brain. 449 00:24:13,998 --> 00:24:16,214 It actively counteracts panic. 450 00:24:17,318 --> 00:24:21,323 One of the really unexpected things that happened when I started refactoring 451 00:24:21,323 --> 00:24:23,776 simply for the act of refactoring itself was 452 00:24:23,776 --> 00:24:26,861 that I started optimizing for happiness. 453 00:24:28,455 --> 00:24:30,989 I would specifically put something into its own class 454 00:24:30,989 --> 00:24:33,748 just so that I could load just that class and nothing else. 455 00:24:34,278 --> 00:24:37,717 The feedback loop you get when you have subsecond test suites 456 00:24:37,717 --> 00:24:40,756 for the thing that you're working on is unbelievable. 457 00:24:41,704 --> 00:24:44,051 It's a huge enabler for flow. 458 00:24:45,682 --> 00:24:46,881 Flow is nice. 459 00:24:47,766 --> 00:24:49,200 Flow is more than nice. 460 00:24:49,200 --> 00:24:51,077 It's like a bliss cocktail. 461 00:24:52,612 --> 00:24:56,721 Selfishly isolating code to improve my own happiness 462 00:24:56,721 --> 00:24:59,399 turned out to be a pretty good deal for the code as well. 463 00:24:59,872 --> 00:25:01,689 Suddenly I was avoiding dependencies. 464 00:25:01,689 --> 00:25:04,809 I was constantly asking myself 465 00:25:04,809 --> 00:25:06,883 "Can this be a separate class?" 466 00:25:06,883 --> 00:25:08,206 "How about a gem?" 467 00:25:08,206 --> 00:25:12,152 "What is the unrelated subproblem here?" 468 00:25:12,152 --> 00:25:14,731 It made a dramatic difference. 469 00:25:16,271 --> 00:25:19,935 In summary, refactoring makes you smarter by giving you an exobrain. 470 00:25:19,935 --> 00:25:23,379 It is preventative and curative with respect to panic. 471 00:25:23,379 --> 00:25:28,070 If you optimize for developer happiness by making your tests really fast, 472 00:25:28,070 --> 00:25:32,410 you get loosely coupled code that adheres to the Single Responsibility Principle. 473 00:25:33,152 --> 00:25:34,327 Thank you.