Refactoring is stupid ! (part 2)

Thomas Benard
The way of the Craftsman
4 min readApr 15, 2019

The discussion reported here happens the day after the one in my last post.

What have you done ? Now, things that work together are in different places !

Ok, Thomas. I kind of see the interest of the refactoring we talked about yesterday. But some things are still bothering me.

Hello, Jean-Michel ! Sure, what’s the problem ?

If the code is all split up like that, isn’t it going to be harder to understand ? I mean, just compare my solution :

public double computeElectricalResistance() {
ParticularElectricApi particularElectricApi = new ParticularElectricApi();
double tension = particularElectricApi.readTension();
double current = particularElectricApi.readCurrent();
return tension / current;
}

With yours :

public Resistance computeElectricalResistance() {
Tension tension = voltmeter.readTension();
Current current = ammeter.readCurrent();
return Resistance.createUsingOhmLaw(tension, current);
}

All the related code is in the same place. It’s a lot easier to understand what’s actually happening with what I proposed. With your solution, I have to browse 3 different classes and 2 interfaces in order to have a complete view of what’s happening. Such a bother !

Yes, I think I understand what you mean. You prefer your proposal rather than mine because everything is in the same place, right ?

Exactly !

Do you remember what happened to your code when the new feature request came ?

public double computeElectricalResistance() {
ParticularElectricApi particularElectricApi = new ParticularElectricApi();
double tension = particularElectricApi.readTension();
double current;
if (useTeslaWasBetterThanEdison) {
TeslaWasBetterThanEdison teslaWasBetterThanEdison = /* instanciate API here */
current = teslaWasBetterThanEdison.current()
} else {
current = particularElectricApi.readCurrent();
}
return tension / current;
}

Everything is still in the same place. And yet, you don’t like it, right ?

Right. In this case, I agree with your solution with the interfaces becomes better. But to be honest, it feels like all this added complexity in the initial case is just not worth it because we sacrifice readability…

Remember, the purpose of code is to be changed. Therefore, good code is easy to change.

Readability matters because a readable code tends to be easier to change than code that is hard to understand. But, if making the code more readable also makes it harder to change, that defeats the original purpose of why you wanted to make that code easier to understand in the first place.

Right, I get that. But, on the practical side, wouldn’t it be easier to leave it like I did and then refactor it if needed later ?

Sometimes, it may be a good idea. Sometimes, it may not.

Often, there are plenty of things that could change in the code in the future. Making the code supple enough to handle all the possible cases would render it unreadable and therefore unchangeable.

Checkmate !

Ah Ah ! Checkmate ! See ? Even you ended up saying always doing this refactoring would harm maintanability !

On the contrary, my friend !

What I mean is that making the code as easy to change as possible is what actually matters, because it is what the business actually wants. These guys love changing their mind ! It gives them a huge advantage on the market.

In that regard, readability and flexibility both matter, but we must be careful not to harm one in the pursuit of the other.

Uh… This conversation is becoming way to theoretical for me, let’s go back to the initial problem if you don’t mind.

How do you know when to introduce these interfaces, then ?

That’s an excellent question ! I think it takes time and experience. It’s one of those things you learn by doing, in my opinion. That’s why doing those code katas is so important: it provides us a place where we can safely try new things.

But still, as a guidance, there are two questions I like to ask myself:

  1. Is the code testable ? If not, can introducing an interface make it testable ? If so, go ahead and introduce that interface. In our case, the hard-coded dependency to ParticularElectricApi makes the code untestable without linking with the associated library.
  2. How likely is a change to happen here ? And how hard would it be to introduce that interface while refactoring later if needed ? If it would be easy to add and a change is unlikely to happen, don’t bother. On the contrary, if it would hard and a change is very likely, you should probably introduce it right now.

By testable, do you mean unit testable ?

Exactly !

For example, let’s imagine the function computeElectricalResistance is part of the Foo class:

@Test
public void resistance_is_tension_divided_by_current() {
Foo foo = new Foo(
new VoltmeterThatReturns(Tension.createInVolts(1.0)),
new AmmeterThatReturns(Current.createInMilliAmpere(1.0)));
Resistance actualResistance = foo.computeElectricalResistance();
assertEquals(Resistance.createInKiloOhm(1.0), actualResistance);
}

Now I can test my code behaves as expected given any possible input defined by the interfaces I chose and run those tests every time I want, which is probably once every few minutes because unit tests are VERY fast. Isn’t that amazing ?

Oh right ! I see it ! Because we have desined an interface that describes what its implementations can do, I can test what happens with this code for every possible input !

That’s it ! We can write a test to see what happens if the Ammeter return 0, or null or even if it throws an exception.

We couldn’t do that with the original solution because the code was coupled with a piece of hardware, and so, untestable.

Even if the code ended up “split up in different places”, our trust in it increased tremendously !

YaY !

--

--