Refactoring improves your code while keeping the external interface the same.
When writing code, we rarely get it perfect the first time. Most engineers would agree there is room for improvement in their code.
Refactoring code is the act of restructuring software code to improve readability, maintainability, and efficiency without altering the external functionality.
When building code we have a set of requirements that code must meet in order to be functionally correct. Those requirements may be formally documented in a user story or they may be an informal set of requirements in our head. First, we write the code until we are satisfied that it operates correctly, meaning it takes the right inputs, gives the correct output, and does not have any unwanted side effects.
Once we are satisfied that the code works correctly, it makes its way to being used in ‘production’. The meaning of ‘production’ may vary, but for the sake of this article, we mean that the code is in use and there will be people or systems that now rely on the specific behavior of the code as it currently operates.
After the code is in ‘production’, we often notice opportunities to improve the code. These changes may be cosmetic, or they may be functional. Refactoring may be functional as long as the external behavior does not change. Before we charge forward with making improvements we need to consider how we can guarantee that we do not alter external behavior.
Ensure external functionality remains the same.
If your code is covered by unit tests, congratulations, you may proceed with wild abandon because you can run your tests after your changes to ensure your code still performs the expected behavior.
If you don’t use unit tests, or your unit coverage is not complete, it is a good time to reflect on how that might be worth the investment of time so that you can refactor with confidence. The true benefit of unit tests is that it allows us to improve our code, test our improvements, and have confidence that we have not introduced unwanted behavior. After you reflect on that, you may decide to write tests for your existing code or proceed with your refactoring with caution.
Whether you use unit tests or not, you will want to make sure that you keep externally facing interfaces the same.
- Keep the code at the same address or same function name.
- Keep the input parameters the same.
- The output of your code should be exactly the same.
Refactor To Improve readability
When reading the code line by line, it should tell a story from a consistent perspective. It has been said that “Clean code reads like well-written prose.” (Grady Booch author of Object Oriented Analysis and Design with Applications). To make code read as prose requires:
- Naming functions and variables well.
- Keep functions short (I like about 5 lines or less, but this varies per project).
- Keep code in a function at the same level (high level vs. low level).
- Each function should accomplish a single task.
- Don’t allow code to have side effects.
- Use standard naming conventions for the language of your choice.
Refactor To Improve maintainability
Programs tend to become brittle over time. It doesn’t have to be this way and we should work with all our might to avoid allowing that to happen. Any engineer that has worked on brittle code, knows the pain. What seems like a small request to change the existing code ripples into chaos. This is usually the result of tightly coupled code. Work to keep your code loosely coupled. Follow the SOLID design principles.
Refactor To Improve efficiency
Improving efficiency involves making judgment calls. Optimizing for efficiency comes at a cost. It can cost by making the code less readable, and more difficult to maintain, however, it could also improve those, so use careful judgment. We often refactor in order to solve problems with response times. It is difficult to suggest how to improve efficiency in a general way. I have found that following the 80/20 rule has worked well for me. If I feel the code is 80% optimally efficient, it is often more than good enough to satisfy user expectations. Of course, we can improve further after that, but I would not suggest spending a lot of time going further without an indication from your users that they feel it needs to be improved. The time you spend on squeezing out more efficiency might be better spent adding new features or working on something else entirely.
Summary
The goal of refactoring is to improve the code. Refactoring is a balancing act. Every decision we make regarding a change to our existing code will require us to weigh the pros and cons. There is a risk in making changes to existing code. A risk that we may break code while trying to improve it. We must weigh the risk and the cost of the change against the perceived improvement. Having a full suite of unit tests will make it easy to know if your refactoring has caused collateral damage. Making consistent small changes every time you work in your code is usually better than trying to make a large-scale refactoring effort.

Leave a comment