Refactoring

Last updated Dec 30, 2025 Published Oct 12, 2025

The content here is under the Attribution 4.0 International (CC BY 4.0) license

Refactoring is a topic that has recently got a lot of attention in a project I was part of. Many non-technical staff members perceive refactoring as a major project that requires significant time and resources. However, this is one side of the possible techniques available in the industry (to dedicate an entire workfoce just to “refactor”). Here the goal is to explore the different strategies to tackle the maintenance of source code. In this page, I will explore the importance of refactoring, its common misconceptions, and the approaches to refactoring. The goal is to provide a comprehensive view of refactoring, its benefits, and how it can be integrated into the daily development workflow based on the literature out there and my own experience in the industry.

Disclaimer

This post is a work in progress, it is not finished yet. It is a collection of ideas and thoughts that I have been gathering over the years about refactoring.

Sneak peek in the references used here

The references used in this post are based on the seminal work of Martin Fowler, who is a well-known figure in the software development community. His book “Refactoring: Improving the Design of Existing Code” is a foundational text on refactoring and has influenced many developers and teams in their approach to code quality and maintenance (Fowler, 2018). However, this work does acknowledge that refactoring was introduced much earlier by William Opdyke in his 1992 PhD thesis (Opdyke, 1992) which is a reference for others in the field (eg: (Zakeri et al., 2025)).

In addition to Fowler’s work, Michael Fathers book “Working Effectively with Legacy Code” is also a key reference. Feathers provides practical techniques for dealing with legacy code and emphasizes the importance of characterization testing as a way to understand existing code before making changes. This approach is particularly relevant in the context of refactoring, as it helps developers ensure that they are not introducing bugs or breaking existing functionality when making changes to the codebase. (Feathers, 2004).

The content is divided into two parts. The first part provides an overview of refactoring, its importance, and common misconceptions, in addition to the approaches to refactoring. The second part will focus on practical examples of refactoring in real-world projects, combining the theory with practice.

Part I

In this first part, we will go over an overview of refactoring, its importance, and common misconceptions, in addition to the approaches to refactoring. We will also discuss the tools available to help with refactoring and the different types of refactorings, such as readability, design, and performance.

What is Refactoring?

Refactoring is the process of restructuring existing computer code without changing its external behavior (Fowler, 2018). It aims to improve the nonfunctional attributes of the software. Refactoring is crucial because it helps maintain code quality, making it easier to understand, maintain, and extend.

Why to refactor?

Refactoring should be focused on the economics of software development. We refactor because we want to make the next thing in the software cheaper to implement than it is today. (Beck, 2023) highlights that “What makes sense for us to do as programmers may go contrary to the nature of money. When geeky imperatives clash with money imperatives, money wins. Eventually.” In that sense, finding the balance between monetary reasons and long-term sustainability is a balancing act.

The cost associated from unit testing

In discussions of costs regarding refactoring, the idea of costs in unit testing also come into play as they act as a safety net for enabling refactoring. (Ellims et al., 2006) work points to a misunderstanding of the costs associated with unit testing. The paper argues that the benefits of unit testing are often underestimated.

Despite the monetary game, refactoring is not just about making code look nice. It is about improving the structure to reduce complexity, enhance readability, and make it easier to maintain. It should be an integral part of the development process, not a separate project. Refactoring is a continuous process that should be done regularly. According to (Fritzsch, 2024), around 70% of software developers spend time on the maintenance of existing systems. (Hermans, 2021) argues that developers spend more time reading and understanding code compared to writing it.

The implications of refactoring often add to the reduced costs of future changes, as the code becomes easier to understand and modify. Modifying code is the core of evolving business requirements, and software should enable that dynamic, not prevent it. Doing so might pay back in terms of reduced costs in the future.

Common Misconceptions

One common misconception is that refactoring is a major project that needs to be planned and executed separately. This thinking is flawed because it overlooks the continuous nature of refactoring. Treating refactoring as a separate project can lead to increased technical debt and reduced code quality over time.

In my opinion, refactoring is not exactly a flow to follow. It is a learning process. While we are facing the known, refactoring helps developers to learn as they gain understanding of the source code. It might be needed to do many refactorings and see the code in a worse state until it gets to a better state.

While we refactor, we do not introduce new features or fix bugs. The focus is on improving the existing codebase, making it cleaner and more maintainable.

Rewrite or refactor?

Another misconception is that refactoring is the same as rewriting code. While rewriting involves starting from scratch, refactoring focuses on improving existing code without changing its external behavior. Rewriting can be a risky and expensive endeavor, often leading to more problems than it solves. Refactoring, on the other hand, is a safer and more incremental approach that allows developers to make continuous improvements to the codebase.

Slides of the talk "Refactoring at software crafters Madrid"

In june my colleague Javier and I gave a talk at the Software Crafters Madrid meetup about refactoring. The slides are available at speakerdeck. The talk covered topics described in this post with a focus on the practical aspects of refactoring in a real-world project.

Approaches to refactoring

Martin Fowler, in his seminal book “Refactoring: Improving the Design of Existing Code,” advocates for a continuous approach to refactoring. According to Fowler, refactoring should be an integral part of the development process, performed regularly and incrementally. This approach ensures that code remains clean and maintainable, preventing the accumulation of technical debt (Fowler, 2018). However, in this section, I will share some of the approaches that I have seen in the industry: brownfield projects and greenfield projects.

Brownfield

Working on code and changing it to fit a better state requires other techniques that will help developers check its behavior at the moment of the change, for example, having tests in place. To that end, there are katas that use the “characterization testing” approach to build the confidence to refactor.

Characterization testing, also known as golden master testing or approval testing, originated as a way to describe and protect the actual behavior of existing legacy software when no formal specification or tests exist. Michael Feathers coined the term (Feathers, 2004). The goal is to capture the current behavior of the system so that future changes can be verified against this baseline, ensuring no unintended changes occur. Unlike traditional tests that assert expected behavior, characterization tests verify that behavior remains consistent with the observed legacy behavior, making them change detectors rather than correctness validators.

Approval testing vs Characterization testing
It depends on the source used the wording used vary, but refers to the same key idea of checking the output hasn't changed.

It happens to be that while working in professional source code, the safety net of the characterization might not exist. In that case, there are other approaches to start with refactoring:

  1. Characterize the code first - Characterizing the code first means that before doing anything in the existing code, the characterization will be created and then executed to confirm behavior. (Colla & Acerbis, 2025) suggests that the first step is to understand before acting. Characterizing the code is a way to get there.
  2. On new code, Write the test first - In this stage, we take the opposite direction. We make the observed behavior with tests that check the rules we expect to be fulfilled.
The trap of refactoring

When you don’t have characterization or other tests before changing the code, you might not be refactoring. You might be changing the code without knowing what it does, in other words, how would you verify that the behaviour of the code is the same? The manual approach might work and it is often used in legacy code bases, but it is not a sustainable approach. The key is to ensure that you have a safety net in place before making any changes to the code. In 2019, I wrote a post about a possible strategy to refactor legacy code without tests, which might relevant today.

While I am suggesting these two approaches, I would like to highlight that the usage of characterization tests prior to refactoring is an approach lacking in the practitioners I have interacted with in the past. The most common approach is to change the code and then check if the behavior hasn’t changed manually, which is not sustainable in the long run. At events like Software Crafters Madrid, we often discuss these approaches and share experiences on how to effectively implement them in real-world projects.

Greenfield

In greenfield projects, where you are starting from scratch, the approach to refactoring is different. The focus is on writing clean, maintainable code from the beginning. This involves following best practices, adhering to coding standards, and using design patterns to create a solid foundation for the codebase. In this case, refactoring is more about maintaining the code quality as the project evolves, rather than fixing existing issues.

It is achieved through the use of techniques such as Test-Driven Development (TDD), where tests are written before the code, ensuring that the code is well-structured and meets the requirements from the start. This approach helps prevent the accumulation of technical debt and ensures that the code remains clean and maintainable throughout the development process. TDD enables refactoring to be a natural part of the development workflow, as tests provide a safety net that allows developers to make changes confidently.

Tools at hand

There are several tools available to help with refactoring, such as IDEs with built-in refactoring support, static code analysis tools, and automated testing frameworks. These tools can help identify areas of the code that need refactoring, automate repetitive refactoring tasks, and ensure that the code remains functional after changes are made.

ApprovalsJs and StrykerJs

In a blog post by Codesai, the author discusses how to use ApprovalsJs and StrykerJs in WebStorm to facilitate refactoring. The post contribution focuses on building a safety net for refactoring by using these tools to create and run characterization tests.

The types

The programmers brain book, points to an approach of readability, which is a key aspect of refactoring. The book emphasizes that readability is not just about making code look nice, but also about making it easier to understand and maintain. This aligns with Fowler’s approach, where the goal of refactoring is to improve the design of existing code without changing its external behavior. The same idea of readability is used here in the order in which one refactoring should be used over another.

Readability

Readability refactorings focus on improving the clarity and understandability of the code. These refactorings include renaming variables and methods to more meaningful names, breaking down large methods into smaller ones, and simplifying complex conditional statements. The goal is to make the code more readable and easier to follow, which ultimately leads to better maintainability and collaboration among team members. The premise is that readable code is easier to understand, which reduces the cognitive load on developers and makes it easier to spot bugs or improvements(Hermans, 2021).

Design

Design refactorings focus on improving the overall structure and organization of the code. These refactorings include extracting classes or methods, introducing design patterns, and reorganizing code to follow the Single Responsibility Principle (SRP). The goal is to create a more modular and flexible codebase that can adapt to changing requirements. Design refactorings help reduce coupling between components, making it easier to modify or extend the code without affecting other parts of the system.

Design refactorings are not exclusively about readability, but rather about improving the design of the code to make it extensible. They often involve more significant changes to the code structure, such as introducing new classes or methods, or reorganizing existing code to follow best practices and design patterns.

Performance

Performance refactorings focus on optimizing the code for better performance. These refactorings include optimizing algorithms, reducing memory usage, and improving the efficiency of data structures. The goal is to make the code run faster and use fewer resources, which is especially important in performance-critical applications.

Performance refactorings are often necessary when the codebase has grown over time and performance issues have emerged. They can involve significant changes to the code, such as replacing inefficient algorithms with more efficient ones, or restructuring data to improve access times. However, performance refactorings should be done carefully, as they can introduce new bugs or make the code harder to understand if not done properly. In this list they are the last ones to be done.

In addition to the safety net for refactoring, performance refactoring might require additional tools or techniques, such as fitness functions to automate the process of measuring performance improvements (Ford et al., 2017).

A playlist of videos about refactoring

Throughout my searchings about refactoring, I have been able to stumble upon a few videos that changed the way I refactor. To keep track of that, I made a playlist on youtube with all of them.

Even though the types are presented in a linear way, in practice, refactorings might involve a combination of these types, when in doubt, focus on readability first.

Measuring Refactoring Results

Measuring the results of refactoring can be challenging, as the benefits are often intangible and difficult to quantify. However, there are several metrics that can be used to assess the impact of refactoring efforts, research on the the topic points to a set of quality attributes that can be used to measure the effectiveness of refactoring efforts based on QMOOD attributes (missing reference) that applies to Object-Oriented systems:

  • Reusability: The extent to which code can be reused in different contexts or applications.
  • Flexibility: The ability of the code to adapt to changing requirements or environments.
  • Understandability: How easily developers can comprehend the code’s structure and logic.
  • Functionality: The degree to which the code meets its intended purpose and requirements.
  • Extendability: The ease with which new features or functionalities can be added to the codebase.
  • Effectiveness: The overall efficiency and performance of the code in achieving its goals.

Tools

In order to measure these attributes, several tools can be used, such as static code analysis tools, code coverage tools, and performance profiling tools. These tools can help identify areas of the code that need refactoring, track changes over time, and assess the impact of refactoring efforts on code quality and maintainability. Usually an approach is to use a combination of these tools to get a comprehensive view of the codebase and its quality, then measure it before and after refactoring efforts to assess the impact of the changes made.

A note on tools

I have experienced at first hand the usage of SonarQube for those kind of measurements to focus on code quality and trace its evolvability.

Resources

Slidedeck - Refactoring at Software Crafters Madrid

References

  1. Hermans, F. (2021). The Programmer’s Brain: What Every Programmer Needs to Know About Cognition. Manning Publications.
  2. Fowler, M. (2018). Refactoring: Improving the Design of Existing Code (2nd ed.). Addison-Wesley Professional.
  3. Ford, N., Parsons, R., & Kua, P. (2017). Building Evolutionary Architectures: Support Constant Change. O’Reilly Media.
  4. Beck, K. (2023). Tidy First?: A Personal Exercise in Empirical Software Design. Pearson.
  5. Ellims, M., Bridges, J., & Ince, D. C. (2006). The economics of unit testing. Empirical Software Engineering, 11, 5–31.
  6. Fritzsch, J. (2024). Architectural refactoring to microservices: a quality-driven methodology for modernizing monolithic applications.
  7. Feathers, M. (2004). Working effectively with legacy code. Prentice Hall Professional.
  8. Colla, A., & Acerbis, A. (2025). Domain-Driven Refactoring: A hands-on DDD guide to transforming monoliths into modular systems and microservices. Packt.
  9. Opdyke, W. F. (1992). Refactoring object-oriented frameworks. University of Illinois at Urbana-Champaign.
  10. Zakeri, M., Abdi, F., & Bagheri, F. (2025). Enhancing Software Quality Attributes Through Multi-Dimensional Refactoring at Source-Level. Science of Computer Programming, 103434. https://doi.org/https://doi.org/10.1016/j.scico.2025.103434
  11. Bansiya, J., & Davis, C. G. (2002). A hierarchical model for object-oriented design quality assessment. IEEE Transactions on Software Engineering, 28(1), 4–17.

Changelog

  • Oct 20, 2025 - Updated headings

You also might like