As a software development manager you just inherited a large codebase and everyone is expecting you to come up with crucial new features.
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.
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.
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:
|Focus of Research|
Once applications were deployed, they could no longer be altered.
Including knowledge-based software engineering, program transformation, and database schema evolution.
Refactoring code is changing code with two crucial constraints:
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.
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?
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.
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:
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.
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:
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.
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.
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.
To minimize the likelihood that you’ll accidentally introduce bugs as part of your refactoring, you want to follow a strict process.
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.
If they’re not satisfactory and can’t easily be improved, restore back to the working code you had before the refactoring process.
It isn’t. I’ve never heard that it is dangerous.
However, there are some mistakes you could make:
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.
You have a code fragment that can be grouped together.
// Print details.
Console.WriteLine(“name: ” + this.name);
Move this code to a separate new method (or function) and replace the old code with a call to the method.
Console.WriteLine(“name: ” + this.name);
Console.WriteLine(“amount: ” + this.GetOutstanding());
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.