Legacy Coderetreat: Part 10 – Extract pure functions

Extract Pure Functions

Blog post series

This blog post is part of a series about legacy coderetreat and legacy code techniques you can apply during your work. Please click to see more sessions about legacy code.

Purpose

We have some class that is very big. We want to divide the existing code into more methods and classes so that we have in the end some code that is easier to understand. If the methods are small, they are easy to change. In the same time the code would respect the Single Responsibility Principle. We optimize our software for changeability. In the following we will see how we can extract pure functions to achieve that.

Pure functions

Pure functions

Concept

The function always evaluates the same result value given the same argument value(s).
Evaluation of the result does not cause any semantically observable side effect or output, such as mutation of mutable objects or output to I/O devices.
http://en.wikipedia.org/wiki/Pure_function

Whenever the code is complicated we need to simplify it. Complicated code means long methods, long classes, classes and methods bearing more than one responsibility. When we need to work with existing code, we often cannot understand what the code does. So we will want to split the big methods and classes into smaller ones that we can understand.

By using this technique we will be able to have small methods. Having small methods means that we will have a clearer image on what we need to test. In this way we can start writing unit tests on the existing code. All we have done until now was just adding black box system tests to create a safety net. Now it’s the moment to break the system into more smaller cohesive parts that we will test in isolation. But now let’s just see how we can break the system into smaller parts.

I am looking at the codebase for trivia, on GitHub. I will not put all the code here, because it’s quite long. Please have a look at it by following the link.

These are the steps for extracting a pure function:

Step 1: Search for some code where there are clearly more responsibilities.

I want to find a good spot to change the code. Let me pick the method add(String playerName).

This code clearly has multiple responsibilities:

  • adds the new player to a list
  • resets the places
  • resets the purses
  • resets the penalty box
  • writes an output that the player was added
  • writes an output what is the number of the newly added player
  • returns a hardcoded value for success of the operation.

Step 2: Extract that code to a new method

I extracted a very small method that knows how to write the player number to the output. This method is simple enough. I clearly call the static dependency System.out.println, but it being static does not make my function impure.

Step 3: Make the method static

In the moment I make the method writePlayerName() static, there is an error: the variable players at line 13 is an instance of the class, so clearly we cannot use it like that.

Step 4: If you have errors when you make the method static, then stop using class instances

I have an error, because my static method writePlayerNumber() is using a class instance. What I need to do is to refactor the code so that the method writePlayerNumber() receives the information it needs, without needing to call an instance variable.

We just extracted the players.size() to another variable.

Then we passed the variable as a parameter. Now instead of using the player.size() call, we just receive an integer with the value of the player number.

Step 5: Make method static again

Step 6: You have a static function. Refactor the code

Now we have a private static function. But it is not pure, because it causes output to an I/O system as a side effect. In order to make it pure, we should just return the value of the text.

The function also has two responsibilities: it writes to the console and creates the message. We must decide if this is appropriate for our usage. Sometimes we could allow functions like this not respecting Single Responsibility Principle, because they are small and easy to understand. Sometimes we might need to extract the creation of the message to some other function because for example we need to internationalize our system.

But now let’s make the method pure.

The first thing to do is to acknowledge, by renaming the method accordingly, that the method has two responsibilities. This helps us understand which are the two methods we could extract.

Then we extract the creation of the message to another method.

Now we have a method that is pure:

It is very easy to understand and extremely easy to test.

We need to refactor the system a bit now.

First we add the message as a parameter

Then we rename the methods accordingly to their new behaviour.

Step 7: Test the production code still works

 

Outcomes

Now we have a pure function. This is the usual process of extracting a pure function. Sometimes we can find simpler code, that can be easily transformed into a pure function. But sometimes we need to refactor a lot to reach the moment when we can extract a pure function.

We started with cluttered code that is impossible to test in isolation and ended up with a simple function that is easy to understand and easy to test.

Remarks

Careful, this method considers we already have some system tests written and we run them constantly to check if we introduced any mistake. We must also test the system manually from time to time.

As an heuristic I always start with the code that is the deepest in a method. I try to extract a pure function and in this way minimizing, step by step, the depth of the code. In this way I also minimize the cyclomatic complexity.  Usually lower cyclomatic complexity means having a code that is easier to understand. It also means that it is more difficult to introduce defects.

The next step is to start writing unit tests on the pure method. We can also extract it to another class, that deals with creation of messages. We will do all these things in following posts.

This kind of call

is consistent with how one would use a functional programming language. Pure functions is the way to write code in most functional programming languages. We could keep the code like this, or we could transform it to OOP, but with clear one-responsibility classes. We will see how to do this as well during the following posts.

History

I first heard about this technique from JB Rainsberger during my first Legacy Coderetreat. I knew about pure functions, but only then I understood their utility when dealing with legacy code.

Code Cast

Please find here a code cast in Java about this session

Subscribe

If you want to receive an email when I write a new article, subscribe here:

Subscribe for new articles

Leave a Reply

Your email address will not be published. Required fields are marked *

Post Navigation