Software Refactoring
Knowing when to refactor code.
Refactoring is the process of changing existing software to be forward thinking and streamlined for future growth. This should be a day-to-day process that every developer should include in their development life cycle.
Always leave things better than what you found them.
The broken window effect states that visible signs of disorder bring about more disorder. Left unchecked, maintenance becomes expensive and bugs are more likely to crop up.
Many assumptions are made when developing software, they can be correct; or, other times these assumptions are stale or absolutely wrong. If left unchecked, can lead to bigger hacky changes if developers aim to simply "add a feature or fix a bug".
Hindsight is 20/20 and once a design becomes clear, its gaps or room for improvements become clear.
Refactoring is a continual process to maintain the integrity of the architecture to stand the test of time.
When to refactor
It's important to think about refactoring as a choice. An engineering team can choose to ship out a change quicker at the expense of tech debt. Or, they can avoid taking on tech debt and refactor the surrounding code to maintain the code quality.
When working on prototypes, POCs or MVPs, refactoring isn't always at the top of the priority. You are trying to validate an idea, first.
If a software application is well-established, then it makes more sense to refactor code to fit the evolving needs of the business.
Why we want to refactor
Let's assume there is a method that calculates the time and user preferences to determine whether to render nightmode or daylight mode. Initially, it is baked into the view layer for the android app. However, now there is a need to support web views as well. At the time, it was good enough; however, it makes more sense to refactor this out into a common namespace as to reduce the code duplication.
The goal is to reduce duplication where code is truly shared. Otherwise, you risk having duplicated code that is painful to maintain.
A novice developer might be tempted to make the fewest possible changes to the program. When encountering stale code no longer aligned with the assumptions of the business, it should be refactored or removed. It might be better to sometimes refactor the program to make it easy to add the feature as opposed to waiting until a future time to do it.
Refactoring Tips
Develop a test suite
A test suite will "lock" in the behavior of existing code. Ensure that a test suite exists for the respective code for refactoring. A solid set of tests with good coverage is key; it is not meant to be busy work, but more of a sanity check for our human tendencies to make mistakes.
I encourage you to read about test driven development for more insight on how to achieve this.
By having an automated test suite, it allows us to refactor code without worrying about breaking the existing functionality.
Decomposing and Redistributing a cognitively complex block of code
A big block of code without some semantical meaning is daunting to understand for anyone who wasn't the original author. We want to decompose long-spanning logical ideas into smaller manageable groups.
While not an exact science, you have to delineate where a scope of work can be abstracted away. Moreover, how to encapsulate modules and replace them with semantic labels that allow the code to read more in high-level terms.
The Extract Method found in modern IDEs is useful to do most of this boilerplate work for you. It will pick variable names for you where possible. We want to communicate the true intent of the code, so we update variables and entities were applicable.
Code that communicates the true intent of code is valueable.
Replacing Conditional Logic with Factories and Adapters
It is not a good idea to introduce a switch based on the attribute of another object for processing. Instead, we should use the factory pattern to create the appropriate object that can handle the logic. You can also use the adapter pattern to adapt the existing code to the new interface. This way, nested conditional logic doesn't pollute coexisting code paths.