Zsolt Fabók

Practical ideas on kanban, lean, scrum, xp, java, programming…

The Refactoring only Constraint

in development

Doing code kata is more fun with constraints. Once I heard about a very impressive one, but I was unable to find anything about it, so I decided to investigate and write about it on my own.

The constraint is simple: only refactoring can be done in the production code, in other words: any kind of new functionality shall be tested and implemented in test code and refactored to the production code.

Until now, I was unable to find any reasonable argument that favours this methodology in a production environment, however, doing code kata with this constraint really improves…

  • …knowledge about the refactoring capabilities of the tools being used

  • …the refactoring experience

  • …communication skills - if done in pairs

  • …craftsmanship and patience

My recommended steps for the first try:

  1. Find an easy code kata

  2. Write a small piece of functionality with TDD, but keep it in the test case

  3. Move that small piece of functionality to production code

  4. Write another small piece of functionality with TDD, but still keep the real and the test code in the test case

  5. If necessary, refactor the production code so that it can accept the new functionality

  6. Write an integration test case which tells how the production code shall behave after moving the new piece of code to the production

  7. Move that small piece of functionality to the production code

  8. Refactor the test cases, remove redundancy - for example, the test cases of steps 2 and 4

  9. Refactor the code and keep encapsulation) in mind

  10. Continue with step 4, until everything is implemented

Of course, you can choose different ways, but pay attention to the importance of the integration test case. It makes sure that nothing is broken after the move operations.

While doing the refactoring, try to…

  • …use only the refactoring tools and assistance features of your IDE - this will help you learn the capabilities of your tool(s)

  • …use only documented refactoring techniques (Fowler - Refactoring, Kerievsky - Refactoring to Patterns) - this will help you gain lexical knowledge

  • …avoid adding new [helper] lines to the code, even if you know that you will remove them later - there is a good chance that these lines will remain in the code after all

As an example, I did Roy Osherove’s String Sum exercise, with some small changes:

  • The input is always one line and always valid

  • The separator is ‘,’

I’m using eclipse, and focusing on the most important parts of work, meaning that I’m covering only one angle in the following example: return the summary of an input such as “2,3,5”

The first functionality:

1
2
3
4
5
6
7
8
9
10
11
12
    @Test
    public void shouldSumAnArrayOfIntegers() {
        assertEquals(10, sumNumbers(new int[]{2, 3, 5}));
    }

    int sumNumbers(int[] numbers) {
        int sum = 0;
        for (int number : numbers) {
            sum += number;
        }
        return sum;
    }

Now I’m creating a stringSum private field with the type StringSum, and moving the sumNumbers there with right click on the method -> Refactor… -> Move…

Now comes the parsing:

1
2
3
4
5
6
7
8
9
10
11
12
13
    @Test
    public void shouldConvertStringToIntArray() {
        assertArrayEquals(new int[]{2, 3, 5}, convert("2,3,5"));
    }

    int[] convert(String string) {
        String[] items = string.split(",");
        int[] numbers = new int[items.length];
        for (int i = 0; i < items.length; i++) {
            numbers[i] = Integer.parseInt(items[i]);
        }
        return numbers;
    }

So far everything is green, now comes the integration test case:

1
2
3
4
    @Test
    public void shouldPerformTheSumOnTheInputString() {
        assertEquals(10, stringSum.sum("2,3,5"));
    }

The sum() method shows up, makes the test code red (compilation failure), but with the quick fix (CTRL + 1) on the method, I can create it, and now it’s just the test assertion itself that fails. In order to finish, the convert() method is required, but it is still in the test code, and until I have a green bar, I’m not really allowed to change the code base. I am ignoring the integration test case for a minute to have a green bar, and moving the convert() method to the production code. After having the test case on board again, the bar is red, but using the content assist (CTRL + space) I’m inserting the convert() and sumNumber() calls:

1
2
3
    public int sum(String string) {
      return sumNumbers(convert(string));
    }

Now everything is green, but the code is a bit ugly. The test code does not need the shouldSumAnArrayOfIntegers() and shouldConvertStringToIntArray() test cases, so I’m removing them, and making every method in the StringSum class private except the sum().

After using Refactor -> Inline…, and moving around some lines (ALT + up/down arrow) the StringSum has only one method, which looks like this:

1
2
3
4
5
6
7
8
    public int sum(String string) {
        int sum = 0;
        String[] items = string.split(",");
        for (int i = 0; i < items.length; i++) {
            sum += Integer.parseInt(items[i]);
        }
        return sum;
    }

The example above is very simple. After adding more functionality - like supporting more separators and having proper error handling -, the task became harder. It took me hours to finish it, but on the bright side, I’ve learnt new refactoring techniques.

Comments