Refactor Code

Refactor Code

Refactoring is a controlled technique for improving the design of an existing code base over time.

We all want to write perfect code, but given the challenges in software development, we often end-up with less-than-perfect solutions.

  • Rushed features: Sometimes we don’t have enough time, so we cut corners, complete things “on-time”.
  • Lack of tests: We might think that the code is ready, but we haven’t tested it adequately.
  • Lack of communication: Perhaps we misunderstood requirements, or how a feature would integrate into the larger product.
  • Poor design: Possibly our design is rigid and makes adding or modifying features difficult.

These are all actions that may cost us time later. We may need to stop and redesign a rushed feature, or we may need to fix bugs later.

We refer to this as technical debt — the deferred cost of doing something poorly.

What is refactoring?

Martin Fowler (2000) also introduced the notion of refactoring: systematically transforming your working code base into a cleaner, improved version of itself that is easier to read, maintain and extend.

Refactoring is a controlled technique for improving the design of an existing code base. Its essence is applying a series of small behavior-preserving transformations, each of which “too small to be worth doing”. However the cumulative effect of each of these transformations is quite significant. – Martin Fowler, 2018.

Refactoring suggests that code must be continually improved as we work with it. We need to be in a process of perpetual, small improvements.

The goal of refactoring is to reduce technical debt by making small continual improvements to our code. It doesn’t reduce the likelihood of technical debt, but it amortizes that debt over many small improvements.

Refactoring your code means doing things like:

  • Cleaning up class interfaces and relationships.
  • Fixing issues with class cohesion.
  • Reducing or removing unnecessary dependencies.
  • Simplifying code to reduce unnecessary complexity.
  • Making code more understandable and readable.
  • Adding more exhaustive tests.

In other words, refactoring involves code improvement not related to adding functionality.

TDD and Refactoring work together. You continually refactor as you expand your code, and you rely on the tests to guarantee that you aren’t making any breaking changes to your code.

TDD and Refactoring complement each other.

TDD gives you the confidence to refactor. Tests should fail if you introduce errors into code.

When to refactor code?

The Rule of Three tells you when it’s time to refactor.

  1. When you’re doing something for the first time, just get it done.
  2. When you’re doing something similar for the second time, do the same thing again.
  3. When you’re doing something for the third time, start refactoring.

This is a little bit tongue-in-cheek of course, but it points out the balance between the desire to “get something done with reasonable effort” and the desire to “find the perfect design”. Improving code is a good idea, but you also need to be pragmatic and actually deliver something!

When adding a feature

Refactor existing code before you add a new feature, since it’s much easier to make changes to clean code. Also, you will improve it not only for yourself but also for those who use it after you.

When fixing a bug

If you find or suspect a bug, refactoring to simplify the existing code can often reveal logic errors.

During a code review

The code review may be the last chance to tidy up the code before it becomes available to the public.

Refactoring actions

Fowler lists a series of refactoring actions in his original book. He has also made them available at: https://refactoring.com/catalog/

IntelliJ IDEA makes refactoring easy by providing automated ways of safely transforming your code. These refactorings often involve operations that would be tricky to do by-hand but easy for a tool to perform for you (e.g. renaming a method that is called from multiple locations).

To invoke refactorings, select an item in your source code (e.g. variable or function name) and press Ctrl-T to invoke the refactoring menu. You can also access this from the application menu.

JetBrains also offers a free online course on advanced code refactorings with IntelliJ IDEA.

There is a complete list of refactorings in the IntelliJ IDEA documentation. Refactorings include:

Refactoring Purpose
Rename Change an identifier to something that is more meaningful or memorable.
Move Move classes or functions to different packages; move methods between classes.
Extract method Take a code fragment that can be grouped, move it into a separated method, and replace the old code with a call to the method
Extract field Extract an expression into a variable, and insert the expression dynamically.
Safe delete Check for usage of a symbol before you are allowed to delete it.
Change signature Change the method name, add, remove, reorder, and rename parameters and exceptions.
Type migration Change a member type (e.g. from integer to string), method return types, local variables, parameters etc. across the entire project.
Replace constructor with factory Modify class the become a singleton (returns a single instance).

Last Word

XKCD: Code Quality