Glacial Refactoring

A Glacier-inspired Approach to Code Cleanup

Rose Hooper   |   PyOhio 2023

PyOhio 2023

Why glaciers?

Why glaciers?

Glacial refactoring is a gradual process.

Several distinct stages

We can draw parallels between code features and glacial features.

Glacial Refactoring Stages

  • Codescape Accumulation
  • Refactoring Phase
  • Evolutionary Codescaping
  • Accumulation (Pre-Glacial Period)
  • Glacial Advance
  • Glacial Retreat

Codescape

All software has a shape. I call this a codescape.

Some code is easy to follow, some is rugged and complex.

Codescape Accumulation

The beautiful beginnings.

Initial concept. Startup/project formed. Prototype. Adoption. Features added, capabilities changed.
New developers, more contributors, more ideas. More capabilities. Plugins, microservices, 3rd party APIs.

The ever evolving ecosystem around us

Every-day influences shape the codescape.
Bigger.
Better.
Faster.

New hardware 🎮,  new frameworks 🏗️,  new languages 🐍,  new tools 🗜️.

New everything!

New everything!

Everything is amazing!

But Wait

The promised land of bug-free, performant, easy to modify, unit-tested code doesn't exist.

The unit tests are fragile.
Some integration tests don't run.

There are magics sprinkled in the code and deployment toolchain.

Even cosmic rays cause problems.

Software projects have an evolving codescape.

Gradual accumulation of cruft and code features

Well-intentioned refactorings. Aborted features.

 Bugs 🐛 Backwards compatibility. Database cruft. Relics. Business priorities and community pressure. Team churn. 

Rushed code.
 Misunderstood code. Magic dev environments.
Ownership changes. Outsourcing.
Friendly and hard forks. Broken dependencies.
Unsupported EOL
versions of things; Ubuntu 12; Python 2.7, 3.4; RHEL/CentOS 5.
Unmaintained dependencies. Performance fixes.
Cloudification.
Scope creep, deadlines, break-fix. Community issues, disagreements.

My head hurts.

Glacial Refactoring

  • Gradual process intended to take time.
  • Frequent small changes with minimal scope.
  • Safe, easy to roll-back changes.
  • Self-documenting next steps.
  • Easy to incorporate into any release lifecycle.

Glacial Refactoring

The First Refactoring Phase

During this phase, we'll act like glacial detectives, looking for clues in the structure of our code.

Our goal? To gradually flatten and streamline the code, smoothing out those rugged 'code mountains' we encounter.

Identifying the Terrain

The first step in glacial refactoring is understanding our terrain. We'll look for and identify features in our codescape that have emerged over time.

Example Codescape Features

  • Feature Moraines: Accumulated layers of code, diverse and large, each layer a testament to our evolving project.
  • Code Erratics: Isolated pieces of code, remnants of past needs, adrift in our current codescape.
  • Workaround Eskers: Trails of quick fixes and shortcuts, weaving through our code, often born from urgent needs.
  • Deadline Scars: Marks of rushed development; hasty patches, unpolished code, the product of tight timelines.
  • Data Kettles: Pockets of data, scattered and disconnected, leftovers from the flow of information over time.
  • Code Mountains: Deeply nested and complex code structures, resembling the rugged terrain carved by glaciers.

Some Example Codescape Features

Exploring Feature Moraines in Code

Let's examine a code sample illustrating potential Feature Moraines — areas where code has accumulated over time.

from db.manager import db_connection, dsdb

class GlacierAnalysisTool:
    def __init__(self, db=db_connection): ...
    def analyze_ice_thickness(self): ...
    def map_glacial_retreat(self): ...

class MoraineDataProcessor(GlacierAnalysisTool):
    def aggregate_debris_data(self): ...
    def calculate_moraine_age(self): ...
    def import_initial_core_analysis(self, path): ...
    def legacy_data_cleanup(self): ...
    def add_core_analysis(self, core_id, url=CORE_API, provider="USGS"): ...
Hints of legacy data and and importing initial data.

Tracing the System's Architecture

Here, we find hints of our system's architecture, like a separate Data Science database. This knowledge helps guide our refactoring strategy.

from db.manager import db_connection, dsdb

class GlacierAnalysisTool:
    def __init__(self, db=dsdb): ...
    def analyze_ice_thickness(self): ...
    def map_glacial_retreat(self): ...

class MoraineDataProcessor(GlacierAnalysisTool):
    def aggregate_debris_data(self): ...
    def calculate_moraine_age(self): ...
    def import_initial_core_analysis(self, path): ...
    def legacy_data_cleanup(self): ...
    def add_core_analysis(self, core_id, url=CORE_API, provider="USGS"): ...
            
            

Documenting with Glacial Notes

As we refactor, it's vital to add 'Glacial Notes' — comments that document our observations and action items. This helps both our understanding and that of future developers.

from db.manager import db_connection, dsdb
# GlacialNote: dsdb appears to be a data science database that we don't have access to.

class GlacierAnalysisTool:
    def __init__(self, db=dsdb): ...
    def analyze_ice_thickness(self): ...
    def map_glacial_retreat(self): ...

class MoraineDataProcessor(GlacierAnalysisTool):
    __glacial_note__ = "Flatten: Pull base up, this is the only use of GlacierAnalysisTool."
    def aggregate_debris_data(self): ...
    def calculate_moraine_age(self): ...
    def import_initial_core_analysis(self, path):
        # GlacialNote: Appears to be unused, no refs in current code
        # Investigate: Git history for reference.
        ...
    def legacy_data_cleanup(self): ...
    def add_core_analysis(self, core_id, url=CORE_API, provider="USGS"):
        # GlacialNote: Investigate: if CORE_API has other providers
        # or if its is only ever USGS.
        ...
            
            

Flattening Code Mountains

A 'Code Mountain' is an unusually deeply nested code formation.

One of the most intimidating naturally occuring code formations to work with. Usually full of Code Moraines and Code Erratics.

Flattening Code Mountains

The distinct shape of a Code Mountain is easy to spot. The left margin is shaped like the peaks and valleys of a mountain range.

for slide in presentation:
    if slide.is_have_content():
        for elemento in slide.elements:
            if elemento.is_textual():
                for parrafo in elemento.parrafos:
                    if parrafo.is_have_special_format():
                        if parrafo.is_bold():
                            elemento.apply_bold(parrafo)
                        elif parrafo.is_italic():
                            elemento.apply_italic(parrafo)
                        else:
                            if sys.getenv("FF_20090101_OPS-2319"):
                                sys.exit("Unsupported format - 不适合")
                        continue
                    else:
                        parrafo.normalize_format()
            else:
                for img in elemento.media:
                    if img.is_fit(slide):
                        if media_utils.needs_light_adjustment(img):
                            slide.add_img(img, bg=(255,255,255))
                        else:
                            raise NotImplementedError('फिट नहीं होता')
                    elif img.need_resize():
                        img.resize_for(slide)
                    else:
                        raise ValueError("Img not match - 图片不匹配")
                else:
                    elemento.handle_no_media()
        else:
            slide.alert_no_elementos()
    else:
        logging.error("Slide is empty")
    
        

Flattening Code Mountains

Here we add a couple Glacial Notes documenting findings before changing code.

for slide in presentation:
    if slide.is_have_content():
        for elemento in slide.elements:
            if elemento.is_textual():
                for parrafo in elemento.parrafos:
                    if parrafo.is_have_special_format():
                        if parrafo.is_bold():
                            elemento.apply_bold(parrafo)
                        elif parrafo.is_italic():
                            elemento.apply_italic(parrafo)
                        else:
                            # GlacialNote: Flag no longer set, JIRA issue closed in 2022
                            # Clue: merge commit for REL-4832 fixed in presentation UI.
                            # Action: Remove
                            if sys.getenv("FF_20090101_OPS-2319"):
                                sys.exit("Unsupported format - 不适合")
                        continue
                    else:
                        parrafo.normalize_format()
            else:
                # GlacialNote: Good spot to flatten or extract to functions
                for img in elemento.media:
                    if img.is_fit(slide):
                        if media_utils.needs_light_adjustment(img):
                            slide.add_img(img, bg=(255,255,255))
                        else:
                            raise NotImplementedError('फिट नहीं होता')
                    elif img.need_resize():
                        img.resize_for(slide)
                    else:
                        raise ValueError("Img not match - 图片不匹配")
                else:
                    elemento.handle_no_media()
        else:  # GlacialNote: validation, flip if-else
            slide.alert_no_elementos()
    else:  # GlacialNote: validation, flip if-else
        logging.error("Slide is empty")
    

Commit

Hauling Away Code Erratics

With our intent committed, we can now make fixes, one at a time. One example is Code Erratics..


for slide in presentation:
    if slide.is_have_content():
        for elemento in slide.elements:
            if elemento.is_textual():
                for parrafo in elemento.parrafos:
                    if parrafo.is_have_special_format():
                        if parrafo.is_bold():
                            elemento.apply_bold(parrafo)
                        elif parrafo.is_italic():
                            elemento.apply_italic(parrafo)
                        else:
                            # GlacialNote: Flag no longer set, JIRA issue closed in 2022
                            # Clue: merge commit for REL-4832 fixed in presentation UI.
                            # Action: Remove
                            if sys.getenv("FF_20090101_OPS-2319"):
                                sys.exit("Unsupported format - 不适合")
                        continue
                    else:
                        parrafo.normalize_format()
            else:
                # GlacialNote: Good spot to flatten or extract to functions
                for img in elemento.media:
                    if img.is_fit(slide):
                        if media_utils.needs_light_adjustment(img):
                            slide.add_img(img, bg=(255,255,255))
                        else:
                            raise NotImplementedError('फिट नहीं होता')
                    elif img.need_resize():
                        img.resize_for(slide)
                    else:
                        raise ValueError("Img not match - 图片不匹配")
                else:
                    elemento.handle_no_media()
        else:  # GlacialNote: validation, flip if-else
            slide.alert_no_elementos()
    else:  # GlacialNote: validation, flip if-else
        logging.error("Slide is empty")
    

Hauling Away Code Erratics

Add Glacial Notes as needed.


for slide in presentation:
    if slide.is_have_content():
        for elemento in slide.elements:
            if elemento.is_textual():
                for parrafo in elemento.parrafos:
                    if parrafo.is_have_special_format():
                        if parrafo.is_bold():
                            elemento.apply_bold(parrafo)
                        elif parrafo.is_italic():
                            elemento.apply_italic(parrafo)
                        continue  # GlacialNote: Appears to be unnecessary.
                    else:
                        parrafo.normalize_format()
            else:
                # GlacialNote: Good spot to flatten or extract to functions
                for img in elemento.media:
                    if img.is_fit(slide):
                        if media_utils.needs_light_adjustment(img):
                            slide.add_img(img, bg=(255,255,255))
                        else:
                            raise NotImplementedError('फिट नहीं होता')
                    elif img.need_resize():
                        img.resize_for(slide)
                    else:
                        raise ValueError("Img not match - 图片不匹配")
                else:
                    elemento.handle_no_media()
        else:  # GlacialNote: validation, flip if-else
            slide.alert_no_elementos()
    else:  # GlacialNote: validation, flip if-else
        logging.error("Slide is empty")
    

Heavy Duty Excavation: Flipping if-else

Flipping if-else blocks is a powerful way to evaluate and flatten code.


for slide in presentation:
    if slide.is_have_content():
        for elemento in slide.elements:
            if elemento.is_textual():
                for parrafo in elemento.parrafos:
                    # GlacialNote: if-else main code path second
                    if parrafo.is_have_special_format():
                        if parrafo.is_bold():
                            elemento.apply_bold(parrafo)
                        elif parrafo.is_italic():
                            elemento.apply_italic(parrafo)
                        continue  # GlacialNote: Appears to be unnecessary.
                    else:
                        parrafo.normalize_format()
        

Heavy Duty Excavation: Flipping if-else

Here we negate the if and swap the code before and afer else. The normal main code path is now first.


for slide in presentation:
    if slide.is_have_content():
        for elemento in slide.elements:
            if elemento.is_textual():
                for parrafo in elemento.parrafos:
                    if not parrafo.is_have_special_format():
                        parrafo.normalize_format()
                    else:
                        if parrafo.is_bold():
                            elemento.apply_bold(parrafo)
                        elif parrafo.is_italic():
                            elemento.apply_italic(parrafo)
                        continue  # GlacialNote: Appears to be unnecessary.
    

Flipping Off The Else

Now we can use continue to tell the else to take a hike and peel off a layer.


for slide in presentation:
    if slide.is_have_content():
        for elemento in slide.elements:
            if elemento.is_textual():
                for parrafo in elemento.parrafos:
                    if not parrafo.is_have_special_format():
                        parrafo.normalize_format()
                        continue
                    if parrafo.is_bold():
                        elemento.apply_bold(parrafo)
                    elif parrafo.is_italic():
                        elemento.apply_italic(parrafo)
                    continue  # GlacialNote: Appears to be unnecessary.

    

Evolutionary Codescaping

The Glacial Retreat

Envisioning the Future

Evolutionary Codescaping is envisioned as an integral part of software development life-cycles.

  • Using GlacialNotes for continuous improvement
  • Integrating refactoring seamlessly into SDLC
  • Maintaining a focus on core Glacial Refactoring principles
  • Tooling to facilitate understanding code behaviour and usage
  • Developing parallel code paths for risk mitigation

The Road Ahead

The journey of Glacial Refactoring is just beginning.

Assistance Welcome

Join me in shaping this methodology.

  • Refining terminology, creating guidelines, and writing examples
  • Aligning with existing practices and terminology
  • Promoting adoption of a positive perspective on all kinds of past code.
  • Building tooling: helper libraries, reporting tools, SDLC integrations, linting tools, etc.

Contacting Rose

Thank you for joining me on this exploration of Glacial Refactoring.

PyOhio 2023

Here's ways to reach out to me.