You just have to have the guidance to lead you in the direction until you can do it yourself.
It is the neglect of timely repair that makes rebuilding necessary.
The goal of software development is the continuous delivery of business value to users and stakeholders. Constantly changing technology, coupled with the evolution of business objectives and changing user paradigms, makes it difficult to maintain and constantly increase business value. Two paths to the future exist:
- Keep adding new functionality to an existing code base toward an eventually unmaintainable “throw-away” state
- Continuously modify the system to provide a foundation for efficiently delivering not just the current business value but future business value as well
The second choice is better. Refactoring—the activity of improving the internal structure or operation of a code base without changing the external behavior—is a means to this end. With continuous refactoring, the useful life of an Enterprise’s investment in software assets can be extended as long as possible, and users can continue to experience a flow of value for years to come. Refactors are a special type of Enabler story in SAFe and, like any other Story, they must be estimable, demonstrable, and evaluable, as well as accepted by the Product Owner.
Figure 1 illustrates the essence of Refactoring, which is the modification of any software entity—a module, method, or program—to improve its structure or viability, without changing the external functionality.
For example, refactors may accomplish such things as increases in processing speed, sourcing different internal data, or improving security concerns. Another type of refactoring involves streamlining some aspect of the code to make it more efficient, more maintainable, or more readable.
Refactoring mandates that each change is tested immediately to verify the accomplishment of the desired goal. A refactor may be broken into a series of sequential micro-refactors in order to accomplish a larger goal; each small refactor must be tested to ensure correctness. This iterative process preserves the integrity of the software at any stage.
SAFe emphasizes the importance of keeping all work visible, including refactoring. Like user value work, it must be planned for, estimated, and prioritized at all levels of the Solution.
Sources of Refactors
Refactors arise from various sources, as illustrated in Figure 2.
A refactor can be instigated by a business Feature or can be a part of larger refactoring initiative required by some new architectural Enabler. New user Stories may also require some refactoring of code. Accumulated technical debt may drive the team to refactor certain components. Some refactors may be necessitated by new Nonfunctional Requirements.
It is important to emphasize that not all the team’s refactoring effort comes in the form of specific story refactoring. Much of it should be “in-line” clean-up work, done while implementing user stories. Such work should be factored into the estimate of the corresponding Story (see continuous refactoring techniques described in , , ). Specific refactors, however, represent larger pieces of redesign that need to be planned and tracked as separate backlog items.
It is important to understand what value will be achieved once the refactor is completed. Teams may wish to use the “ . . . so that . . . ” portion of the user story voice form to foster shared understanding of purpose and value, as Figure 3 illustrates:
As with user stories, splitting refactors is important, as it helps sustain better development flow. Table 1 provides some useful methods for splitting refactors, and examples for each.
|1. By User Scenario or User Story Refactor incrementally in a story-by-story or scenario-by-scenario mode, or otherwise identify the functional areas as a basis for incrementing.|
|Improve DB queries and introduce data caching so that the system will run faster||. . . Refactor all user management functionality . . . Catalog browsing functionality . . . Check functionality|
|2. By Component First, refactor everything related to a single component, then move to the next.|
|Change indexing process to batch processing so that the process is at least 2 – 3 times faster for an average web page||. . . For parsing (parser component) . . . For entity extraction (analyzer component) . . . For storing in the index (index component)|
|3. Interface / Implementation First, create new interfaces and wrap in the old functionality, then refactor the functionality itself.|
|Extract all parsing parameters into an xml configuration file so that that the process can be tuned easily without changing the code||. . . Install PINGID Protocol server and test with mock identity provider . . . Read configurations from file in any format . . . Refactor configuration functionality to support certain structure and schema validation|
|4. Strangle Component Incrementally move the functionality of the component to other components; once everything is moved, delete the old code.|
|Replace database with custom search index so that indexing and search performance improves 10 – 20 times||. . . Move index data to custom search index first . . . Move entity dictionaries|
|5. Inline Refactoring / Extraction Refactor the functionality in-line where it currently is, but then extract it and encapsulate into a component, class, or method / function.|
|Replace current ad hoc parsing with grammar-based functionality so that changing parsing rules would become easy and without need for coding||. . . Refactor the code (as is) to use grammar notation . . . Extract all grammar-related functionality into a grammar engine|
|Table 1. Methods of splitting refactors|
Establishing Acceptance Criteria
As with user stories, defining acceptance criteria for refactors helps resolve ambiguities. Figure 4 illustrates the additional specificity that comes with an established acceptance criteria.
Acceptance criteria can often be used as a natural basis for splitting. For example, the first step in Figure 4 might be to “ . . . make synchronous non-configurable batch processing with a single query to dictionary, but without the debug logging.” Then “ . . . add capability to read batch size from the file.” Step 3 would be “ . . . process items asynchronously,” and finally “ . . . add debug logging functionality.”
Even though refactoring is focused on the internal working of the code, as with any other story, teams demonstrate the results. From the example above, the teams might demonstrate the following:
- Reduced processing time on a few web pages, compared to the previous benchmark
- Dependency of processing time on the size of the batch, which can be configured from the file
- A code snippet for asynchronous processing
- The debug log file that captures all the operations
- The number of queries to dictionaries per batch (from the log file)
Adopting the Culture
Refactoring is a mandatory skill for Agile Teams. Refactors should routinely appear on the Team Backlog and be included—along with in-line refactoring—in story estimates. A Design Community of Practice (CoP) can foster awareness and attention to refactoring techniques. Scrum Masters can help their teams learn efficient approaches to specifying, estimating, and splitting refactors. Product Owners should embrace refactoring by prioritizing the work and by helping to define acceptance criteria.
Learn More Fowler, Martin, et al. Refactoring: Improving the Design of Existing Code. Addison-Wesley, 1999.  Martin, Robert. Clean Code: A Handbook of Agile Software Craftsmanship. Prentice Hall, 2008.  Wake, William. Refactoring Workbook. Addison-Wesley, 2003.
Last update: 4 November, 2015