Navs
Code Smells Catalog
Divergent Change

Last Revision — April 19, 2022

2 Min Read


  • Also Known As

    ---

  • Obstruction

    Change Preventers

  • Occurrence

    Responsibility

  • Expanse

    Within a Class

  • Related Smells

    - Shotgun Surgery (family)

  • History

    Martin Fowler in book (1999): "Refactoring: Improving the Design of Existing Code"

Divergent Change

If adding a simple feature makes the developer change many seemingly unrelated methods inside a class, that indicates the Divergent Change code smell. Simply put, the class has irrelevant methods in it [1]. As an example, suppose that someone needs to modify class A due to a change in the database but then has to modify the same class A due to a change in the calculation formula [2].

The difference between Divergent Change and Shotgun Surgery is that the Divergent Change addresses the issue within a class, while the Shotgun Surgery between classes.

Causation

Over time, a class tries to do more and more things and has many responsibilities. The fact that the class already has two or more different types of decisions implemented (for example, finding an object and doing something with object [3]) was overlooked and left unrefactored.

Problems

Single Responsibility Principle Violation

The class has too much responsibility.

Duplication

Example

Smelly

class ReportModifier:
    def get_report(self, report_name):
        ...
        return report

    def modify_report(self, report, new_entry):
        ...
        return modified_report

    def run(self, report_name, new_entry):
        report = self.get_report(report_name)
        return self.modify_report(report, new_entry)


report_modifier = ReportModifier(...)
modified_report = report_modifier.run('raport.csv', 'Parsed')

Solution

class ReportReader:
    def get_report(self, report_name):
        ...
        return report

class ReportModifier:
    def modify_report(self, report, new_entry):
        ...
        return modified_report

report_reader = ReportReader(...)
report = report_reader.get_report('raport.csv')

report_modifier = ReportModifier(...)
modified_report = report_modifier.modify_report(report, 'Parsed')

Refactoring:

  • Extract Superclass, Subclass, or new Class
  • Extract Function
  • Move Function

Sources
  • [1] - MS Haque, J Carver, T Atkison, "Causes, impacts, and detection approaches of code smell: A survey" (2018)
  • [2] - Mika Mäntylä, "Bad Smells in Software - a Taxonomy and an Empirical Study" (2003)
  • [3] - William C. Wake, "Refactoring Workbook" (2004)
  • [Origin] - Martin Fowler, "Refactoring: Improving the Design of Existing Code" (1999)