If I had to pick one thing that sets XP apart from other approaches, it would be refactoring, the ongoing redesign of software to improve its responsiveness to change. – Jim Highsmith
The easiest technical debt to address is that which you didn’t incur to begin with. – Scott W. Ambler
Refactoring definition – What, How and Why
There is a great work related to Refactoring, starting with the book “Refactoring: Improving the Design of Existing Code” of Martin Fowler and its collaborators (Kent Beck, John Brant, William Opdyke, Don Roberts) and continuing with the XP – Extreme Programming ecosystem of practices, that include Refactoring as the fundamental Agile practice.
The definition from Martin Fowler, at www.refactoring.com:
<<In the Refactoring Book, I made the following definition of “Refactoring”
noun: a change made to the internal structure of software to make it easier to understand and cheaper to modify without changing its observable behavior
verb: to restructure software by applying a series of refactorings without changing its observable behavior
Refactoring isn’t another word for cleaning up code – it specifically defines one technique for improving the health of a code-base. I use “restructuring” as a more general term for reorganizing code that may incorporate other techniques.>>
- The objective could be generalized: Improving the design of existing code, without changing the observable functionality, must be performed for improving the health and economics of the software, where “easier to understand and cheaper to modify” are only some of the objectives (others: less defects, easier to detect the defects)
- Clean is just one “technique“… Which are the others?
- The extended definition contains also this part: “Its heart is a series of small behavior preserving transformations. Each transformation (called a “refactoring”) does little, but a sequence of transformations can produce a significant restructuring. Since each refactoring is small, it’s less likely to go wrong. The system is kept fully working after each small refactoring, reducing the chances that a system can get seriously broken during the restructuring.”- Martin Fowler, at refactoring.com.
Improving the design of existing code, without changing the observable functionality, must be performed for improving the health and economics of the software.
Refactoring versus waste – Clean versus Adapt
Refactoring offers a process solution for design flexibility. A possible approach for getting a flexible solution could require some up-front design based on imagined future requirements. In many cases, these requirements never become real and that it is a waste. Martin Fowler says: “Refactoring can lead to simpler designs without sacrificing flexibility. This makes the design process easier and less stressful. […] Once you have a broad sense of things that refactor easily, you don’t even think of the flexible solutions. You have the confidence to refactor if the time comes.”
In this case, the better design will come later, when the new context will be revealed.
Let’s think about some other kind of refactoring types, the ones that fix the majority of the design smells such: function and classes’ sizes, multiple responsibilities, magic numbers, duplicated code, meaningful names, comments and others. In this case, when these rules are applied or corrected later, the waste is bigger and even in exponential manner.
That seems contradictory: sometime it is better to refactor later and sometime is better to refactor sooner. Well, it is not! “Flexibility” and “Design smells” are two different problems.
There are two kinds of design decisions involved here:
- Context independent – the ones that makes the code clean
- Context dependent – the ones that are adapted to the current context
Clean and Adapt are two different things and each one must be managed accordingly.
The sequence of decisions related to the design could be like this:
- First development: Simple and clean first code – apply Simple Design and Clean Code. Use some “in development” refactoring, but try to write clean from the first time.
- Next development: Adapt the first simple and clean design to the new context and perform also necessary cleaning.
The statement “You build the simplest thing that can possibly work.” should be reformulated as “You build the simplest clean thing that can possibly work”.
Sources of waste: any wild guess about future contexts, but also any mess remaining from previous development.
Reformulating the process
We have two kind of activities: Clean and Adapt and more practices that could realize their objectives:
- Clean Code – write the code clean from the first time
- Refactor – improve the design, while preserving observable behavior; executed in small transformations, while keeping the system stable
- Re–design – similar with refactoring, but not necessary in small steps (with increased risks)
Clean Code & Refactoring
Both practices are introducing a set of design rules, specified in two books, where “Clean Code” it is creation of Robert C. Martin and “Refactoring: Improving the Design of Existing Code” of Martin Fowler and its collaborators (Kent Beck, John Brant, William Opdyke, Don Roberts). Logically it is a single set of rules that:
- Keep the code clean
- Are rather context independent
Keeping the code clean – practices and approach
In most of the cases, the design decisions related to a clean code are context (requirements) independent. In order to eliminate waste, the code could be kept clean as follows, using these practices:
- Clean Code: write the code as clean it is possible from the first time
- Refactor “in development”: earliest refactoring, during the development is the cheapest
- Refactor “legacy”: paying the debt of dirty code as early is possible is the cheapest variant, when the debt already exist
- Re-design “legacy”: only in cases where Refactoring (small steps) it is not possible, because the risks are increased in this case and the testing it is more difficult.
Simple Design it is considered as default complementary practice for all these cases.
Sources of waste:
- Writing first code dirty is the most massive source of Technical Debt and refactoring it is not efficient/effective
- Technical Debt reduce productivity, quality, agility and increase system rigidity and fragility
- The cost of paying the Technical Debt could increase exponentially when it is accumulated in high amounts of dirty code
Adapt Design rules – practices and approach
These kind of decisions – adapt the design – are context dependent. In order to eliminate waste, the best time to take such decisions is to defer them until the context it is revealed. A possible approach could be:
- Respect the above clean code rules
- Simple (Design) and Clean (Code) – “build the simplest clean thing that can possibly work” for the current context/requirements
- Adapt “legacy” design in new context – adapt the previous simple design to the new context /requirements, when this new context it is revealed (Simple Design it is applied again):
- By Refactoring
- By Re-design ( see comments for re-design from Clean Code rules)
Important: Refactor/Re-design to adapt the previous design must be done when this previous design does not match exactly to the new context. There are some practices that for Adaptive Design that increase the chance for an emergent design with less work related to adaptation – See “Roadmap to an Agile Design.”
Simple Design Formula: Simple Design (context 1) + Adapt & Simple Design (context 2) + …
Sources of waste:
- Premature design decisions based on wild guesses about future requirements, as opposite to Simple Design
- Skipping the adapt design decisions needed in the new context
- Trying to evolve the design only with these adapting tactical steps without any strategic approach for emergent design as Functional Cohesion and Separation of Concerns from Clean Architecture
Effective/Efficient “Clean”: clean and simple design first and then refactor.
Effective/Efficient “Adapt”: clean and simple architecture (& design) first and then adapt.
Process level upgrades – DAD style
The above logic could be re-formulated, refactored (sic!) again in terms of:
- Process goals: keep the code clean, adapt the design (previous simple design in new context)
- Goal options: An alternative for Refactoring is Re-design, where Refactoring is the default option and Redesign (with increased risks) should be considered only when the Refactoring is not possible
- Note: Strategic options are complementary to tactical options and not alternatives
This process level logic it is used by DAD – Disciplined Agile Delivery, and finally help to fill the process gaps from other methods or a custom process and offer a much better support for process guidance.
For the Adapt goal , Refactoring is the core tactical technique, but in fact there are many practices that contribute to this goal:
- Examples of tactical practices for Adapt goal: Refactoring, Test first, Re-design, Model Storming, Look Ahead Modeling.
- Examples of strategical practices for Adapt goal: Clean Architecture, Continuous Integration.
More: a generic goal it is Design Envisioning & Change, where the two main aspects are what is new and what is adapted. For an Agile context, based on Simple Design, the Adapt aspect is fundamental and has a “big share” in the process.
There is not only Simple Design, there are “twins”: Simple Design + Adapt