Code Refactoring

One possible refactoring of Java's stream class hierarchy. Source: Murphy-Hill and Black 2008, fig. 1.
One possible refactoring of Java's stream class hierarchy. Source: Murphy-Hill and Black 2008, fig. 1.

Software is rarely perfect. Bugs need to be fixed. Code and its structure can be improved. Even when no new features are added, restructuring code can make it easier to understand and maintain. Refactoring is thus about restructuring existing code without changing its behaviour. Refactoring changes internal structures while preserving external behaviour. Refactored code works just as before, passes existing tests but is better in terms of structure and organization.

Refactoring shouldn't be a special task that needs to be scheduled. Refactoring should be a day-to-day programming discipline. Whenever developers see an opportunity to improve existing code, they should refactor.

IDEs can help in automated refactoring. Testing is an essential activity. It ensures that refactored works as before and nothing is broken. Incremental refactoring is preferred over a full-scale code rewrite.

Discussion

  • When does it make sense for me to refactor working code?

    Given any working code, developers may be hesitant to change code unnecessarily. But refactoring can still be useful. When program flow is not clear, refactoring improves clarity. Software becomes easier to update and maintain in the long run. We may also refactor to improve performance. Refactoring first can help developers add new features more easily. Sometimes refactoring is done to enable code reuse.

    There's a common misconception that refactoring activities need to be scheduled into a project. While planned refactoring is possible, it's better to refactor continuously. Whenever developers detect bad code and they sense an opportunity to make it better, refactoring should be done. When small improvements to code are done continuously, there will hardly be a need to schedule refactoring as a separate task.

    A common excuse is that refactoring takes time away from actual development. On the contrary, refactoring saves time in the long run. Software tends to degrade over time as complexity increases. Refactoring is a way to mitigate this.

  • What are some indicators that my code may need refactoring?

    As multiple developers work on the same code base over many release cycles, complexity increases. There's less cohesion in terms coding styles and design. Some call this code rot, characterized by duplicate code, myriad patches, and bad classifications. Other symptoms include unhealthy dependencies between classes or classes doing too many things.

    In one project, 20 developers had created 65,000 lines of code over 5 years. The codebase had lot of dead code, code pertaining to many older API versions, and classes with too many dependencies.

    When a bug fix is required and the fix has to be made in multiple places, this indicates duplicated code. Refactoring can help here. Another example is when a bug fix itself introduces another bug. This implies fragile code that needs refactoring.

    When a new feature request comes in, the current design might make it difficult to build this feature. This is another area where refactoring can help.

  • What does it mean to say that refactoring shouldn't change external behaviour?

    Preserving external behaviour means that given an input and the current system state, the software should give a predictable output.

    However, there are some specialized systems where input-output behaviour is insufficient. Other aspects of behaviour are just as important and must be preserved during refactoring. We note three such systems:

    • Real-time software: Execution time is important. Refactoring should preserve all temporal constraints.
    • Embedded software: Memory usage and power consumption are important. Refactoring should preserve these constraints.
    • Safety-critical software: Concrete notions of safety should be preserved.
  • Could you mention some case studies of code refactoring?
    Some refactorings take more time than others. Source: Szőke et al. 2014, fig. 5.
    Some refactorings take more time than others. Source: Szőke et al. 2014, fig. 5.

    One team took a bottom-up approach to code refactoring by "extracting new classes or coercing existing classes to enforce single responsibility, loose coupling, testability, and low complexity". However, one area of the codebase had to be rewritten. They adopted an incremental TDD approach of writing unit tests, making some changes, and testing.

    Another project team decided not to bring in new dependencies during refactoring. They identified concept boundaries (Admin, HR, User) and refactored within these boundaries.

    A study from 2007 of a small team of mobile app developers showed that refactoring not only improves code quality but also improves productivity. Productivity was measured as lines of code written per hour.

    One misconception is that refactoring can reduce performance. One study found that replacing conditional logic with polymorphism improved performance since compilers optimize polymorphic methods.

    Martin Fowler explains many use cases with original and refactored code. These are worth reading: loading JSON data returned by another service, calculating and formatting a bill for a video store, and refactoring to manage module dependencies.

  • Is there a process that I can follow when refactoring my code?
    Refactoring is a distinct step in a TDD process flow. Source: Albano 2018.
    Refactoring is a distinct step in a TDD process flow. Source: Albano 2018.

    Those who practice Agile methodology, know that refactoring is part of Test-Driven Development (TDD). Refactoring is done only when current tests pass. This gives developers confidence to go ahead and refactor code. Should the refactored code fail some tests, developers can either fix the problem or revert to the working code. Tests ensure that code behaviour isn't affected by refactoring.

    The essence of this approach is incremental refactoring and testing. Test after every small refactoring change. Testing after dozens of refactoring changes can be problematic. If tests fail, it will be hard to isolate the faulty refactoring. It's also clear that we shouldn't add new features or change behaviour while also refactoring. The process isolates adding functionality and refactoring.

    Martin Fowler identifies different refactoring workflows: TDD Refactoring, Litter-Pickup Refactoring, Comprehension Refactoring, Preparatory Refactoring, Planned Refactoring, and Long-Term Refactoring. Frequent planned refactoring might indicate that the team isn't doing enough of the other workflows.

  • Which are the different levels of code refactoring?

    Refactoring can happen at three different levels:

    • Code-level: Remove dead or unused code. Rename variables. Reduce number of method arguments by packaging them into a class. Convert global variables into class data members. Create access methods.
    • Function-level: Merge and consolidate duplicated code. Merge similar code blocks into methods.
    • Architecture-level: Create new class hierarchy. Reorganize responsibilities and encapsulation between subclasses and superclass. Refactor to make way for future changes. Refactor to interface better with databases, external services or frameworks.

    Some make the distinction between primitive refactoring and composite refactoring. The latter is a sequence of primitive refactorings. For example, Rename and Move are primitive refactorings. Others use the terms low-level refactoring and high-level refactoring, or equivalently in a dental metaphor, floss refactoring and root canal refactoring.

  • What are some techniques to refactor code?
    Refactoring techniques and their change scores. Source: Ouni et al. 2016, table I.
    Refactoring techniques and their change scores. Source: Ouni et al. 2016, table I.

    Refactoring Guru explains a number of refactoring techniques, of which we mention a few:

    • Composing Methods: Refactor long methods into smaller method calls. Use local variables instead of modifying method parameters. Move duplicated code into a method.
    • Moving Features between Objects: If a method/field is called more often by another class, move it into that class. Refactor to multiple single-purpose classes if a class is doing many different things. Remove a method that simply calls another method.
    • Organizing Data: Know when to use references and when to use value objects. Make a public field private with access methods.
    • Simplifying Conditional Expressions: Instead of many if-else statements or complex expressions, use a method call that returns a boolean result. In loops, use break, continue or return instead of control flags.
    • Simplifying Method Calls: Remove unused method parameters. Replace methods fivePercentRaise() and tenPercentRaise() with the parameterized method raise(percentage).
    • Dealing with Generalization: Move common methods/fields from subclasses to the superclass. Create a subclass for a feature that's used in only some scenarios.
  • How do I prioritize when there's too much to refactor?

    Use your best judgement to decide what to refactor now and what could be done later. Refactoring too much at once can impact software delivery. Always use an incremental approach.

    In one research study, Analytic Hierarchy Process (AHP) was applied to rank different refactoring techniques and apply those that work best for the codebase. A multi-objective search-based approach has also been proposed.

  • Which are some best practices for code refactoring?

    Refactor only if you've good regression tests and the tests are passing. If some tests are lacking, add extra tests. Some existing tests might depend on old program structure and would need to be rewritten.

    It's always better to refactor before adding new features since tests exist only for current features. Refactor after delivery and before starting the next release cycle. Kent Beck has summarized it thus,

    For each desired change, make the change easy (warning: this may be hard), then make the easy change.

    When team members are working on different feature branches, refactoring can make merges difficult. If some members own some pieces of code, there will be reluctance to refactor their code. These are barriers to refactoring. Work as a team to see how these can be overcome.

    Don't refactor just because you love writing new code or wish to use a coding pattern you've just learned.

  • Are there tools to automate code refactoring?
    Visual Studio 2017 lists six refactoring techniques for C# code. Source: iNoryaSoft 2018.
    Visual Studio 2017 lists six refactoring techniques for C# code. Source: iNoryaSoft 2018.

    Smalltalk's Refactoring Browser (released in 1997) was well-liked and adopted by Smalltalk developers. This was followed by refactoring tools for Java and C#.

    By 2020, most IDEs supported code refactoring. This includes Eclipse, Visual Studio, Xcode, Squeak, Visual Studio Code, , IntelliJ-based IDEs (AppCode, IntelliJ IDEA, PyCharm, WebStorm), and more.

    Back in 2003, it was noted that creating a refactoring tool for C++ has been difficult. Refactoring makes use of the program's Abstract Syntax Tree (AST) but macros and templates add complexity. In fact, it's been said that any tool that works for the whole of C++ would be AI Complete. A subset of refactoring techniques for C++ is supported by Visual Studio.

    Automatic refactoring is aided by static type information. A codebase's development history could be used to identify refactoring opportunities.

Milestones

Aug
1981

In an article on Smalltalk in BYTE magazine, Ingalls uses the word factoring in a software context. He mentions factoring as one of the design principles behind Smalltalk, supported via class inheritance. He defines factoring thus, "Each independent component in a system should appear in only one place." Without factoring, it would be hard to keep interdependent components synchronized and consistent. Factoring makes it easier to locate and maintain the component. The concept of refactoring itself comes from mathematics where a complex algebraic expression might be factored into a simpler and equivalent expression.

1986

R.S. Arnold uses the term software restructuring that's about incrementally making changes to software internals as a way to manage its increasing complexity. It's been noted that the term refactoring came to be used a little later in the context of object-oriented software development.

1990

William Opdyke and Ralph Johnson present a conference paper titled Refactoring: An Aid in Designing Application Frameworks and Evolving Object-Oriented Systems. This is possibly the first use of the term refactoring in published literature. Indeed, refactoring is initially adopted for object-oriented programs.

1991

William Griswold publishes his PhD dissertation on the topic of refactoring functional and procedural programs. A year later, William Opdyke's own dissertation does the same for object-oriented programming.

Jul
1999

Fowler et al. publish their book titled Refactoring: Improving the Design of Existing Code. After introducing the topic, the book describes over 70 refactoring tips. The word refactoring is defined both as a noun and as a verb. In the years to come, this book influences software development. IDEs go on to implement many of the practices to automate refactoring. The second edition of the book appears in 2018.

2006
Popularity of automated refactoring techniques in Eclipse for Java: Developer (row) vs. Technique (col). Source: Murphy-Hill and Black 2008, fig. 5.
Popularity of automated refactoring techniques in Eclipse for Java: Developer (row) vs. Technique (col). Source: Murphy-Hill and Black 2008, fig. 5.

Although many tools existing, developers might not use them if they're not aligned with refactoring tactics preferred by developers. A survey of 41 users of Eclipse IDE for Java development finds that only a few of them use these tools. Simpler techniques such as Rename and Move are more often used than more complex ones such as IntroduceFactory and PushDown.

Sample Code

  • // Source: https://refactoring.guru/extract-method
    // Accessed: 2020-04-28
    // Before
    void printOwing() {
      printBanner();
     
      // Print details.
      System.out.println("name: " + name);
      System.out.println("amount: " + getOutstanding());
    }
     
    // After refactoring (Extract Method)
    void printOwing() {
      printBanner();
      printDetails(getOutstanding());
    }
     
    void printDetails(double outstanding) {
      System.out.println("name: " + name);
      System.out.println("amount: " + outstanding);
    }
     

References

  1. Albano, Justin. 2018. "What Is Refactoring?" DZone, February 08. Accessed 2020-04-28.
  2. Alshehri, Sultan and Luigi Benedicenti. 2014. "Ranking the refactoring Techniques Based on the Internal Quality Attributes." International Journal of Software Engineering & Applications (IJSEA), vol.5, no. 1, January. Accessed 2020-04-28.
  3. Beck, Kent. 2012. "Tweet @KentBeck." Twitter, September 26. Accessed 2021-02-16.
  4. Fowler, Martin. 2003. "C-Refactory." ThoughtWorks, October 18. Accessed 2020-04-28.
  5. Fowler, Martin. 2004. "Definition Of Refactoring." ThoughtWorks. Accessed 2020-04-28.
  6. Fowler, Martin. 2011. "Opportunistic Refactoring." ThoughtWorks. Accessed 2020-04-28.
  7. Fowler, Martin. 2014. "Workflows of Refactoring." ThoughtWorks, January 08. Accessed 2020-04-28.
  8. Fowler, Martin. 2018. "Refactoring: Improving the Design of Existing Code." ThoughtWorks. Accessed 2020-04-28.
  9. Fowler, Martin. 2020. "Refactoring." ThoughtWorks. Accessed 2020-04-28.
  10. Fowler, Martin, Kent Beck, John Brant, William Opdyke, and Don Roberts. 1999. "Refactoring: Improving the Design of Existing Code." First Edition, Addison-Wesley Professional, July. Accessed 2020-04-28.
  11. Ingalls, Daniel H. H. 1981. "Design Principles Behind Smalltalk." BYTE, Byte Publications Inc, pp. 286-298, August. Accessed 2020-04-28.
  12. Kramer, Mark and Philip H. Newcomb. 2010. "Legacy System Modernization of the Engineering Operational Sequencing System (EOSS)." Chapter 10 in: William M. Ulrich and Philip H. Newcomb (eds), Information Systems Transformation, Morgan Kaufmann. Accessed 2020-04-28.
  13. Mens, Tom and Tom Tourwe. 2004. "A Survey of Software Refactoring." IEEE Transactions on Software Engineering, vol. 30, no. 2, February. Accessed 2020-04-28.
  14. Microsoft Docs. 2019. "Edit and refactor C++ code in Visual Studio." Visual Studio 2019, Microsoft Docs, May 31. Accessed 2020-04-28.
  15. Miller, Ivo. 2020. "A refactoring rewrite — when refactoring alone can’t hack it." Redgate, on Medium, January 07. Accessed 2020-04-28.
  16. Moser, Raimund, Pekka Abrahamsson, Witold Pedrycz, Alberto Sillitti, and Giancarlo Succi. 2007. "A Case Study on the Impact of Refactoring on Quality and Productivity in an Agile Team." In: Meyer B., Nawrocki J.R., Walter B. (eds), Balancing Agility and Formalism in Software Engineering, CEE-SET 2007, Lecture Notes in Computer Science, vol. 5082, Springer, Berlin, Heidelberg. Accessed 2020-04-28.
  17. Murphy-Hill, Emerson, and Andrew P. Black. 2008. "Refactoring Tools: Fitness for Purpose." Portland State University, Portland, Oregon, May 7. Accessed 2020-04-28.
  18. Novoseltseva, Ekaterina. 2017. "Code refactoring techniques." Apiumhub, December 05. Accessed 2020-04-28.
  19. Novoseltseva, Ekaterina. 2018. "Code Refactoring Techniques." DZone, January 03. Accessed 2020-04-28.
  20. Ouni, Ali, Marouane Kessentini, Houari Sahraoui, and Mohamed Salah Hamdi. 2013. "The Use of Development History in Software Refactoring Using a Multi-Objective Evolutionary Algorithm." GECCO'13, ACM, July 6–10. Accessed 2020-04-28.
  21. Ouni, Ali, Marouane Kessentini, Houari Sahraoui, Katsuro Inoue, and Kalyanmoy Deb. 2016. "Multi-Criteria Code Refactoring Using Search-Based Software Engineering: An Industrial Case Study." ACM Transactions on Software Engineering and Methodology, vol. 25, no. 3, article no. 23, June. Accessed 2020-04-28.
  22. Refactoring Guru. 2020. "Refactoring Techniques." Refactoring Guru. Accessed 2020-04-28.
  23. Stone, Sydney. 2018. "Code Refactoring Best Practices: When (and When Not) to Do It." Blog, AlexSoft, September 27. Accessed 2020-04-28.
  24. Szőke, Gábor, Csaba Nagy, Rudolf Ferenc, and Tibor Gyimóthy. 2014. "A Case Study of Refactoring Large-Scale Industrial Systems to Efficiently Improve Source Code Quality." In: Murgante B. et al. (eds), Computational Science and Its Applications, ICCSA 2014, Lecture Notes in Computer Science, vol. 8583. Springer, Cham. Accessed 2020-04-28.
  25. VS Code Docs. 2016. "Refactoring." Visual Studio Code, April 14. Updated 2020-04-08. Accessed 2020-04-28.
  26. Vetter, Rebecca. 2017. "Refactoring Dos and Don’ts." Blog, Radial Development Group, March 24. Accessed 2020-04-28.
  27. Waszak, Tobiasz. 2018. "Working with Legacy Code: Cooleaf Refactor Case Study." Blog, Monterail, July 10. Accessed 2020-04-28.
  28. WikiWikiWeb. 2008. "History Of Refactoring." WikiWikiWeb, November 11. Accessed 2020-04-28.
  29. WikiWikiWeb. 2012. "Refactoring Browser." WikiWikiWeb, April 10. Accessed 2020-04-28.
  30. Wikipedia. 2020. "Code refactoring." Wikipedia, April 21. Accessed 2020-04-28.
  31. iNoryaSoft. 2018. "Refactoring C# Code Using Visual Studio." Blog, iNoryaSoft, September 12. Accessed 2020-04-28.

Further Reading

  1. Albano, Justin. 2018. "What Is Refactoring?" DZone, February 08. Accessed 2020-04-28.
  2. Refactoring Guru. 2020. "Refactoring Techniques." Refactoring Guru. Accessed 2020-04-28.
  3. Fowler, Martin, Kent Beck, John Brant, William Opdyke, and Don Roberts. 1999. "Refactoring: Improving the Design of Existing Code." First Edition, Addison-Wesley Professional, July. Accessed 2020-04-28.
  4. Mens, Tom and Tom Tourwe. 2004. "A Survey of Software Refactoring." IEEE Transactions on Software Engineering, vol. 30, no. 2, February. Accessed 2020-04-28.
  5. Szőke, Gábor, Csaba Nagy, Rudolf Ferenc, and Tibor Gyimóthy. 2014. "A Case Study of Refactoring Large-Scale Industrial Systems to Efficiently Improve Source Code Quality." In: Murgante B. et al. (eds), Computational Science and Its Applications, ICCSA 2014, Lecture Notes in Computer Science, vol. 8583. Springer, Cham. Accessed 2020-04-28.
  6. Roberts, Don, John Brant, and Ralph E. Johnson. 1997. "A refactoring tool for Smalltalk." Theory and Practice of Object Systems, John Wiley & Sons, Inc., vol. 3, no. 4, pp. 253-263, October. Accessed 2020-04-28.

Article Stats

Author-wise Stats for Article Edits

Author
No. of Edits
No. of Chats
DevCoins
4
0
1751
1
0
10
2021
Words
2
Likes
7487
Hits

Cite As

Devopedia. 2022. "Code Refactoring." Version 5, February 15. Accessed 2023-11-12. https://devopedia.org/code-refactoring
Contributed by
2 authors


Last updated on
2022-02-15 11:55:29
  • Code Refactoring for the Cloud
  • Database Refactoring
  • Software Design Patterns
  • Clean Code
  • Code Quality Metrics
  • Static Code Analysis