Refactoring by Martin Fowler – Ch. 1 Summary

I was recommended this book during a conversation with a more senior engineer I had recently, and decided A) to give it a read, and B) to write short summations of each chapter as I go through it, as well as a longer, more in-depth one once I finish it.

I’ve said it before, by writing about a topic you’re forced to explain the concepts in your own words(or more specifically, in the way that makes the most sense to you), which solidifies your understanding of them. With that said, I’ll try and outline what I think are the main points from this first chapter.

The Purpose of Refactoring

Any fool can write code that a computer can understand. Good programmers write code that humans can understand.

Code that works isn’t that difficult to write. The problem is, software is a never-ending work in progress, usually among groups of people.

Great software requires a lot of programmers working together, which means eventually someone else will be looking at your code to try and modify it or build off of it.

It doesn’t matter how well it works or how elegant you think it is. If another programmer struggles to understand it, or they don’t notice the small edge cases caused by it, or they need to use one section of your code in a different way but can’t because it’s hopelessly intertwined in a 50 line long function, it’s not good code.

Good code is easy to understand, modular, re-usable, and most of all:

The true test of good code is how easy it is to change it.

Let’s take a look at those pieces one by one.

Good code is easy to understand

The main takeaway I’d put under this point relates to how you name your functions and variables. The example Fowler gives in the book is that of a company that conducts plays, and the bills they submit to the people who contract their services.

As an example, here are some of the names he gives the functions and classes that calculate the cost and return the final bill for the customer:

  • createStatementData(invoice, plays)
  • enrichPerformance(aPerformance)
  • playFor(aPerformance)
  • totalAmount(data)
  • totalVolumeCredits(data)
  • createPerformanceCalculator(aPerformance, aPlay)
  • class PerformanceCalculator
  • class TragedyCalculator extends PerformanceCalculator
    • get amount()
    • get volumeCredits()
  • class ComedyCalculator extends PerformanceCalculator
    • get amount()
    • get volumeCredits()

Without the full context of the example it might be a bit tough to understand some of these, but if a knowledgeable programmer spent event a few minutes studying the code it’d be fairly easy to understand how the pieces fit together.

Even the way that the parameters passed to the functions are named is quite clever. In the “createStatementData” function it receives “plays” as a parameter, whereas in the “enrichPerformance” function is receives “aPerformance” as a parameter. By the naming alone we know whether we’re receiving a single object or an array of objects.

The examples provided here in the various functions, which handle small bits of the modifying and displaying of data, also brings us to the next point:

Good code is modular

The code to accomplish what needed to be accomplished (print out a bill for services rendered) could have been done in one long function, and it would work. As the first quote in this piece stated, it would be code that a computer could understand.

However, it would be difficult for a human to understand, and one part of why that is has to do with the density of code.

When you have 50 lines of code smashed together, it can be very difficult to keep track of all the individual threads within that code block. You’ll have a dozen variables swimming around which are constantly being changed or displayed in different places.

It can be VERY difficult to keep track of all these threads when you’re trying to read and understand code written by someone else, and in many cases even when YOU YOURSELF wrote it! There have been situations where I’ve gone back and looked at code I wrote on a project a few weeks prior and struggled to understand what I had done and why.

It’s much easier to read a smaller block of code (a single, appropriately named function) that performs one main action than it is to read a giant block that performs a dozen different data mutations and variable assignments.

There’s another large benefit to this modular style, which is that:

Good code is reusable

A small function that does one or two main things can be re-used in different places or later in the project without much issue.

If you have a small section of code in a giant block that you need to reuse in another place, your can either copy/paste it in that other place (which is no bueno, and violates the DRY principle), or you can extract it out to a separate function to be used in whatever place you need.

Part of the process of refactoring is doing exactly that: breaking large chunks of code and giant functions out into smaller, more modular, reusable functions. That said, it’s a good practice to write code like this in the first place as opposed to waiting for a refactoring session to do it.

It’s slower going, but pays dividends in the long run. That ties into the last main point about the purpose of refactoring, which is that:

Good code is easy to change

If your code is easy to understand, modular, and reusable, then it is likely much easier to change. Making a change in one place shouldn’t cause dozens of unexpected downstream effects, because it should be VERY clear (to the human reading it) exactly what each piece of your code does.

Values shouldn’t be mutated if it’s not necessary to return a changed value. Data points should only exist within the context they are used. You don’t need a global variable when it’s only used in one or two specific places.

Broadly speaking, the more easy it is for another programmer to understand your intent with each block of code you’ve written, and the fewer unforseen side effects changes to your code will have, the easier your code will be to change and the better it is.

Final Thoughts

That’s about the gist of the first chapter at this stage. There are a number of smaller examples given about very specific ways to refactor things, but I wanted to keep this post to the broader principles.

One nice thing about the way this book was written is that the bulk of the book is essentially a reference filled with the most common refactorings that a programmer will need to do in their job.

Fowler himself writes that it’s not necessary or reasonable for a programmer to memorize all of them (since there’s so many). It’s better to understand the broader principles and just have a general idea of what’s optimal, and to refer back to the specific examples as necessary. From there, a programmer can learn the refactorings more concretely through repetition.

That’s all for today, til I finish chapter 2!

-Brandon