Skip to main content

Module 2: Refactoring Techniques: Case Studies

These cases focus on behavior-preserving change. The goal is not prettier code in isolation; it is a safer path from a hard-to-change design to a clearer one.


Case Study 1: Extract Method Without Changing Behavior

Scenario: A generateInvoice() method is 180 lines long. It loads customer data, calculates discounts, formats tax lines, writes audit events, and returns a PDF. A developer wants to split it quickly before adding a new discount.

Source anchor: Refactoring catalog - Martin Fowler, especially extract-method style refactorings.

Module concepts:

  • refactoring as behavior preservation
  • extract method
  • characterization tests
  • the two hats rule

Wrong Approach

Extract methods while also adding the new discount. When a test fails, nobody can tell whether the extraction or the feature changed behavior.

Better Approach

First pin current behavior with tests for normal invoice, discount invoice, tax-exempt invoice, and empty-line invoice. Extract one concept at a time, keep names domain-specific, and commit the pure refactor before adding the new rule.

Tradeoff Table

ChoiceGainCost
Refactor and feature togetherFewer PRsHard to review and debug
Characterize then extractClear safety netMay preserve existing quirks temporarily
Rewrite from scratchClean design possibleHigh behavior regression risk

Failure Mode

The PDF total changes by one cent because rounding moved to a different step.

Required Artifact

Produce a refactoring checklist with input examples, expected outputs, extraction order, and rollback point.

Project / Capstone Connection

Use this checklist before touching any long method in the Semester 3 project.


Case Study 2: Replacing a Shipping Switch With Polymorphism

Scenario: A shipping calculator has a switch carrier with FedEx, UPS, DHL, in-house courier, and freight. Each branch validates dimensions, calculates price, and formats the tracking number differently.

Source anchor: Refactoring catalog - Martin Fowler.

Module concepts:

  • replace conditional with polymorphism
  • strategy-like dispatch
  • open/closed principle
  • test matrix design

Wrong Approach

Add another case for every new carrier and copy an older branch as a starting point.

Better Approach

Create a CarrierRatePolicy interface and migrate one carrier at a time. Keep the old switch as an adapter during the transition, compare old and new outputs in tests, and remove the switch only after every branch has moved.

Tradeoff Table

ChoiceGainCost
Keep switchEasy local editBranches grow and duplicate
Big-bang replacementClean final shapeLarge risky diff
Incremental polymorphismReviewable migrationTemporary adapter code

Failure Mode

A new carrier changes tracking-number formatting and accidentally changes freight pricing because both live in the same conditional.

Required Artifact

Build a carrier test matrix with dimensions, destination, expected price, expected tracking format, and migration status.

Project / Capstone Connection

Use the same branch-by-branch migration plan for any capstone workflow driven by type codes.


Case Study 3: Introduce Parameter Object for Search Filters

Scenario: A product search function accepts twelve arguments: text, category, min price, max price, currency, sort, page, page size, availability, vendor, country, and personalization flag. Call sites pass ull` and boolean flags in different orders.

Source anchor: Refactoring catalog - Martin Fowler.

Module concepts:

  • introduce parameter object
  • preserve whole object
  • data clumps
  • API readability

Wrong Approach

Keep the signature and add comments at every call site.

Better Approach

Introduce a SearchQuery or ProductSearchCriteria object with named fields, validation, defaults, and focused construction helpers. Migrate call sites gradually and keep tests around pagination and default behavior.

Tradeoff Table

ChoiceGainCost
Long parameter listNo new typeCall sites are fragile
Options objectClear call sitesNeeds validation rules
BuilderHandles complex defaultsCan be overbuilt for simple APIs

Failure Mode

A caller swaps page and pageSize, causing expensive queries and bad UX.

Required Artifact

Write the new parameter object contract, including defaults, required fields, validation failures, and a migration list of affected call sites.

Project / Capstone Connection

Use this artifact for any function in the project with more than four related parameters.


Case Study 4: Branch by Abstraction for a Notification Provider

Scenario: A system sends email through Provider A. The team wants Provider B for reliability, but direct calls to Provider A are scattered across controllers, jobs, and admin tools.

Source anchor: The module's branch-by-abstraction concept and Google Engineering Practices: Small CLs.

Module concepts:

  • branch by abstraction
  • parallel change
  • small reviewable changes
  • rollback design

Wrong Approach

Create a long-lived feature branch that rewrites every call site and switches providers at the end.

Better Approach

Introduce a otificationSender` boundary, route existing Provider A through it, migrate call sites in small changes, add Provider B behind the same contract, then switch traffic with configuration and monitoring.

Tradeoff Table

ChoiceGainCost
Long feature branchOne final diffMerge conflicts and delayed feedback
Direct provider swapFast code changeHard rollback
Branch by abstractionIncremental safetyTemporary indirection

Failure Mode

One admin job still calls Provider A directly and sends duplicate notifications during migration.

Required Artifact

Create a migration board with call sites, adapter status, tests, rollout flag, and rollback steps.

Project / Capstone Connection

Use this plan for any external dependency replacement in the capstone.


Case Study 5: Rename and Move Without Breaking Public API

Scenario: A package exposes UserManager.doStuff(user) publicly. Internally it actually provisions accounts. Several downstream services import it directly, and the team wants a clearer AccountProvisioner.provision(user) API.

Source anchor: Google Engineering Practices: Small CLs.

Module concepts:

  • public contract
  • deprecation path
  • move method
  • semantic naming

Wrong Approach

Rename the class and method in one PR and fix only the current repository.

Better Approach

Add the new API beside the old one, route old calls through the new implementation, mark the old symbol deprecated, update downstream users in small changes, and remove the old entry point only after usage is gone.

Tradeoff Table

ChoiceGainCost
Immediate renameClean code nowBreaks consumers
Alias and deprecateSafe migrationTemporary duplicated surface
Never renameNo migrationMisleading API persists

Failure Mode

A scheduled job in another repository fails at runtime because it imported the old name.

Required Artifact

Write a deprecation plan with old API, new API, compatibility window, affected consumers, and removal criteria.

Project / Capstone Connection

Use this when cleaning names in shared modules during the final design review.


Source Map

SourceUse it for
Refactoring catalog - Martin FowlerNaming refactoring moves and sequencing behavior-preserving changes.
Google Engineering Practices: Small CLsKeeping risky changes reviewable and easy to roll back.