The last couple of years I have been busy talking to developers about their practices and how they go about this and that. For a long time, I have found myself in a reoccurring pattern which involved (and honestly – still does) refactoring.
Refactoring as we know it. Finding something ugly that needs to be fixed. Whereas that just might be code I have written three or four months ago and by nature – I just don’t like that anymore, only by looking at it. It might be some technical debt, some code smell, or something that just does not fit my new favourite architecture pattern.
So that got me thinking: Does it ever end? Is refactoring something that will be done for the rest of my life? Or is there something more to it. Because it is unsatisfying knowing: Whatever I will code – I will refactor it. I will have to touch that piece of code again. So what do I do? Do my best, write the best code I have ever written. I will do everything by the book and more, this code will be untouchable, it will be perfect.
This pattern will bring joy for a maximum of two days. First exceptions to my architectural and design rules are coming in. Compromise doesn’t hurt, does it? And here we go again. Refactoring.
But what if we go on wrong about the practice of refactoring? What if it is not that built-in mechanism of cleaning up technical debt? According to Wikipedia, it is about changing the codes internal structure without changing its external functionality. That sounds neat, and I have heard that before.
What if – refactoring – is something that can be built in from the beginning? Something to be done on purpose? Is there something, where this is done on purpose? Those questions lead me to something we tend to ignore.
Because it is ‘not possible’. ‘That doesn’t work everywhere’. That’s something out of TDD. Yep, that old bugger. But what if we went all wrong about that?
Let’s get back to the definition of refactoring. Changing the internal structure without changing external behaviour. External behaviour: This is the thing I want my code to do. The stuff I need to know about upfront. The public API – however that one is structured, from GUI to REST and so on.
And since I know about that up front, I can test it. Yes, exactly – that is my unit under test. My public API. So I write a test for that. What do I do next? I do whatever necessary, so my code works and fulfils my test – making it green.
Now, hopefully, only 20% of the time I planned for my outcome has passed. But what a mess it is. A green test case – a dirty but working public API – and 80% time for something I now know would come up: refactoring.
Putting my code into shape. Making it fit the structure and patterns I want it to while keeping my external behaviour the same. With a green test, I have written at the beginning.
“We never get time for refactoring” – well maybe you go wrong about it. Maybe you still feel that your unit under test is a function in a class.
Try out three simple things:
1. Only write one test for your public API
2. Make that test work the fastest way you can (Yes, 1 file, scripting it down, full-speed)
3. Break up that file, make it fit your patterns – Refactor.
Of course, that does not mean you’ll never have to touch that code. But it is well tested and in the shape you want it to be. And refactoring – even the ‘old’ way – becomes way easier: Because there is a test for that.