< Return to Video

Cascadia Ruby Conf 2012 Therapeutic Refactoring by Katrina Owen

  • 0:06 - 0:09
    Few things draw my attention
    like a looming deadline.
  • 0:11 - 0:15
    Some part of my brain
    watches in morbid fascination
  • 0:15 - 0:16
    as the deadline approaches
  • 0:16 - 0:20
    wondering whether to
    call emergency services;
  • 0:20 - 0:22
    or maybe just settle in,
  • 0:22 - 0:24
    make popcorn,
    and watch the show.
  • 0:25 - 0:29
    I can't help but keep a wary eye on it.
  • 0:29 - 0:34
    A wary eye that is no longer
    paying attention to the code at hand.
  • 0:34 - 0:37
    I need all the wary eyes I can get.
  • 0:37 - 0:39
    Without them, forget best practices.
  • 0:40 - 0:43
    I revert to less successful strategies
  • 0:43 - 0:44
    like guessing;
  • 0:45 - 0:48
    and desperately copying code off
    of Stack Overflow.
  • 0:52 - 0:54
    Sometimes I'm happy.
  • 0:55 - 0:57
    There are moments when
    the world melts away.
  • 0:58 - 1:01
    Your sense of self dissipates.
  • 1:02 - 1:05
    You become
    completely absorbed in what you're doing.
  • 1:06 - 1:11
    Time appears to slow down
    and speed up simultaneously.
  • 1:12 - 1:15
    Being awesome feels effortless.
  • 1:16 - 1:19
    I've taken to coming into
    the office early in the morning
  • 1:19 - 1:22
    and committing random acts of refactoring.
  • 1:22 - 1:25
    Some people are calling this
    guilt-driven development.
  • 1:28 - 1:29
    But really it's not.
  • 1:29 - 1:31
    Refactoring just makes me happy.
  • 1:32 - 1:34
    More refactoring is an optimization.
  • 1:34 - 1:38
    So today I am going to tell you a story.
  • 1:44 - 1:48
    The arrow needs to point the other way
    because this is a refactoring story.
  • 1:50 - 1:52
    Anyway it has a middle...
  • 1:52 - 1:56
    Actually it has a beginning,
    two middles and an end.
  • 1:56 - 1:57
    And a moral.
  • 1:58 - 2:01
    So to be clear, before I get started,
  • 2:01 - 2:03
    when I say refactoring, I mean
  • 2:03 - 2:08
    small careful steps that improve the
    structure and the readability of the code
  • 2:08 - 2:10
    without changing its behavior.
  • 2:10 - 2:12
    Tests are implied.
  • 2:14 - 2:15
    So.
  • 2:17 - 2:18
    Once upon a time
  • 2:19 - 2:23
    there was an application that
    performed some incredibly dirty hacks
  • 2:23 - 2:27
    in order to deliver data straight
    into a number of real-world,
  • 2:27 - 2:30
    old-school, hardcopy publishing systems.
  • 2:31 - 2:35
    I found this particular specimen
    in the dark recesses of that codebase.
  • 2:36 - 2:39
    It was in a module that
    was 300-or-so lines long
  • 2:39 - 2:42
    most of which was in a single method.
  • 2:46 - 2:48
    This module talked to code
    all over the application.
  • 2:48 - 2:52
    It dealt in temporary files, FTP;
    it shelled out;
  • 2:52 - 2:55
    used Exif 2 to write
    metadata into JPEG files;
  • 2:55 - 2:57
    it handled encoding.
  • 2:58 - 3:00
    It had a comment in it that read:
  • 3:00 - 3:03
    "A kitten dies every time
    this code is run."
  • 3:07 - 3:09
    We had it running on a cron job.
  • 3:16 - 3:18
    It had no tests
  • 3:18 - 3:20
    and there was no documentation.
  • 3:22 - 3:25
    This is essentially
    a bucket of business logic.
  • 3:25 - 3:27
    It jumps through hoops
    in order to name files
  • 3:27 - 3:30
    so that they end up where they need to be.
  • 3:30 - 3:32
    Now there's a comment
  • 3:32 - 3:35
    but it's almost as bad as the code.
  • 3:35 - 3:37
    Also it's wrong.
  • 3:43 - 3:45
    The input to the method is a target,
  • 3:45 - 3:48
    which is the God object
    in this application.
  • 3:48 - 3:50
    It's an ActiveRecord model.
  • 3:50 - 3:52
    That class is 500 lines long.
  • 3:53 - 3:56
    The big picture here is
    that information is being shoved
  • 3:56 - 3:59
    onto a string in order to
    build up the file name.
  • 4:00 - 4:04
    There is a bit of code that's not actually
    shovelling things onto the string.
  • 4:05 - 4:06
    This draws the eye.
  • 4:06 - 4:09
    It appears to be a chunk of something.
  • 4:10 - 4:13
    And it's probably a good
    candidate for extraction.
  • 4:14 - 4:17
    Another striking aspect of the code
    is that it's littered
  • 4:17 - 4:18
    with a bunch of low-level details.
  • 4:19 - 4:21
    It makes it harder to see what's going on.
  • 4:21 - 4:24
    But amidst all the clutter
  • 4:24 - 4:27
    there does seem to be things...
  • 4:27 - 4:31
    chunks that could probably be named.
  • 4:31 - 4:33
    So what are we looking at?
  • 4:34 - 4:36
    We've got a very large method.
  • 4:36 - 4:39
    It appears to be doing things
    at 2 different levels of abstraction.
  • 4:39 - 4:43
    And nothing at the lower level
    of abstraction is being named.
  • 4:44 - 4:47
    These are not
    characteristics of good code.
  • 4:48 - 4:54
    The thing to do with big ugly code
    is to break it apart into small ugly code.
  • 4:56 - 5:00
    There's a critical difference
    between breaking code down,
  • 5:00 - 5:01
    and breaking code.
  • 5:02 - 5:07
    The first middle of this story
    is gonna be about adding
  • 5:07 - 5:09
    characterization tests to the method.
  • 5:09 - 5:12
    And then the second middle is
    the actual refactoring.
  • 5:18 - 5:21
    Where do you even begin
    testing something like this?
  • 5:22 - 5:26
    We don't really know
    what the inputs look like.
  • 5:27 - 5:30
    We certainly don't know
    what the output looks like.
  • 5:30 - 5:32
    We do know that it's in production,
  • 5:32 - 5:34
    and it appears to be working
  • 5:34 - 5:37
    because we haven't had any complaints
    from the customer about it.
  • 5:39 - 5:43
    The easiest way to discover inputs
    is to just send something—
  • 5:43 - 5:44
    anything really—into the method
  • 5:44 - 5:46
    and then see what comes back out.
  • 5:47 - 5:49
    We need an assertion to work against.
  • 5:49 - 5:52
    And again, it doesn't matter
    what that assertion is.
  • 5:52 - 5:54
    It is going to be wrong.
  • 5:55 - 5:59
    The nice thing about being wrong
    is it tells you what 'right' looks like.
  • 5:59 - 6:01
    Running this fails, obviously.
  • 6:01 - 6:04
    The error message tells us
    that we're missing an input.
  • 6:05 - 6:09
    It also points us to the exact spot
    in the code where this is happening.
  • 6:10 - 6:14
    What we find on line 6—
    it comes as no surprise at this point—
  • 6:14 - 6:19
    the message publish_on
    is being sent to target
  • 6:19 - 6:20
    and it returns a date.
  • 6:21 - 6:22
    Meanwhile back in our tests,
  • 6:22 - 6:25
    we know that the stub represents a target.
  • 6:25 - 6:29
    And we are ready to start
    fleshing out those inputs.
  • 6:29 - 6:32
    The one we need right now is publish_on,
  • 6:32 - 6:33
    which we know is a date.
  • 6:33 - 6:36
    And the failure tells us
    what our next input is.
  • 6:37 - 6:39
    Digging around in the code reveals
  • 6:39 - 6:42
    that xyz_category_prefix is a string.
  • 6:43 - 6:45
    Next up is kind, which is also a string.
  • 6:46 - 6:49
    personal is a boolean.
  • 6:49 - 6:51
    id is an integer.
  • 6:51 - 6:54
    And title is a string.
  • 6:55 - 6:59
    And this gives us our first real failure.
  • 6:59 - 7:01
    Meaning that we've gotten
    all the inputs right.
  • 7:03 - 7:05
    These are the inputs.
  • 7:05 - 7:07
    The messages that we need
    to send to target
  • 7:07 - 7:09
    in order to build up a file name.
  • 7:09 - 7:11
    And with the inputs in place,
  • 7:11 - 7:12
    we're also given the output,
  • 7:12 - 7:13
    which looks like this.
  • 7:15 - 7:18
    And now all that practice
    copying-and-pasting from Stack Overflow
  • 7:18 - 7:19
    comes in handy.
  • 7:19 - 7:23
    Copy that string from the failure into
    the assertion and the test should pass.
  • 7:26 - 7:28
    It doesn't.
  • 7:30 - 7:33
    The fix for this is to use a regex.
  • 7:33 - 7:35
    This gives us our first passing test.
  • 7:36 - 7:38
    We're not quite done yet.
  • 7:39 - 7:42
    There are a bunch of low-level details
    that aren't being exercised.
  • 7:42 - 7:45
    And there are alternate paths
    through the method.
  • 7:45 - 7:48
    We need to improve the inputs
    for these 3 lines of code
  • 7:48 - 7:52
    and add test cases for the lines
    that have conditionals in them.
  • 7:55 - 7:58
    At every step, failing tests tell us
    how to tweak our assertions.
  • 7:58 - 8:01
    These are trivial to fix; boring to watch.
  • 8:01 - 8:03
    So I'll show you some highlights.
  • 8:03 - 8:06
    publish_on gets zero-padded,
  • 8:06 - 8:09
    and pi doesn't actually
    get affected by this
  • 8:09 - 8:10
    so we'll fall back on e.
  • 8:11 - 8:13
    kind gsubs out underscores.
  • 8:13 - 8:16
    We need an underscore
    so we can see it being removed.
  • 8:17 - 8:20
    title appears to strip out everything.
  • 8:20 - 8:23
    And the existing input
    doesn't have very much cruft in it.
  • 8:23 - 8:25
    So we need some numbers
    and some funky characters
  • 8:25 - 8:28
    and a couple of upper-case letters.
  • 8:32 - 8:34
    There's something fishy about that regex.
  • 8:41 - 8:42
    What's with the square brackets?
  • 8:44 - 8:46
    I'm too lazy to reason about this
  • 8:46 - 8:48
    so I'm adding a test case,
  • 8:48 - 8:50
    overriding the stub's input for title,
  • 8:50 - 8:51
    giving it some square brackets.
  • 8:51 - 8:54
    And that proves that
    brackets get left in place.
  • 8:54 - 8:57
    This is probably not
    the intended behavior.
  • 8:58 - 9:00
    This test now serves as documentation
  • 9:00 - 9:02
    until we can clarify this
    with the customer.
  • 9:04 - 9:06
    The first conditional deals in
    personalization
  • 9:06 - 9:08
    and our stub doesn't personalize.
  • 9:08 - 9:12
    So we need a test for the case
    that actually does.
  • 9:14 - 9:17
    We're informed about yet another input
    that's missing.
  • 9:17 - 9:19
    It's trivial to supply.
  • 9:19 - 9:23
    The other conditional on that line of code
    provides a fallback in case age is nil.
  • 9:23 - 9:27
    And the test for this stubs out
    the same inputs as the previous one.
  • 9:28 - 9:31
    The final conditional is
    a ternary statement
  • 9:31 - 9:34
    that is trying to determine
    where to truncate the title.
  • 9:36 - 9:39
    Our default input for the title is
    neither long nor short.
  • 9:39 - 9:42
    So by making it longer
    we can make sure that it gets truncated.
  • 9:42 - 9:47
    And then the only case left to test for
    is a title that is too short
  • 9:47 - 9:48
    to get truncated.
  • 9:50 - 9:54
    At this point we have
    protection against regressions.
  • 9:58 - 10:00
    What have we actually accomplished so far?
  • 10:02 - 10:06
    We took a piece of
    undocumented, untested code.
  • 10:06 - 10:07
    And with a bit of hand-waving,
  • 10:07 - 10:11
    we got fake assertions
    to give us the inputs.
  • 10:11 - 10:13
    The inputs gave us the outputs.
  • 10:13 - 10:17
    And the outputs
    gave us the real assertions.
  • 10:17 - 10:19
    It's almost karmic.
  • 10:20 - 10:22
    Then we had to reason about the code.
  • 10:22 - 10:23
    We inspected every line.
  • 10:23 - 10:29
    Made sure that we had inputs
    that would actually exercise those lines
  • 10:29 - 10:32
    and then we made sure that
    every branch of every conditional
  • 10:32 - 10:33
    was called from a test.
  • 10:38 - 10:41
    There's no specification here.
  • 10:41 - 10:42
    There's no design being done.
  • 10:43 - 10:46
    We're getting a regression test suite.
  • 10:47 - 10:51
    We are also getting documentation
    up to and including the fact
  • 10:51 - 10:53
    that we probably have a bug.
  • 10:53 - 10:57
    The biggest win at this point
    is that we can start changing things
  • 10:57 - 10:59
    without breaking into a cold sweat.
  • 11:00 - 11:02
    So here's what we're gonna do.
  • 11:02 - 11:04
    We're gonna isolate the method.
  • 11:04 - 11:08
    And then we're going to
    extract a bunch of smaller methods.
  • 11:08 - 11:12
    I'm gonna borrow a detailed prescription
    from Martin Fowler's book Refactoring,
  • 11:12 - 11:15
    called Replace Method with Method Object.
  • 11:16 - 11:19
    Traditionally you do this refactoring
    when you have big computation
  • 11:19 - 11:20
    and a bunch of temporary variables
  • 11:20 - 11:24
    and you don't want to be passing
    those temporary variables around
  • 11:24 - 11:26
    as you extract methods.
  • 11:26 - 11:29
    Here, we pretty much only have
    this one target to worry about.
  • 11:30 - 11:34
    So first of all,
    we need to make a home for this code.
  • 11:34 - 11:37
    Add an initializer that takes the target.
  • 11:37 - 11:38
    Add an attribute for it.
  • 11:38 - 11:40
    And just copy-paste
    the whole thing in there.
  • 11:42 - 11:44
    The method shouldn't be called
    on the class.
  • 11:44 - 11:46
    It needs a better name.
  • 11:46 - 11:48
    And it no longer takes any arguments.
  • 11:48 - 11:50
    Back in the old module,
  • 11:50 - 11:52
    we need to reference the new method
    from the old one,
  • 11:52 - 11:54
    so just delete the whole body
    of the method,
  • 11:54 - 11:56
    reference the new file,
  • 11:56 - 11:57
    instantiate the class,
  • 11:57 - 11:58
    pass in the target,
    call name on it,
  • 11:58 - 12:00
    and we're green.
  • 12:00 - 12:02
    Which means that we have
    a license to go to town on this code.
  • 12:05 - 12:06
    Where to begin?
  • 12:07 - 12:09
    You could start anywhere.
  • 12:09 - 12:11
    I always like to delete something.
  • 12:11 - 12:14
    Let's go ahead and
    get rid of that comment.
  • 12:15 - 12:17
    And now somewhat arbitrarily,
  • 12:17 - 12:19
    I've chosen to go
    from biggest to smallest.
  • 12:19 - 12:21
    The biggest chunk
    that we've seen for extraction
  • 12:21 - 12:22
    is the truncated title bit.
  • 12:22 - 12:27
    The actual mechanics of
    a method extraction is as follows:
  • 12:28 - 12:32
    Create an empty method
    and name it by what it does
  • 12:32 - 12:34
    or what it is,
  • 12:34 - 12:36
    not by how it does it.
  • 12:37 - 12:39
    Coming up with a good name
    is often the hardest thing
  • 12:39 - 12:41
    you'll ever do in a refactoring.
  • 12:42 - 12:46
    The next step is to copy the lines of code
    from the source method into the new one.
  • 12:46 - 12:49
    Scan the new method for local variables.
  • 12:49 - 12:52
    filename here is declared
    in the source method,
  • 12:52 - 12:54
    so we have a choice to make.
  • 12:54 - 12:56
    We can either do the mutation here,
  • 12:56 - 12:57
    which means passing the filename in,
  • 12:57 - 13:00
    or making it an instance variable.
  • 13:00 - 13:02
    Or we could make this a query method.
  • 13:02 - 13:06
    Now the whole point of the refactoring is
    to separate the 2 levels of abstraction—
  • 13:06 - 13:08
    building up the filename in one place
  • 13:08 - 13:10
    and putting together all the little pieces
    in another.
  • 13:10 - 13:13
    So we're gonna just return
    the value that we need.
  • 13:14 - 13:17
    The other temporary variables here
    are all local to this method.
  • 13:18 - 13:21
    We need to assign the result
    of our new query method
  • 13:21 - 13:23
    and delete the temporary variables
    that are no longer used
  • 13:23 - 13:25
    in the source method.
  • 13:25 - 13:26
    And then we're green again.
  • 13:27 - 13:28
    Before we move on
  • 13:28 - 13:30
    I'd like to tidy this up a bit.
  • 13:30 - 13:33
    There's some unnecessary work
    going on in the regex.
  • 13:33 - 13:36
    We're doing a case-insensitive match,
  • 13:36 - 13:38
    and then we're downcasing afterwards.
  • 13:41 - 13:42
    That's kinda backwards.
  • 13:43 - 13:46
    There's also something in the
    match-if brackets that drives me nuts.
  • 13:47 - 13:50
    The spurious parentheses have got to go.
  • 13:50 - 13:53
    Another thing that bugs me is
    the fact that we have a ternary statement
  • 13:53 - 13:56
    to decide the range for the match-if.
  • 13:56 - 13:59
    A match-if is not going
    to raise an exception.
  • 13:59 - 14:02
    If you try to truncate a 4-character
    string to 9 characters,
  • 14:02 - 14:04
    it'll just return 4 characters.
  • 14:05 - 14:06
    The ternary statement can go.
  • 14:07 - 14:09
    Since we no longer need truncate_to,
  • 14:09 - 14:11
    we don't need to worry
    about the length of the title.
  • 14:11 - 14:12
    So that can go too.
  • 14:13 - 14:15
    And with those 2 lines gone,
  • 14:15 - 14:19
    there seems very little point
    in having a temporary variable,
  • 14:19 - 14:21
    so we can ditch that as well
  • 14:21 - 14:24
    effectively leaving us with
    a single line of code in truncated_title.
  • 14:26 - 14:29
    The longest line of code
    is now the hexdigest stuff.
  • 14:29 - 14:33
    Now, hexdigest is a terrible name
    in this context.
  • 14:38 - 14:39
    Copy stuff in.
  • 14:39 - 14:40
    Assign the result.
  • 14:40 - 14:42
    And we're green again.
  • 14:42 - 14:44
    The personalization line
    is now the longest.
  • 14:45 - 14:47
    Perform a quick extraction.
  • 14:48 - 14:49
    publication_day
  • 14:52 - 14:54
    and xyz_category_prefix.
  • 14:54 - 14:57
    Inside this class, xyz is redundant.
  • 14:57 - 15:00
    The fact that it's a prefix is irrelevant.
  • 15:00 - 15:02
    So we can drop the xyz prefix
  • 15:02 - 15:04
    as well as the prefix suffix
  • 15:04 - 15:07
    and just call the new method category.
  • 15:09 - 15:10
    And then we've got kind.
  • 15:10 - 15:13
    This leaves us with
    a fairly readable method.
  • 15:13 - 15:15
    We could totally leave it at this
  • 15:15 - 15:17
    but there's still
    some spurious stuff in here.
  • 15:17 - 15:20
    publication_day for instance.
  • 15:21 - 15:23
    publication_day returns a string.
  • 15:24 - 15:26
    So, why are we interpolating it?
  • 15:28 - 15:29
    Same goes for category
  • 15:29 - 15:31
    and kind.
  • 15:32 - 15:34
    target.id is an integer
  • 15:34 - 15:36
    but string interpolation
    implicitly calls to_s on it.
  • 15:38 - 15:41
    This next move might seem
    a little bit subtle
  • 15:41 - 15:44
    until you recognize that kids' game
    Five Differences.
  • 15:44 - 15:48
    Which line of code here is different
    from all of the other pieces?
  • 15:49 - 15:54
    The first line was the only one that's
    not being shovelled onto the string,
  • 15:54 - 15:58
    so we can start off with an empty string
  • 15:58 - 16:00
    and then make the first piece
    just like all the others.
  • 16:01 - 16:02
    Up to a point.
  • 16:02 - 16:04
    The extension is also different.
  • 16:04 - 16:08
    If you conceptually separate
    this into 2 jobs—
  • 16:08 - 16:09
    building up the filename,
  • 16:09 - 16:11
    and then slapping the extension on it
  • 16:11 - 16:13
    —we're left with 2 distinct sections:
  • 16:13 - 16:15
    one where everything is smooshed together,
  • 16:15 - 16:19
    and another where the pieces
    are separated by underscores.
  • 16:19 - 16:22
    If we extract
    the smooshed together pieces,
  • 16:22 - 16:27
    then all the remaining pieces
    are separated by an underscore.
  • 16:28 - 16:30
    We can now shovel them into an array
    rather than a string.
  • 16:32 - 16:35
    This gets rid of a bunch more
    of the interpolation syntax,
  • 16:35 - 16:37
    joining them with an underscore.
  • 16:41 - 16:42
    Done.
  • 16:43 - 16:45
    Is it perfect?
  • 16:45 - 16:46
    Of course not.
  • 16:46 - 16:47
    Is it better?
  • 16:47 - 16:48
    Hell yeah!
  • 16:49 - 16:53
    We went from this to this
    in less than 30 steps.
  • 16:54 - 16:56
    Let me just show you that again.
  • 16:56 - 16:57
    Before.
  • 16:58 - 17:00
    After.
  • 17:01 - 17:02
    Before.
  • 17:02 - 17:04
    After.
  • 17:07 - 17:08
    So first we quarantined the method.
  • 17:08 - 17:10
    Then we extracted stuff.
  • 17:11 - 17:14
    Extracting stuff basically boils down to
  • 17:14 - 17:17
    identifying a piece of code
    that performs a subtask
  • 17:17 - 17:19
    and then giving that code a name.
  • 17:20 - 17:21
    Back when we started out,
  • 17:21 - 17:23
    we identified 3 code smells:
  • 17:23 - 17:24
    a large method,
  • 17:24 - 17:26
    2 different levels of abstraction,
  • 17:26 - 17:28
    unnamed abstractions.
  • 17:28 - 17:31
    Abstracting methods addressed
    all 3 of those issues.
  • 17:33 - 17:36
    At that point we still had
    some ragged edges though.
  • 17:36 - 17:37
    So we kinda picked through it
  • 17:37 - 17:39
    and removed pointless cruft.
  • 17:42 - 17:45
    This concludes the second middle
    of the refactoring story
  • 17:45 - 17:48
    and I'm gonna end by taking a closer look
    at pointless cruft.
  • 17:51 - 17:54
    In the field of information design,
  • 17:54 - 17:57
    chartjunk refers to visual elements
    in charts and graphs
  • 17:57 - 17:59
    that add noise.
  • 18:00 - 18:02
    They don't help you understand the data.
  • 18:02 - 18:05
    They often get in the way of
    comprehension.
  • 18:05 - 18:07
    To give you an idea of
    what we're talking about
  • 18:07 - 18:09
    this graph gets just about
    everything wrong.
  • 18:12 - 18:15
    This is the exact same data
    presented without chartjunk.
  • 18:15 - 18:18
    Codejunk is the Ruby equivalent
    of chartjunk.
  • 18:18 - 18:21
    This is not about coding practices.
  • 18:21 - 18:25
    It's not about naming and SRP
    and small methods and DRY.
  • 18:25 - 18:26
    It's about noise.
  • 18:27 - 18:30
    I've tried to classify codejunk
    and I came up with this list.
  • 18:31 - 18:32
    #10
  • 18:35 - 18:38
    Does a bear shit in the woods?
  • 18:40 - 18:43
    Comments shouldn't echo
    the implementation.
  • 18:44 - 18:45
    They shouldn't be wrong.
  • 18:49 - 18:51
    They shouldn't be imprecise.
  • 18:51 - 18:53
    And they shouldn't be misspelled.
  • 18:56 - 18:57
    #9
  • 18:57 - 18:58
    It is my conviction
  • 18:58 - 19:02
    that everyone should make their editor
    show them trailing whitespace.
  • 19:07 - 19:09
    If you leave it in,
    you have noise in your code.
  • 19:09 - 19:11
    If you then take it out,
    you have noise in your diffs,
  • 19:11 - 19:13
    which is just wrong on so many levels.
  • 19:14 - 19:18
    #8 If it's dead, let it rest in peace.
  • 19:21 - 19:23
    What were you saving it for?
  • 19:25 - 19:27
    You need to learn to let go.
  • 19:34 - 19:37
    What is this doing in source control?
  • 19:39 - 19:39
    #7
  • 19:50 - 19:52
    More parentheses.
  • 19:52 - 19:53
    This is vile.
  • 19:56 - 19:58
    When I see this,
    I secretly wonder if you
  • 19:58 - 20:00
    wash your hands after you
    go to the bathroom.
  • 20:04 - 20:07
    #6 Intelligent defaults.
  • 20:08 - 20:14
    Yes. 4 characters saved are 4 characters
    that you can spend another day.
  • 20:15 - 20:17
    Or something.
  • 20:17 - 20:19
    Let's talk about dependencies.
  • 20:19 - 20:23
    In particular, dependencies that
    you're not actually depending on.
  • 20:24 - 20:26
    This may or may not impact performance.
  • 20:26 - 20:29
    It probably will impact the performance
    of your tests.
  • 20:29 - 20:31
    A whole different story.
  • 20:31 - 20:34
    Mostly you just added noise to the code.
  • 20:35 - 20:37
    #4 This is familiar territory.
  • 20:38 - 20:39
    Recognize this?
  • 20:39 - 20:41
    Not to beat a dead horse,
  • 20:41 - 20:44
    but ew.
  • 20:45 - 20:49
    #3 Don't do work that the computer
    is already doing for you.
  • 20:51 - 20:53
    More string interpolation.
  • 20:53 - 20:55
    I could've technicallly folded this into
    the previous one
  • 20:55 - 20:57
    but I'm trying to get to 10.
  • 20:59 - 21:00
    This is the same thing.
  • 21:00 - 21:02
    The call to map is superfluous here.
  • 21:03 - 21:05
    The match-if brackets
    don't need your help.
  • 21:05 - 21:06
    We've seen that one.
  • 21:06 - 21:10
    In this example the computer isn't
    doing the work for you but should be.
  • 21:11 - 21:13
    #2 Test suites.
  • 21:13 - 21:21
    They should be curated, maintained, fed,
    watered, coddled and minimized.
  • 21:22 - 21:26
    The only exception I can think of is if
    documenting behavior outweighs the cost.
  • 21:26 - 21:28
    Choose wisely.
  • 21:29 - 21:32
    And finally, the compounding sin.
  • 21:32 - 21:36
    You know the old saying,
    "1 + 1 = 3 for very large values of 1"?
  • 21:40 - 21:41
    This is revolting.
  • 21:42 - 21:45
    In fact, it bears a horrifying resemblance
    to the code that
  • 21:45 - 21:46
    we started out with today.
  • 21:46 - 21:49
    When people say that
    software is grown, not built,
  • 21:49 - 21:52
    I'm pretty sure that this
    is what they're talking about.
  • 21:52 - 21:53
    It's kinda like fungus.
  • 21:56 - 21:59
    Earlier I said about the refactoring
    that after we extracted methods
  • 21:59 - 22:02
    we kinda polished up the code a little.
  • 22:02 - 22:05
    I'm gonna restate that
    in light of the previous section.
  • 22:05 - 22:08
    First, we extracted methods.
  • 22:08 - 22:09
    Then we eliminated codejunk.
  • 22:11 - 22:13
    That was the ending of
    the refactoring story.
  • 22:14 - 22:16
    Before I get to the moral,
    I'd like to mention
  • 22:16 - 22:18
    the whole thing lives on Github.
  • 22:18 - 22:20
    It was committed step-by-step,
  • 22:20 - 22:22
    so if you wanna look
    at any of the mechanics
  • 22:22 - 22:23
    or run the tests or anything,
  • 22:23 - 22:26
    just dig through the commit history.
    It's all there.
  • 22:30 - 22:37
    I have a vague recollection of the night
    when that code was written.
  • 22:38 - 22:40
    Let me rephrase that:
  • 22:41 - 22:43
    I vaguely recall writing it.
  • 22:44 - 22:45
    Yeah.
  • 22:47 - 22:50
    When I panic, I write godawful code.
  • 22:51 - 22:55
    Other people do yoga, or meditate,
    or go white water rafting.
  • 22:56 - 22:57
    I refactor.
  • 22:57 - 22:59
    It soothes me.
  • 23:00 - 23:04
    Science—if you believe that sorta thing—
  • 23:04 - 23:06
    actually explains why.
  • 23:06 - 23:09
    The key player in the game
    is working memory.
  • 23:09 - 23:12
    Working memory is the thing
    that allows you to do mental arithmetic.
  • 23:12 - 23:16
    You store temporary results off in memory
    as you work through
  • 23:16 - 23:18
    other parts of the problem.
  • 23:18 - 23:24
    The main differentiator in performance
    of tasks that rely on working memory,
  • 23:24 - 23:28
    is the type of strategy
    you're able to apply to a problem.
  • 23:28 - 23:30
    If you have better working memory
  • 23:30 - 23:34
    you can apply more complex,
    more successful strategies.
  • 23:34 - 23:35
    Here's the deal:
  • 23:37 - 23:40
    when you worry in ways
    that involve mental chatter,
  • 23:40 - 23:43
    you use up available slots
    in your working memory.
  • 23:43 - 23:49
    You no longer have the swap space
    necessary to use complex strategies
  • 23:49 - 23:53
    and you revert to less successful ones
    like copying stuff off of Stack Overflow.
  • 23:54 - 23:56
    This is where refactoring comes in.
  • 23:56 - 23:58
    Refactoring makes you smarter.
  • 23:59 - 24:02
    Refactoring basically gives you
    an exobrain.
  • 24:02 - 24:06
    You offload a bunch of those
    little details—that
  • 24:06 - 24:08
    under normal circumstances
    go into working memory
  • 24:08 - 24:10
    —into your tests.
  • 24:10 - 24:13
    Once you start refactoring,
    you start reclaiming your brain.
  • 24:14 - 24:16
    It actively counteracts panic.
  • 24:17 - 24:21
    One of the really unexpected things
    that happened when I started refactoring
  • 24:21 - 24:24
    simply for the act
    of refactoring itself was
  • 24:24 - 24:27
    that I started optimizing
    for happiness.
  • 24:28 - 24:31
    I would specifically put something
    into its own class
  • 24:31 - 24:34
    just so that I could load
    just that class and nothing else.
  • 24:34 - 24:38
    The feedback loop you get
    when you have subsecond test suites
  • 24:38 - 24:41
    for the thing that you're working on
    is unbelievable.
  • 24:42 - 24:44
    It's a huge enabler for flow.
  • 24:46 - 24:47
    Flow is nice.
  • 24:48 - 24:49
    Flow is more than nice.
  • 24:49 - 24:51
    It's like a bliss cocktail.
  • 24:53 - 24:57
    Selfishly isolating code
    to improve my own happiness
  • 24:57 - 24:59
    turned out to be a pretty good deal
    for the code as well.
  • 25:00 - 25:02
    Suddenly I was avoiding dependencies.
  • 25:02 - 25:05
    I was constantly asking myself
  • 25:05 - 25:07
    "Can this be a separate class?"
  • 25:07 - 25:08
    "How about a gem?"
  • 25:08 - 25:12
    "What is the unrelated subproblem here?"
  • 25:12 - 25:15
    It made a dramatic difference.
  • 25:16 - 25:20
    In summary, refactoring makes you smarter
    by giving you an exobrain.
  • 25:20 - 25:23
    It is preventative and curative
    with respect to panic.
  • 25:23 - 25:28
    If you optimize for developer happiness
    by making your tests really fast,
  • 25:28 - 25:32
    you get loosely coupled code that adheres
    to the Single Responsibility Principle.
  • 25:33 - 25:34
    Thank you.
Title:
Cascadia Ruby Conf 2012 Therapeutic Refactoring by Katrina Owen
Description:

more » « less
Video Language:
English
Duration:
26:05

English subtitles

Revisions