In this article, we won't chat about specific refactorings and clean code, but we will talk about the act of refactoring itself.
Writing clean code is an entirely separate article itself. I do plan on writing it at some point, it will be a big one.
I already wrote an article about refactoring and its importance, but it wasn't that practical: Lessons and takeaways on Refactoring.
I expect you to have read this article already, it will help you when reading this: Qualities of good software.
To refactor means to improve the existing code without changing the behavior of the software. If the software's behavior changes, that would mean tests change, and that's not refactoring.
A refactor is definitely an improvement, but it doesn't change the behavior of the software, meaning when you use the software, things still work as they did before.
A practice is something that is done daily. Something that is embedded at the core of the way we build software.
Refactoring should be one of the practices of every team.
Result of refactoring
When I think of refactoring, it is simply just improving the code without changing the behavior. To me we can achieve numerous things by doing so:
- Decouple the software.
- Make code express itself clearer through domain language.
- Get the architecture to more reflect the domain (screaming architecture).
- Keeping things DRY with the right abstractions.
The main results of the above points are reducing the cognitive effort to understand the code and making things easier to change.
This leads to increased productivity of the entire team.
When to refactor
You can refactor the code as you implement the feature. Refactorings shouldn't be separate tasks unless they are big changes that would have an effect on your team's work. They should be embedded in our blood, the default way we write software.
Whenever you are reading code and it takes time to understand the code, you're struggling and feel confused, that is a big sign you should refactor the code.
A mistake many do is once they have understood the code, they go on and implement the feature, despite knowing very well the code they just read took some cognitive effort to understand. This also happens because many software engineers think complex code is cool and may think it is their ability to understand the code that's lacking when it is the opposite way, it is the code!
Refactoring the code before implementing the feature is great, especially when it makes the implementation of the feature easier.
If the refactoring contains a number of changes, it is better to open a separate PR for it. Keeping your PRs scoped and adhering to the single-responsibility principle. This also makes it easier for the reviewers of the code (code reviews are inevitable in our industry), although I'm not a fan of async code reviews.
Refactor every single day.
When not to refactor
There are two things that come to mind when you may not want to refactor:
If it is an urgent feature or fix, you may want to implement it, get it merged and shipped as soon as possible, and deal with the refactoring later.
If the refactoring has no or very little impact, but the effort to implement it is quite high and we have a bunch of other tasks that are far more valuable to work on, then don't do the refactoring.
Refactoring is an extremely important practice. It should be the default way we write software and not something that happens once in a while or occasionally. If we don't refactor, eventually as our software grows, the development will become slow and painful.