Refactoring in Agile:
How To Make Design Changes Safely and Effectively

As a software development manager you just inherited a large codebase and everyone is expecting you to come up with crucial new features.

Preferably yesterday.

You’d love to do it. But neither you nor your team understands the code. Could code refactoring help you? Code refactoring in agile is hot after all. You hear it’s what keeps teams able to respond to change quickly.

And that’s exactly what you need.

But neither you nor your team have experience with it. You’re looking to get a good start, to understand the fundamentals and receive guidance on how to practice it.

Well, you’re in the right place.

This article will equip you with the right basics, so you’ll soon be well on your way refactoring the existing code base and adding new features.

Test Driven Development (TDD)

Brief Overview

Over time, software design tends to degrade, and systems become increasingly difficult to change.

Code refactoring aims to keep software from degrading, or, when it’s already degraded, to improve its design so it becomes easier to understand and change.

Code refactoring is important to eliminate design flaws, achieve maintainability, and extensibility of a software system.

Crucially, code refactoring changes the design of the code, but never the behavior of the software. Never the twain shall mix.

Brief History: The Birth of Refactoring

Refactoring wasn’t a happy accident.

Programmers have been instinctively cleaning up, reorganizing, and restructuring their code since the first programs were written.

Then, In the late 1980s, two computer science graduate students at the time (William Opdyke at the University of Illinois at Urbana-Champaign and William Griswold at the University of Washington), independently invented what’s now called software refactoring.

Here’s a brief evolution of refactoring:

Researcher

William Griswold

  • Young graduate student of Computer Science 
  • Relatively inexperienced in working outside the academia
  • 1985-86: Began doctoral program at University of Washington and started working with David Notkin

William Opdyke

  • Computer Science student
  • Software development experience at Bell Labs involving hundreds or thousands of staff, supporting products with lifetimes of 10 to 20 years or more. 
  • 1988: Began doctoral program at the University of Illinois at Urbana-Champaign
Focus of Research

Software Evolution

Once applications were deployed, they could no longer be altered.

Software Change

Including knowledge-based software engineering, program transformation, and database schema evolution.

Early Influencer(s)

1987:

  • Tony Hoare and his colleague’s Article: Laws of programming.
  • Key principle: Algebraically transform one design of a program into any other possible design.

1988:

  • Notkin’s proposal: Restructure programs to improve their design.
  • Griswold extended Notkin’s proposal that restructurings should be meaning-preserving.

1986

  • Fred Brooks’ Article – No Silver Bullet
  • Shared the key idea: “Grow—don’t build—software”
  • Ralph Johnson’s course in object-oriented programming promotes reusability and the idea to improve the design of code. 
  • Other key influencers: a research team at Tektronix that included Kent Beck, Ward Cunningham, and Rebecca Wirfs-Brock.

1990:

  • Opdyke and Johnson authored “Refactoring: An Aid in Designing Application Frameworks and Evaluating Object-Oriented Systems.”
Key Driver 
  • How to build a tool to support meaning-preserving restructuring?
  • How to program restructuring operations (refactorings) that support the design, evolution, and reuse of object-oriented application frameworks.
Key Contributions

1991:

  • Griswold completed his dissertation.
  • Command-line tool automated a modest catalog of meaning-preserving transformations.
  • Demonstrated that you can carry out a sophisticated rearchitecting of an application using just meaning-preserving transformations.

1992:

  • Opdyke completed his dissertation.
  • What refactorings should you apply in a given situation? 
  • How, if at all, can you safely refactor in a given situation?
  • Demonstrated how you could achieve significant functional changes and system-level design improvements by composing a series of more primitive changes.
  • Opdyke introduced Martin Fowler to his refactoring research.
Unique Contributions
  • Focused on the meaning-preserving aspects of refactoring.
  • Focused on the unique opportunities in refactoring object-oriented programs.

How Does It Work

What is Refactoring?

Refactoring code is changing code with two crucial constraints:

  1. The changes make the code easier to understand and thus cheaper to modify.
  2. The changes never change the functionality, the observable behavior, of the code.

That second constraint bears repeating: a refactoring should never change the behavior of a program. This means that if the program is called before and after a refactoring with the same set of inputs, the resulting set of output values will be the same.

You’ll also hear refactoring used as a noun and a verb. And here’s a quick definition.

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.

Refactoring (verb): to restructure software by applying a series of refactorings without changing its observable behavior.

Why Should You Refactor?

If the code works, isn’t refactoring gold plating? A waste of time? A mental exercise to keep up your billing by the hour? Entertaining yourselves as you try and make your code the best from some purist standpoint, or is there real value in doing it?

code refactor

It turns out that refactoring is how you improve the design and quality of your code. And if you stop working on your code as soon as it seems to work, it’s very likely that it is not well suited to future changes. And thus, future changes will be more expensive.

Without proper care, the cost of changing an application can increase exponentially in proportion to its size and complexity. Eventually, it may no longer be cost-effective to update the application further.

Refactoring helps ensure that your released code is easy to maintain. As the saying goes — A stitch in time saves nine, timely refactoring makes it easy and inexpensive to incorporate new functionality.

When Should You Refactor?

If your system has much technical debt, should you stop working on new features and spend a few weeks refactoring? Sometimes this can make sense, but there are certain problems with this strategy.

Let us illustrate this with an analogy:

when to refactor

Imagine that you’re a chef in a fancy restaurant. You’ve been in business for six months, and you’ve established some regular customers. Things have been pretty busy, however, and you let cleaning slip. The state of your kitchen and your pots and pans interferes with your ability to deliver good-tasting meals before your guests get impatient and leave.

After a few close calls and a visit from the health inspector, it’s time that you debugged the kitchen, as it were. But you can’t just shut down your restaurant for a few weeks while you get everything cleaned up, can you? Your regulars might tolerate it, but it’s not guaranteed and you’ll definitely lose a lot of business from casual passers-by.

The same applies to software development.

Shutting down operations, the creation of value with new or improved features, is unlikely to go well with your client, stakeholders, and managers.

Good restaurants don’t operate like this. They don’t let cleanliness issues go unchecked to the point where they need to shut down. Note that I said good restaurants. They make cleanliness and maintenance a regular part of their ongoing operations.

And, again, the same applies to software development.

Regular code refactoring is like cleaning your kitchen. It’s far more effective in keeping you able to respond to new requirements and delivering value to your users or customers.

When Not to Refactor?

There are times when you shouldn’t refactor.

Refactoring should never change the behavior of the code.

This means you need to be able to verify the behavior of your code. All of your code, not just the bits you’ve changed.

Therefore you can’t, and don’t even want to refactor, when:When Not to Refactor

  • You’re in the midst of a functional change. Even less so, when you’re struggling to get that change to work. It frequently results in going down a rabbit hole. Or two, or three. What you want to do is get the program back into a known good state. Then refactor it so your functional change is easier to make. And only then return to the functional change.
  • You have an application with massive technical debt and few, if any, automated tests to verify its behavior.

    What you want to do in this case is apply a technique called the Golden Master. It involves having a large set of inputs to run through the application and comparing the output before and after a code change. This way you can verify codebases that are otherwise “untestable” and you can find a way to make them more testable by refactoring. 

    Another way to proceed is to declare technical debt bankruptcy and create a Strangler Fig Application that strangles and replaces the current one.

  • You have an immediate deadline to meet. Your best course is to defer a refactoring until after that deadline has been met.

    However, when you knowingly take on technical debt, you need to pay it down right after the deadline. That’s because the quickest path to technical bankruptcy is always putting the deadline, the short term, before paying down the debt, the long term.

The ABCs of Code Smells

Kent Beck coined the term code smells in the 1990s. Code smells are symptoms, or signs, in a program’s source code that indicate a potentially deeper problem.

In his seminal book “Refactoring,” Martin Fowler gives a similar definition: “A code smell is a surface sign that usually corresponds to a deeper problem in the system.”

Code smells are not bugs.

The code is correct and the program functions as it should.

Instead, they show weaknesses in code design that may slow down development or increase the risk of bugs or failures in the future.

An example of a code smell is “Duplicate Code”: the same code copy-pasted in multiple places. It’s just a matter of time before someone forgets to change one of those copies along with its brethren.

The refactoring to apply to deodorize this smell is “Extract Method”: merging the copies into a function or a class method.

Because code smells are “problems waiting to happen” it’s good to strive for zero code smells.

The Process of Refactoring

To minimize the likelihood that you’ll accidentally introduce bugs as part of your refactoring, you want to follow a strict process.

refactoring process

  1. Ensure you can back out — restore to a version that was proven to work correctly. Make sure you’ve committed all your changes and all tests against the committed code succeed. This way you can restore back to this point if your refactoring doesn’t go as you envisioned.
  2. Identify what you want to refactor and how — which refactorings to use.
  3. If you have multiple refactorings to execute to accomplish a bigger restructure, you can optionally, select a subset of automated tests to verify unchanged behavior after each individual refactoring.
  4. Iteratively: apply a refactoring and verify behavior is unchanged. If the tests show you did change the behavior, change the code, never the tests.
  5. If you used a subset of automated tests during the refactoring process, run all tests to verify unchanged behavior for the whole application.

    Here again, if something broke, change the code to make the tests pass — don’t change the tests.

    If you realize that you’re just not going to be able to get the tests passing again in a reasonable amount of time, restore back to the working code you had before the refactoring process.

  6. Assess the effect of the refactorings on quality characteristics of the software (e.g., complexity, understandability, maintainability) or the process (e.g., productivity, cost, effort).

    If they’re not satisfactory and can’t easily be improved, restore back to the working code you had before the refactoring process.

Why is Refactoring Considered Dangerous?

It isn’t. I’ve never heard that it is dangerous.

Refactoring dangers

However, there are some mistakes you could make:

  • You don’t use automated refactoring tools and don’t have enough test coverage. You might fail to complete a refactoring correctly.
  • You might not be using the Golden Master Technique. Code that doesn’t have tests is often not designed to be testable. In order to make the code testable, you have to refactor it. See how this seems like an impossible task? And the solution: The Golden Master test paves the way for being able to safely begin refactoring.
  • You might not be aware of ‘reflective access’ to some of the (supposedly) private things you changed. Some other code might be bypassing the access controls.
  • You go beyond a simple refactoring and end up in re-design territory. When you confuse the two activities, chances are you will make mistakes in both.
  • You ‘refactor’ a published public API – except you did not complete the task because you don’t have access to the calling clients. They just break.

Refactoring is no more dangerous than any other coding practice, really. You have to be aware of what can go wrong and take steps to avoid it.

A Quick Example: Extract Method 

Problem

You have a code fragment that can be grouped together.

————————————————–

void PrintOwing()
{
  this.PrintBanner();
  // Print details.
  Console.WriteLine(“name: ” + this.name);
Console.WriteLine(“amount:”+ this.GetOutstanding());
}

————————————————–

Solution

Move this code to a separate new method (or function) and replace the old code with a call to the method.

————————————————–
void PrintOwing()
{
this.PrintBanner();
this.PrintDetails();
}
void PrintDetails()
{
Console.WriteLine(“name: ” + this.name);
Console.WriteLine(“amount: ” + this.GetOutstanding());
}

————————————————–

Why Refactor

  • The more lines found in a method, the harder it is to figure out what the method does. This is the main reason for this refactoring.
  • Besides eliminating rough edges in your code, extracting methods is also a step in many other refactoring approaches.

Benefits

  • More readable code! Be sure to give the new method a name that describes the method’s purpose: createOrder(), renderCustomerInfo(), etc.
  • Same level of abstraction in both methods, which helps understanding of the code.
  • Less code duplication. Often the code that is found in a method can be reused in other places in your program. So you can replace duplicates with calls to your new method.
  • Isolates independent parts of code, meaning that errors are less likely (such as if the wrong variable is modified).

Further Reading

  1. W.F. Opdyke and R.E. Johnson, “Refactoring: An Aid in Designing Application Frameworks and Evolving Object-Oriented Systems,” Proc. 1990 Symp. Object-Oriented Programming Emphasizing Practical Applications (SOOPPA 90), 1990, pp. 274–282.
  2. W.G. Griswold and D. Notkin, “Automated Assistance for Program Restructuring,” ACM Trans. Software Eng. Methodology, vol. 2, no. 3, 1993, pp. 228–269.
  3. W.G. Griswold and D. Notkin, “Architectural Tradeoffs for a Meaning-Preserving Program Restructuring Tool,” IEEE Trans. Software Eng., vol. 21, no. 4, 1995, pp. 275–287.
  4. W.F. Opdyke, “Refactoring Object-Oriented Frameworks,” PhD diss., Dept. Computer Science, Univ. of Illinois at Urbana-Champaign, 1992.
  5. M. Fowler et al., Refactoring: Improving the Design of Existing Code, Addison-Wesley Longman, 1999.
  6. K. Beck, Extreme Programming Explained: Embrace Change, Addison-Wesley Longman, 1999.

Start Refactoring

Remember that codebase that you inherited? The one that you and your team don’t understand? That had you wondering how to deliver on everyone’s expectations for new features?

You now know how to tackle that codebase with confidence.

If there are few or no tests, your first step is to create a Golden Master.

When you have that Golden Master, or if you’re lucky enough that the codebase does have good test coverage, you can proceed with refactoring to make adding new features easier, and with adding those new features. All without closing for business!

So, go, start refactoring and adding the features everyone is waiting for.

Refactoring in Agile

Life is Good When Your Agile Teams Are in Sync!

Request for a customized SwiftEnterprise Demo!