Refactoring is one of the best practices of Extreme programming. Even the most stable project requires occasional modification. The idea is to clean up duplication and smelly code. The principle of software entropy suggests that program starts off in a well-designed state, but as new bits of functionality are tacked on, programs gradually lose their structure.
We write a small program that does a specific job well. Later we add new functionality on top of existing program, often in a way that the existing program was not intended to support. In such cases, we can either do a complete redesign or patch some work around. Redesign and rewriting existing code results in extra work, which in most cases we can’t afford. Also, we may miss existing functionality or introduce new bugs.
The principle usually followed is: “If it ain’t broke, don’t fix it!”. However, if a code is made complex due to poor design choices and unnecessary constructs then it starts emanating code smell.
Refactoring is somewhere in the middle of redesigning the complete code and leaving the unmanageable and stinking code as it is. Refer to this presentation by Rick Hickey, the author of Clojure, about how to make incorporate simplicity in the code. Essentially simple code benefits from ease of understanding, ease of change, ease of debugging, flexibility. The smelly code can be simplified using Functions (lambda), Polymorphism (Interface and Abstraction), Declarative behavior (annotations as abundantly found in Spring framework), Coding templates and generators for repetitive cookie-cutter code, and proper encapsulation to reduce the exposed surface area of classes/packages.
The major factor, which triggers the need for Refactoring, is the request for a new feature. Refactoring also helps in keeping the code clean for any newcomer to understand.
In most projects code refactoring becomes an end of year task when there aren't many developers in office and no one is shouting at you while renaming classes and methods. However, refactoring should be a common recurrence of any agile practice. Just like in order to keep your house tidy, you need to clean it every week, in order to keep your codebase maintainable, you need to keep it clean every month or so. If you are buried in project deadlines then developers usually give excuses to put a more dirty code in already what could be a ball of mud. In fact, a project which is undergoing frequent enhancements require continuous refactoring more often.
Guidelines of Refactoring:
- General programming concepts such as SOLID, GRASP (General Responsibility Assignment Software Principles), TDD (Test Driven Development), DRY (Don't repeat yourself) and Design Patterns also serves as the foundation for any refactoring activity.
- The end result of refactoring should strive towards loose coupling and high cohesion. It should at the least leave the code in a better shape than it was before. If refactoring is breaking things left and right, then you have other groundwork to do before you take on refactoring. Such groundwork would involve getting understanding of existing code, add unit and integrations test to test all critical paths and understand the business logic in and out so you can validate that those use cases still works after refactoring.
- Refactoring should improve readability, make internal hacks and edge cases more obvious, increase the confidence of the team to make future enhancements and overall improve the design.
- Do not refactor just for namesake. It must be driven by feature request or certain guidelines such as the maximum number of lines proposed for any method or class.
- Do not mix refactoring and enhancements. Impose a clear separation with intermediate QA cycles in between.
- Take short deliberate steps. Move fields or methods from one class to another. Fuse two similar methods to a common method or to the superclass or interface (Java 8) or package (Java 10). Test application after each step.
- Make sure you have good tests in place. Ideally refactoring should be backed by automated tests. The cost of refactoring is more affordable at the early iterations of QA.
- Continous integration with a good amount of unit as well as integration test will provide more confidence to the developers to make changes to any one's code.
- IDE plugins such as JetBrains ReShaper, CodeRush and JustCode helps developers by flagging bad coding as warnings and suggest quick fixes. In eclipse find bugs is a useful tool although it is could show many false positives. For coding standards, always prefer IDE formatting rules and check styles to avoid manual corrections.
- Developers have personal preference and opinions as what is a clean code or what constitutes a good practice. You should avoid such conflicts within the team and stick with well-published guidelines (design patterns or language coding guidelines). If you need to tweak these guidelines then those guidelines should be published to the team as ground rules especially things like space vs tabs, method/class naming scheme and position of curly braces.