JAVASCRIPT • SOFTWARE CRAFT AND COMMUNICATION

Refactoring — Refactor to reduce duplication and improve clarity

Learn to reshape working code so it becomes easier to read, maintain, and extend without changing what it does.

Historical Context & Motivation

Every programmer eventually faces the same frustrating moment: you look at code you wrote last week, and you can barely understand it. Worse, you notice you copied and pasted the same block of logic in three different places. When early software teams in the 1970s and 1980s started building larger programs, this kind of mess became a serious obstacle. Projects slowed down, bugs multiplied, and developers spent more time reading old code than writing new features.

The idea of refactoring — restructuring existing code without changing its external behavior — grew out of that frustration. Pioneering computer scientists realized that code quality is not just about getting the right answer once; it is about making sure the code stays understandable and changeable over time. The timeline below traces how refactoring went from an informal habit to a recognized discipline.

1984
Structured Programming Takes Hold
Languages like C and Pascal encourage breaking programs into small functions, planting the seed for eliminating duplicated logic.
1992
Ward Cunningham & the Wiki Way
Smalltalk developers like Ward Cunningham and Kent Beck begin sharing techniques for cleaning up code in small, safe steps, coining the term 'refactoring.'
1999
Martin Fowler Publishes 'Refactoring'
Fowler's book catalogs dozens of named refactoring moves — like Extract Function and Rename Variable — turning informal cleanup into a repeatable craft.
2015
Modern JavaScript & ES6
Arrow functions, template literals, destructuring, and modules give JavaScript developers powerful new tools for writing cleaner, less duplicated code.
2020s
AI-Assisted Refactoring
IDEs and AI tools now suggest refactoring moves automatically, but understanding the 'why' behind each change remains a uniquely human skill.

The central question refactoring answers is straightforward: how do you improve the internal structure of code that already works, so it stays correct while becoming easier to read and change? That is exactly what this lesson will teach you to do.

Core Principles of Refactoring

Before you start rearranging code, you need a mental framework. Refactoring rests on a handful of guiding ideas that keep you safe and productive. These principles apply whether you are cleaning up a ten-line script or a thousand-line project.

1

Behavior Stays the Same

Refactoring changes how the code is organized, never what it produces. If a function returned 42 before, it still returns 42 after.
2

DRY — Don't Repeat Yourself

Every piece of knowledge should live in exactly one place. Duplicated code means duplicated bugs and duplicated maintenance.
3

Small, Safe Steps

Make one tiny change, then test. This way you always know exactly which edit introduced a problem if something breaks.
4

Meaningful Names

Variables and functions should say what they mean. Renaming x to studentAge is a refactoring move that costs nothing and pays back every time someone reads the code.
5

Single Responsibility

Each function should do one thing well. If you struggle to name a function, it is probably doing too much.
✦ KEY TAKEAWAY
Think of refactoring like reorganizing your locker. Everything you own stays the same — same books, same supplies — but after reorganizing, you can find what you need in seconds instead of digging through a pile. Refactoring reorganizes your code's 'locker' so future-you (and your teammates) can work faster.

Visualizing Duplication and Extraction

The diagram below shows the most common refactoring move you will use: Extract Function. On the left, three different places in a program contain the same duplicated logic (shown in matching colors). On the right, that logic has been pulled into a single shared function, and each original location now calls that function instead. Notice how the total amount of unique logic shrinks dramatically.

Left: three functions each contain identical logic (pink). Right: the shared logic is extracted into printLabel() (pink border), and each original function now contains a single call (cyan).

In the "before" column, every function repeats the same three lines that build and print a label. If you later need to change the label format — say, adding an email address — you would have to edit all three places and hope you do not miss one. In the "after" column, you edit the single printLabel function and the change automatically applies everywhere. This is the DRY principle in action: one change, one place, zero risk of inconsistency.

How Refactoring Works Step by Step

Refactoring is not a wild rewrite. It is a disciplined sequence of micro-changes. Each change is small enough that you can verify correctness immediately. Below is the general recipe for the Extract Function refactoring, the most fundamental move in the catalog.

  1. Identify the duplication. Look for blocks of code that appear in more than one place, or a single block that does too many things.
  2. Create a new function with a descriptive name. The name should explain what the block does, not how it does it.
  3. Move the duplicated code into the new function. Identify which values the code needs (these become parameters) and which value it produces (this becomes the return value).
  4. Replace every original occurrence with a call to the new function. Pass the appropriate arguments.
  5. Test. Run the program or your test suite to confirm the output has not changed.

Beyond Extract Function, several other common refactoring moves deserve attention. Rename Variable swaps a vague name like d for a clear one like daysSinceLastLogin. Inline Variable removes a variable that adds no clarity — if const temp = price * quantity; is used exactly once, you can replace temp with the expression itself. Replace Magic Number with Named Constant turns a mysterious 0.0825 into TAX_RATE so readers instantly know its purpose.

⚠️ The Golden Rule of Refactoring
If you can't easily test that your code still works after a change, the change was too big. Break it into smaller steps.
The safe-change cycle: spot a smell, choose a move, make a small edit, and test. If the test passes, loop back for another move. If it fails, undo and break the change into something smaller.

Recognizing Code Smells

Programmers use the term code smell to describe a surface-level indicator that something deeper might be wrong. A code smell is not a bug — your program still works — but it signals that the code could be clearer or more maintainable. Learning to recognize smells is the first step in every refactoring session.

Common code smells and the refactoring moves that address them.
Code SmellWhat It Looks LikeRefactoring Fix
Duplicated CodeThe same (or nearly the same) block appears in multiple places.Extract Function — move shared logic into one reusable function.
Magic NumbersLiteral values like 3.14 or 60 sit inside expressions with no explanation.Replace Magic Number with Named Constant.
Long FunctionA function stretches across many lines and does several unrelated tasks.Extract Function — split into smaller, focused helpers.
Unclear NamingVariables or functions have names like x, temp, or doStuff.Rename Variable / Rename Function.
Dead CodeLines that are never executed or variables that are never read.Delete the unreachable code safely.
💡 Pro Tip
A quick test for duplication: select a block of code and use your editor's search feature. If the same logic appears elsewhere, you have found a candidate for Extract Function.

Worked Example — Refactoring a Grade Calculator

Imagine you have a JavaScript program that calculates letter grades for three different classes. The original code repeats the grading logic inside each function. We will walk through refactoring it step by step.

Original Code (Before Refactoring)

Here is the messy starting point. Notice that the grading if/else block is copied three times:

function getMathGrade(score) { if (score >= 90) return 'A'; else if (score >= 80) return 'B'; else if (score >= 70) return 'C'; else return 'F'; } function getScienceGrade(score) { if (score >= 90) return 'A'; else if (score >= 80) return 'B'; else if (score >= 70) return 'C'; else return 'F'; } function getHistoryGrade(score) { if (score >= 90) return 'A'; else if (score >= 80) return 'B'; else if (score >= 70) return 'C'; else return 'F'; }

Refactoring the Grade Calculator
1
Step 1 — Identify the DuplicationAll three functions contain the exact same if/else chain. The only difference is the function name — the parameter score and the return values are identical.
Smell detected: Duplicated Code (×3)
2
Step 2 — Create a Shared FunctionWe create a single function called getLetterGrade that accepts a numeric score and returns the corresponding letter. The name clearly describes what the function does.
function getLetterGrade(score) { if (score >= 90) return 'A'; if (score >= 80) return 'B'; if (score >= 70) return 'C'; return 'F'; }
3
Step 3 — Replace Original CallsEvery place that previously held the duplicated logic now simply calls getLetterGrade. If we still want subject-specific wrappers (for example, to add logging), they become one-liners.
function getMathGrade(score) { return getLetterGrade(score); } function getScienceGrade(score) { return getLetterGrade(score); } function getHistoryGrade(score) { return getLetterGrade(score); }
4
Step 4 — TestWe run the same test inputs as before. getMathGrade(92) still returns 'A'. getScienceGrade(74) still returns 'C'. Nothing has changed in behavior — only in structure.
All tests pass ✓ — Refactoring complete.
5
Step 5 — Bonus: Remove Unnecessary WrappersSince the three wrapper functions add no unique logic, we can go further and simply call getLetterGrade directly wherever we need a grade. This reduces the codebase from ~18 lines to ~5 lines.
Final version: 1 function, 0 duplication, maximum clarity.

Strengths & Limitations of Refactoring

Refactoring is powerful, but like any technique, it comes with trade-offs. Understanding both sides helps you decide when to refactor and when to leave working code alone.

Balancing the benefits and risks of refactoring.
StrengthsLimitations
Reduces bugs by eliminating duplicated logic — fix once, fixed everywhere.Takes time upfront that could be spent adding new features.
Makes code easier for teammates (and future-you) to read and understand.Without tests, you risk accidentally changing behavior.
Makes future changes faster because logic lives in well-named, single-purpose functions.Over-refactoring can create too many tiny functions, making the code harder to follow.
Builds your skill at recognizing patterns and thinking about code architecture.Renaming variables or restructuring can cause merge conflicts when working on a team.
✦ KEY TAKEAWAY
Think of refactoring like tuning a car engine. A well-tuned engine does not go to a new destination — it gets you to the same destination more smoothly and efficiently. But if you overtighten a bolt, you can create new problems. Refactor in small, tested steps and stop when the code reads clearly.

Connection to Advanced Software Design

The refactoring moves you have learned here — Extract Function, Rename Variable, Replace Magic Number — are the foundational vocabulary of a much larger discipline called software design. As you advance, you will encounter more sophisticated patterns that build directly on these basics.

How today's refactoring skills connect to professional software practices.
What You Learned TodayWhere It Leads
Extract Function to remove duplicationDesign Patterns — reusable solutions to common coding problems (Strategy, Factory, Observer).
Meaningful namingClean Code principles — entire books and courses on writing expressive, self-documenting code.
Single Responsibility per functionSOLID Principles — five design guidelines used in professional software engineering.
Test after every changeTest-Driven Development (TDD) — writing tests first, then writing code to pass them.

Professional developers refactor constantly — not as a separate task, but as part of their daily workflow. Every time you add a feature, you clean up the code around it. This habit, sometimes called the Boy Scout Rule ("leave the code cleaner than you found it"), is what separates good codebases from unmaintainable ones. The skills you are building now are the same ones used at companies like Google, Microsoft, and every open-source project on GitHub.

Practice Problems

PROBLEM 1 — CONCEPTUAL
In your own words, explain what it means to say that refactoring does not change a program's behavior. Why is this distinction important?
PROBLEM 2 — BASIC CALCULATION
Consider these two functions. Identify the duplicated code and write a single shared function that eliminates the duplication. function areaOfSquare(side) { const result = side * side; console.log('Area: ' + result); return result; } function areaOfCircle(radius) { const result = Math.PI * radius * radius; console.log('Area: ' + result); return result; }
PROBLEM 3 — INTERMEDIATE
Refactor the following code to eliminate the magic numbers and improve variable names. Explain each change you make. function calc(p, q) { let t = p * q; let t2 = t * 0.0825; let t3 = t + t2; if (t3 > 100) { t3 = t3 * 0.9; } return t3; }
PROBLEM 4 — APPLIED
You are building a student dashboard. Three different parts of the app display a student's full name by concatenating firstName, a space, and lastName. Later, your teacher asks you to display names in "Last, First" format instead. Explain how having a single formatName function (instead of duplicated code) makes this change safer and faster.
PROBLEM 5 — CRITICAL THINKING
A classmate argues: "I should refactor my code to be as short as possible — fewer lines always means better code." Do you agree or disagree? Write a paragraph explaining your reasoning and provide a code example where making the code shorter actually makes it harder to understand.

Lesson Summary

Refactoring is the practice of restructuring existing code without changing its external behavior. The core motivation is to eliminate duplication and improve clarity so that code is easier to read, maintain, and extend. You learned to spot code smells — surface indicators like duplicated logic, magic numbers, long functions, and unclear names — and to address them with targeted moves such as Extract Function, Rename Variable, and Replace Magic Number with Named Constant.

The key discipline is to work in small, safe steps — make one change, test, repeat — following the DRY principle (Don't Repeat Yourself) and the Single Responsibility principle. These foundational skills connect directly to professional practices like design patterns, SOLID principles, and test-driven development. Remember: the goal is not the shortest code, but the clearest code.

Varsity Tutors • JavaScript • Refactoring — Refactor to reduce duplication and improve clarity