Janet Riley

Begin Refactoring with Naming the Unknown

November 11, 2014 | In 30 days

I'm in the midst of adding features to existing code where the original developer is not available. This is a double challenge. Not only must I figure out how to add new logic, I'm still deciphering what the code does.

My first step has been to add names. In stories, knowing a creature's name gives power over it. Good names add context. They indicate the significance of something so I don't have to deduce it. Names communicate that understanding to the next maintainer.

Reading over the code, I noticed several magic numbers, like '3'. 3 what? Sides on a triangle? After some sleuthing I replaced it with the constant NUM_BEARS. I'm in the midst of adding features to existing code where the original developer is not available. This is a double challenge. Not only must I figure out how to add new logic, I'm still deciphering what the code does.

My first step has been to add names. In stories, knowing a creature's name gives power over it. Good names add context. They indicate the significance of something so I don't have to deduce it. Names communicate that understanding to the next maintainer.

Reading over the code, I noticed several magic numbers, like '3'. 3 what? Sides on a triangle? After some sleuthing I replaced it with the constant NUM_BEARS. There's a danger of mistaking one 3 for another and lying with code. 3s looks much alike. I discovered a few places where there was a different meaning, and confirmed each replacement manually to be certain.

We can give variables more descriptive names. 'Input2' may be literally correct, in that it's the second input field on a form. 'CharacterName' tells us much more. IDEs like IntelliJ make it safe and easy to rename variables. Later I could refactor the HTML field names as well.

Related constants can be ordered in a hash, especially where there's a sequence. It doesn't change the code, but it helps the humans. For example, in a Goldilocks game,

var PORRIDGE_LEVEL = { TOO_HOT: 'hot', TOO_COLD:'cold', JUST_RIGHT: 'ok'}

Arbitrary conditionals can be encapsulated in methods that describe what we're looking at. I can move the conditions in if( bigBowl.empty && middleBowl.empty && littleBowl.empty) to a descriptive and testable method like porridgeEaten()

Speaking of testable, big blocks of code can be broken into functions. Consider a long narrative method that orchestrates what happens at each step of the game. It checks where Goldilocks is in the story with a bunch of if statements, and describes what should happen at each step. I've extracted the story chapters into their own shorter methods, like kitchenScene() and bearsEatGoldilocks(). This breaks several hundred lines up into shorter methods. We understand how each method participates in the story, and we can add tests that don't require replaying the entire game.

Once we name what we see, we can improve it.

Recommended:
Michael Feathers' Working Effectively with Legacy Code is invaluable and theraputic. Chapter 22, "I Need to Change a Monster Method and I Can't Write Tests For It", has been particularly helpful this week.