Software fails when its complexity increases and can no longer provide the feature it is intended to deliver without being error free. Refactoring is changing the internal structure of the code without a change in the behavior that it exudes. Refactoring improves the design of the code so that it is easier to understand and work on. Refactoring also makes it quicker and easier to upgrade the code down the road to extend features initially not included.
Computer programs are both complex and fragile. We humans are error prone. It’s not about getting to the solution faster, it’s about getting there safer. The damage of solving an expensive error is costlier than taking smaller steps to refactor the code of the system. Refactoring reduces the risk of introducing defects in the short as well as long run. It eliminates the need to debug the code in short run and avoid defects caused by failure in the long run. Refactoring is an ongoing activity that runs parallel to coding that although, consumes significant time, but is effective in saving a lot of time and investment if a need to rewrite or add features emerge in the future. Although a non-refactored code might work just as fine, refactoring is a present investment that bears fruit in the future for anyone that might have to read or change the code.
The less the merrier.
By refactoring a piece of code, we remove duplication and excess code to make it expressive and highly understandable, promoting clean code culture. Writing readable code now, also shaves a chunk of time later while adding features or fixing certain bugs to the code is required.
What are bad smells?
Bad smells or bad designs are common design problems or language design problems. They include unclear, complicated or duplicated code. Some common bad code designs are:
Duplicate code: Duplicated code, be it exact duplication or being identical in logic of what the code does, should be avoided as much as possible. In case of multiple solutions at different places for fundamentally the same problem, the best should be chosen.
Comments: The code and the methods should be self descriptive. eg giving a good name for methods or classes.
Dead Code: Variables, classes, methods, parameters etc that have no contribution to the program anymore, should be thrown away.
Long Methods: Shorter methods that end at average in 5 lines, performing only one core functionality, with a self descriptive name are easier to understand and highly desired. Long methods are frowned upon and considered bad practice. Shorter methods also encourage re-usability.
Complex conditional statements: Complex conditional statements, be it complex in logic or nested conditions, should be avoided as it limits the readability of the software.
Lazy Classes: Classes that don’t do much should be moved to other classes.
Divergent Change: It occurs when we have to make changes to many methods when we want to introduce a change to a class.
Large Classes: Classes that do too much or have way too many responsibilities are unappealing.
Switch Statements: Complex switch operators or sequence of if statements break single responsibility and open/close principles. If absolutely necessary, they should only be used while operating on structures that do not have to be modified for extendability.
Indecent Exposure: Methods or classes should be made private, whenever possible. Failure to do so results in said bad smell.
Temporary Fields: Fields that remain used only in certain scenarios and remain unused the rest of the time of program execution should be shunned.
Refactoring must be done in micro steps or micro transformations so as to keep the behavior of the code intact and so that it always is in a state to be successfully executed and fulfills the same functionality that the non refactored code provided. We should spend as much time on refactoring as we do on adding a new feature.