Book Report: Refactoring by Martin Fowler

Refactoring is a book covering the basics tenants of refactoring as
dictated by Martin Fowler: a very smart person with some very good
ideas about code in general.

First, the interesting thing about the definition of refactoring (as
defined by this book) is that it doesn’t encompass all code
cleanup. It explicitly defines refactoring as a disciplined practice
that involves:

  • a rigorous test suite to ensure code behaves as desired beforehand.
  • a set of steps that ensures that, at every step, the code works as before.

There’s a lot of gems in this book. ‘Refactoring’ not only covers the
basic tenants around refactoring, but also provides a great set of
guidelines around writing code that is very easy for future
maintainers to understand as well.

The Indicators for Refactoring

After showing a great example of a step-by-step refactoring of code
that excellently preserves functionality, the next chapter describes
several code smells that indicate the need for a refactor:

  • duplicate code: a common red flag for anyone familiar with the age
    old adage DRY (Don’t repeat yourself)
  • long methods: definitely a good sign for a refactor. I can’t recall
    how many methods I’ve read where I’ve barely been able to keep mental track
    of what’s really going on here.
  • strong coupling: Definitely not an easy one to catch when you’re
    hacking away hardcore at something. Sometimes it takes a real objective look at
    your code to find that the two classes or methods that you’ve been working with
    should really be one, or maybe organized separately.

Aside from this, the book explicitly describes several situations
which indicate the need to consider refactoring. That said (and Martin
also admits this), it’s not even close to outlining every single
situation where refactoring is necessary. After all, programming,
despite requiring a very logical and objective mind, can be a very
subjective practice.

The Actual Refactorings

After going over the smells, the next chapters finally describe the
actual refactoring themselves. The description of the refactoring
themselves is very rigorous, covering motivation, explicit steps and
examples. It’s a very good reference to cover all of your bases, and
like any book that describes patterns, is a good reference to keep
somewhere when tackling particularly difficult refactoring tasks.

A lot of the refactors were ones I was already familiar with, but
there were some interesting cases I didn’t really think a lot about, that
‘Refactoring’ helped me assess more deeply:

Replace Temp with Query

The summary of this description is to replace temporary variables with
a method that generates the state desired:

def shift_left(digits, value):
    multiplier = 2 ** digits
    return value * multiplier

After:

def shift_left(digits, value):
    return value * _power_of_two(digits)

def _power_of_two(digits):
    return 2 ** digits

This is a trivial example, and not necessarily representative of a
real refactoring. However, using a ‘query method’ to generate state
helps prevent several bad patterns from emerging:

  • modifying the local variable to be different than the initial intention
  • ensure that the variable is not misused anywhere else

It’s a good example of a refactoring that help ensure the variable is
actually temporary, and is not misused.

Introduce Explaining Variable

At the end of the day, good code is 90% about making it easier for
others to read. Code that works is great, but code that can not be
understood or maintained is not going to last when that code is
encountered a second time.

Explaining variables really help here. This is the idea of making
ambiguous code more clearer by assigning results to named variables that
express the intent a lot better:

def interest(amount, percentage, period):
    return amount * (1.414 ** (percentage / period))

After:

def interest(amount, percentage, period):
    e_constant = 1.414
    return amount * Ce_constant ** (percentage / period))

Having very descriptive variables can make understanding the code a
lot easier.

Remove Assignment to Parameters

This is saying basically avoid mutating input parameters:

def multiply(x, y):
    x *= y
    return x

After:

def multiply(x, y):
    result = x * y
    return result

This is nice because it makes it easier to work with input parameters
later: mutating values that have clear intent can result to poor
misuse of those variables later (because you assume no one changed it,
or it actually describes the value it should). This could be
inefficient, but compiler optimizers can get rid of these
inefficiencies anyway, so why make it more confusing to a potential
consumer?

Duplicate Observed Data

This is basically pushing for a decoupling of data stored on both a
client (interface) as well as a publisher. There’s a lot of times
where the client will store data that’s almost identical to an object
that already exists and has all the information stored neatly. Reducing the
duplication of data is always a good thing.

Separate Query from Modifier

There’s a lot of methods that not only perform formatting or retrieve
data, but also mutate data as well. This refactoring suggests
separating them:

def retrieve_name(log_object):
    log_object.access_count += 1
    return [str(x) for x in log_object.names]

After:

def increment_access_count(log_object):
  log_object.access_count += 1

def retrieve_name(log_object):
  return [str(x) for x in log_object.names]

increment_access_count(log_object)
return retrieve_name(log_object)

I can’t count the number of times I wanted to have one specific part
of the function a function performs. Refactorings such as this one
really give modular pieces that can be stitched together as necessary.

The General Refactoring Principles

The book’s scatters some great gems about what a good refactoring
looks like, and it’s very similar to what is commonly known to be good
code:

  • mostly self-documenting: code that is so easily legible that it your
    barely even need comments to understand what it’s doing: intelligible
    variable and function names, written like plain English more that code.
  • modular: each function is split into small, singularly functional units.
  • taking advantage of the principles and idioms for the language at
    hand: ‘refactoring’ was written with object-oriented languages in
    mind, so it advocated strong utilization of OOP. Utilize the
    programming language’s strengths.

Any step that takes your code in that direction (whilst preserving
functionality) is a good example of a refactoring.

How to Allocate Time to Refactor

‘Refactoring’ also stresses and appropriate time to refactor code:
constantly. Martin Fowler argues refactoring should occur during the
development process, and time should be added to estimates to give
space for refactoring. I’ve never been given explicit amounts of time
to refactor code, and most of the time, you won’t. Best thing to do is
to push yourself to refactor whenever it’s appropriate. The book also
warns against going overboard, only refactoring what you need. It’s a very
agile approach to the idea of refactoring.

Conclusion

Ultimately, ‘Refactoring’ doesn’t blow my mind and introduce me to
some life-changing concept. That said, it definitely changed my
mindset about refactoring. Refactoring should:

  • be done as you go
  • move the code toward being easily comprehensible
  • move the code toward being easily extendable
  • have a strong set of testing around it to preserve functionality

As I was about to tackle a fairly large refactoring, It was a great
read to organize my thoughts about my methodologies and practices, and
my goals.

I don’t recommend reading every word, but the chapters that explain
philosophies and glancing over the refactoring patters was more that
worth the time spent reading.

Author: toumorokoshi

Love to code, love emacs!