The simplest possible solution

Filed under: Agile, Clean code, Java, Software craftsmanship, TDD, — Tags: Simplest possible solution — Thomas Sundberg — 2011-11-16

The simplest possible solution that could work. Ever heard that expression? What does it mean? Really?

The answer is obviously something that in a really simple way satisfies the test you currently are working on. Nothing more, nothing less.

Let me walk you through an example so I can show you what I mean. This is the beginning of my world domination computer system. Some people may call it a simple calculator. I will test drive it and build in small increments.

Test driving it means that I will write the tests before I write any production code. I never add any production code if I don't have a test that tells me that I have to add it. I will never do any refactoring when I have failing tests. Refactoring can only be done when all tests passes.

The process can be described as

It is sometimes called red-green-refactor. The expected cycle time is on the minute scale and normally below 10. This is a fast cycle. You only take small steps.

Lets start with the first test. Add 1 and 1 and expect the result 2.

@Test
public void addOneAndOne() {
    Calculator calculator = new Calculator();

    int expected = 2;
    int actual = calculator.add(1, 1);

    assertThat(actual, is(expected));
}

The simplest possible solution is in my mind this:

public int add(int a, int b) {
    return 2;
}

I hard code the expected answer. I once did this at a code retreat and my programming partner said: "That's cheating!" and was almost upset. I think hard coding the first value is ok if you do something about it as soon as possible. As soon as possible is when the next test fails and I know what to fix, normally within minutes. I actually claim that this is the preferred way to grove a system. I don't want to build more production code then I have to, if there isn't any reason for making a solution more general. I want to defer a general solution as long as possible and implement it only when there is a real need for it.

It can be argued that this solution isn't safe for the future. It may even be considered to be incorrect. But if all we know is that we have the requirement to solve the simple add above, add 1 and 1, then I claim that this is a correct solution.

Adding another test may show us that we are incorrect. Let's add 2 and 3 and expect the result 5.

@Test
public void addTwoAndThree() {
    Calculator calculator = new Calculator();

    int expected = 5;
    int actual = calculator.add(2, 3);

    assertThat(actual, is(expected));
}

It turns out, hardly surprising, that the test fails.

Ok, lets apply the simplest possible to this problem so we get a passing test.

public int add(int a, int b) {
    if (a == 1) {
        return 2;
    } else {
        return 5;
    }
}

This solution solves the problem. It may not be the smartest solution, but it does the job. We could of course continue with this approach until we have implemented all possible additions. Following that path may, however, not be very wise. It may even be the dumbest thing we could do.

The tests passes, we have green bar. Now we can apply some refactoring if we think there is anything we could do in a better way. An obvious refactoring is of course this one:

public int add(int a, int b) {
    return a + b;
}

All tests passes and we have done some refactoring that may have done this solution generic and future safe. We can stop here.

Why didn't I just apply the last solution from the beginning? The answer if of course that I wanted to show you how I think when I build something. I always try to use really small, safe steps. Many small, safe steps later I usually have a system that works correctly.

It is always easier to solve a small problem compared to solve a large problem. It is also always better to be safe then sorry. So small and safe is my motto.

Generally applicable?

Can this always be applied? Or does it exist situations where small steps are impossible? I have been a developer for more then twenty years by now. I have still not found a system where small steps are impossible. Small steps are often hard since you have to divide a large problem to smaller parts. But there is a big difference between hard and impossible.

I have seen many systems where developers regularly take very large steps. They often claim that their system is so special that small steps are not possible to apply. They also tend to claim that small steps are to slow. When they take large steps, they often struggle to get whatever they are working even to compile. I have seen examples where it has taken days to get things to compile. These developers are very seldom using test driven development

My conclusion is that small and safe steps are always possible. I also think that many developers are either

Some of the issues above can be addressed by showing them good examples. Laziness is hard to deal with, force is seldom a good solution. Stress is sometimes a matter of personality, personality is also hard to address. Stress can also be addressed by management by understanding that it takes time to do the job properly. Skill is something you can gain by working on it. Leaving your comfort zone will most likely give you skill if you give it a fair chance.

Inspiration

Inspiration to this blog came from a conversation with a colleague at an airport in Ukraine where he looked a bit skeptic when I claimed that it is always ok to hard code a solution. Given that you don't leave it there if you have reasons to suspect that there are tests that will reveal that this hardcoded solution isn't good enough.

Thank you Ari for the inspiration!

Acknowledgements

This post has been reviewed by some people who I wish to thank for their help

Thank you very much for your feedback!

Resources



(less...)

Pages

About
Events
Why

Categories

Agile
Automation
BDD
Clean code
Continuous delivery
Continuous deployment
Continuous integration
Cucumber
Culture
Design
DevOps
Executable specification
Git
Gradle
Guice
J2EE
JUnit
Java
Javascript
Kubernetes
Linux
Load testing
Maven
Mockito
New developers
Pair programming
PicoContainer
Presentation
Programming
Public speaking
Quality
React
Recruiting
Requirements
Scala
Selenium
Software craftsmanship
Software development
Spring
TDD
Teaching
Technical debt
Test automation
Tools
Web
Windows
eXtreme Programming

Authors

Thomas Sundberg
Adrian Bolboaca

Archives

Meta

rss RSS