Refactoring Best Practice You May Not Know About
So you looked at your code and architecture, and you decide it’s time to refactor it or replace it with a more reliable algorithm or architecture. It’s going to be a big refactoring project, it will definitely involve big changes.
You know these updates must be done — it will make the product faster, more secure, easier to scale in future projects. There is no doubt this is the right way to go.
But one big thing worries you. How are you going to make these changes in a way that does not end up breaking your existing users? You’ve had too many refactoring experiences in the past that you know the likelihood of breakages happening is pretty high.
What lot of developers don’t know is that there is a simple practice that will put refactoring and re-architect worries at ease. One which ensures that whatever changes are made, however many developers participate in the project, it will not break their users. So what is this?!?
Well, you need to know your user use cases, and make sure you have the test automation that validates these before you start refactoring.
This means that before making any big changes, if you have no test automation for the uses cases your users exercise and your product supports, the first thing you need to do is create these test automations. These test automations will be your insurance policy and one that will make you sleep through the night.
Having the right set of test automation means that, as you surgically change underlying code or architecture, you will know right away if you’ve broken any existing use case. You can call this as use-case-driven refactoring.
Note that when refactoring, your unit test automation may not help you much because you are changing the code these tests are validating (you actually would likely need to create new unit tests). In some cases, you may be able to use your low level tests if what you are refactoring is even lower level than what these tests are validating.
So here it is. A simple 3-step plan that will help you on your big refactoring project. You can also use it to justify to your boss that the refactoring project you are proposing to her will not cause your product to blow up or break your users.
Step 1: Have an inventory of existing user use cases
It is important to know what your users are able to do now or are doing with your product. This is your golden reference. These use cases should continue to work after your refactoring project.
Step 2: Ensure you have the test automation that validates these use cases
If you have none or have an incomplete set, create these test automation first. Make sure they are working and passing in the current codebase.
Step 3: Run your tests frequently during the refactoring project.
Put a mechanism or process in place so that your team is constantly running these use case tests as they are making changes with the underlying code and architecture. They should ensure that, at any point, all the tests continue to pass. If any of the tests fail, they need to fix their code (not the tests!).
Can I skip Step 2 and perform manual testing of all the identified use cases instead?
If you are able to perform manual testing of all use cases regularly and are able to come up with the same desired result, i.e., no regressions for your users — yes, you can! But just because you can do it, it does not mean you should. This path will cause you and your team more pain and more wasted effort and time in the long run. I advise against it.
How about non-functional tests? Should I also create those?
Yes! You need to have benchmark metrics of your original codebase as far as performance, security, resilience, scale. Your product should continue to comply with these requirements and SLO/SLAs with the new refactored code or architecture - meaning, you should not see any non-functional regressions.
And maybe, if you made the right technical call on your refactored code or architecture, you might see huge improvements in one or more of these non-functional areas of your product. Win-win!