COMPUTER PROGRAMMING 1 (JAVASCRIPT) • DEBUGGING, TESTING & CODE QUALITY

Refactoring for Readability — I can refactor code for readability (names, functions, formatting) without changing behavior.

Transform messy code into clean, understandable programs that work exactly the same way.

Historical Context & Motivation

In the early days of programming, code was written primarily for machines. Programmers punched instructions into cards and fed them into massive computers that filled entire rooms. Readability was an afterthought because humans rarely needed to revisit their code after it ran. As software grew larger and teams started collaborating, developers realized a painful truth: code is read far more often than it is written. A single function might be written once but read dozens or even hundreds of times by different people trying to fix bugs, add features, or understand how a system works.

This realization gave rise to refactoring — the practice of restructuring existing code without changing its external behavior. The term was popularized in the late 1990s, but the underlying idea is simple: clean up your code so that the next person (or future you) can understand it quickly. Think of it like rewriting a confusing essay so the same ideas come through more clearly.

1972
Structured Programming Movement
Edsger Dijkstra argued that clear code structure reduces bugs. The idea that code should be organized for human understanding — not just machine execution — began to take hold.
1991
William Opdyke's PhD Thesis
Opdyke formally defined refactoring as behavior-preserving code transformations in his doctoral dissertation, giving the practice an academic foundation.
1999
Martin Fowler's Refactoring Book
Fowler published "Refactoring: Improving the Design of Existing Code," which became the definitive guide and introduced a catalog of named refactoring techniques to mainstream developers.
2015
ES6 and Modern JavaScript
JavaScript received major syntax improvements like arrow functions, template literals, and const/let. These tools gave developers new ways to write cleaner, more readable code.

The central question that refactoring addresses is this: How do we improve the quality and clarity of code that already works, without accidentally breaking it? This lesson will teach you three powerful refactoring strategies — better names, extracted functions, and consistent formatting — and show you how to apply them in JavaScript.

Core Principles of Refactoring for Readability

Refactoring for readability rests on a few foundational ideas. Each principle targets a different aspect of what makes code hard to understand, and together they form a complete toolkit for cleaning up messy programs. The golden rule underlying all of them is that the behavior of your program must remain identical before and after refactoring. You are reorganizing, not rewriting logic.

1

Meaningful Names

Variables, functions, and parameters should describe what they hold or what they do. Replace cryptic names like x or d with descriptive names like studentAge or calculateTotal.
2

Extract Functions

When a block of code does one specific task, pull it out into its own function with a descriptive name. This replaces a confusing chunk with a single, readable function call. Each function should do one thing well.
3

Consistent Formatting

Use consistent indentation, spacing, and brace placement throughout your code. Formatting acts like punctuation in English — it helps readers parse structure at a glance without having to think about it.
4

Behavior Preservation

After every refactoring change, the program's output, return values, and side effects must remain exactly the same. Test your code after each small change to make sure nothing broke.
✦ KEY TAKEAWAY
Think of refactoring like cleaning your room. Everything stays in your room — you don't throw anything away or buy anything new. You just organize it so you can actually find what you need. Your room (the program) has the same stuff (behavior), but now it makes sense to anyone who walks in.

Visual Explanation — Before vs. After Refactoring

The following diagram illustrates how the same program logic looks before and after refactoring. On the left, you see a single long function with cryptic variable names and tangled logic. On the right, the same behavior is split into clearly named functions with descriptive variables. Notice that both sides produce the exact same output — only the organization and naming have changed.

Both versions produce identical output. The left side uses vague names and a single bloated function. The right side uses descriptive names, named constants, and extracted functions that each do one thing.

Look at the left side of the diagram and imagine you encounter this code for the first time. You would have to read every line, trace the math, and guess what a, b, and c represent. Now look at the right side — the function names tell you exactly what each step does. You can read addTax(getSubtotal(price, qty)) almost like an English sentence. That is the power of refactoring.

How Refactoring Works — The Three Techniques

Technique 1: Rename Variables and Functions

The simplest refactoring technique is renaming. A good name answers the question "what is this?" or "what does this do?" without requiring the reader to look at surrounding code. In JavaScript, the convention is to use camelCase for variables and functions (e.g., totalPrice) and UPPER_SNAKE_CASE for constants (e.g., MAX_RETRIES). Avoid single-letter names except for simple loop counters like i or j.

Examples of renaming variables for clarity
Bad NameGood NameWhy It's Better
delapsedDaysReveals that the value represents a time duration in days
temptemperatureFahrenheitSpecifies what is being measured and the unit
fnformatUserNameDescribes the action the function performs
liststudentGradesIndicates what the array contains, not just that it's a list
flagisLoggedInBoolean names starting with is/has/can read like yes/no questions

Technique 2: Extract Functions

When you notice a block of code inside a larger function that performs one distinct task, you can extract it into its own named function. The original code is replaced with a function call. This achieves two things: the extracted function gets a descriptive name that documents what it does, and the original function becomes shorter and easier to follow. A good rule of thumb is that if you need to write a comment explaining what a block of code does, that block should probably be its own function — the function name replaces the comment.

Technique 3: Consistent Formatting

Formatting refers to indentation, spacing around operators, placement of curly braces, and blank lines between sections. Inconsistent formatting forces your brain to constantly re-adjust, like reading a book where the font size changes every paragraph. In JavaScript, common conventions include using 2 spaces per indentation level, placing opening braces on the same line as the statement, and adding blank lines between function definitions. Tools like Prettier can automatically format your code, but understanding the principles behind formatting helps you write cleaner code from the start.

💡 The Rule of Three
If you find yourself copying and pasting the same block of code three or more times, that's a strong signal to extract it into a function. This is sometimes called the DRY principle — Don't Repeat Yourself.

Recognizing Code Smells — What Needs Refactoring?

Developers use the term code smell to describe patterns in code that suggest something could be improved. A code smell doesn't mean the code is broken — it runs fine — but it hints that the code will be hard to understand, maintain, or extend. Learning to recognize these smells is like developing a spidey-sense for code quality.

Six common code smells and their corresponding fixes. The bottom panel shows the refactoring safety process: identify, change, and test in small increments.

The key insight from this diagram is the refactoring safety net at the bottom. You should never refactor everything at once. Instead, make one small change, test that the output hasn't changed, and only then move on to the next improvement. If something breaks, you only need to undo the last small change rather than trying to untangle a massive rewrite. This discipline is what separates refactoring from rewriting.

Worked Example — Refactoring a Grade Calculator

Let's walk through a complete refactoring of a JavaScript function that calculates a student's letter grade from an array of scores. We'll apply all three techniques — renaming, extracting functions, and formatting — one step at a time, verifying behavior after each change.

Refactoring a Grade Calculator
1
Step 1 — Examine the Original CodeHere is the original, hard-to-read function: function g(s){let t=0;for(let i=0;i<s.length;i++){t+=s[i];}let a=t/s.length;if(a>=90){return 'A';}else if(a>=80){return 'B';}else if(a>=70){return 'C';}else if(a>=60){return 'D';}else{return 'F';}} This works, but the function name g and variables s, t, and a tell us nothing about what's happening.
2
Step 2 — Rename Variables and the FunctionReplace every cryptic name with a descriptive one. g becomes getLetterGrade, s becomes scores, t becomes totalPoints, and a becomes average. Test: calling getLetterGrade([85, 92, 78]) still returns 'B'. ✓
All names now describe their purpose. Behavior unchanged. ✓
3
Step 3 — Apply Consistent FormattingAdd proper indentation (2 spaces), put each statement on its own line, and add blank lines between logical sections. Place opening braces on the same line as the keyword. The code is now visually scannable: function getLetterGrade(scores) { let totalPoints = 0; for (let i = 0; i < scores.length; i++) { totalPoints += scores[i]; } let average = totalPoints / scores.length; if (average >= 90) { return 'A'; } else if (average >= 80) { return 'B'; } else if (average >= 70) { return 'C'; } else if (average >= 60) { return 'D'; } else { return 'F'; } } Test again — same output. ✓
Formatting is consistent throughout. Behavior unchanged. ✓
4
Step 4 — Extract Helper FunctionsThe function does two distinct jobs: computing the average and converting the average to a letter. Extract each into its own function: function calculateAverage(scores) { let totalPoints = 0; for (let i = 0; i < scores.length; i++) { totalPoints += scores[i]; } return totalPoints / scores.length; } function convertToLetter(average) { if (average >= 90) return 'A'; if (average >= 80) return 'B'; if (average >= 70) return 'C'; if (average >= 60) return 'D'; return 'F'; } function getLetterGrade(scores) { const average = calculateAverage(scores); return convertToLetter(average); } The main function is now just two lines that read like plain English. Each helper function can also be tested independently.
Final result: three small, readable, single-purpose functions that produce identical output to the original.
👀 Did You Notice?
We also changed let average to const average in the final version because the value is never reassigned. Using const signals to readers that this value won't change, which is another small readability win.

Benefits and Limitations of Refactoring

Refactoring is one of the most valuable skills a programmer can develop, but like any tool it comes with trade-offs. Understanding both the benefits and the limitations helps you decide when and how much to refactor.

Benefits vs. limitations of refactoring for readability
BenefitsLimitations / Risks
Easier to understand — new team members or your future self can read the code quicklyTakes time — refactoring doesn't add new features, so it can feel unproductive in the short term
Easier to debug — small, named functions make it clear where a bug livesRisk of introducing bugs — even careful changes can break something if you skip testing
Easier to extend — adding a feature is simpler when the existing code is well-organizedOver-refactoring — splitting code into too many tiny functions can make it harder to follow
Reduces duplication — shared functions replace copied code, so fixes only need to happen onceSubjective naming — reasonable people can disagree about the 'best' name for a variable or function
✦ KEY TAKEAWAY
Think of refactoring like tuning a guitar between songs. It takes a moment, and the audience might not notice the difference directly. But if you skip it, every song that follows will sound slightly off. Regular, small refactoring keeps your codebase 'in tune' so that future work stays fast and reliable.

Connection to Advanced Practices

The refactoring skills you're learning now — renaming, extracting functions, and formatting — are the foundation for more advanced software engineering practices that you'll encounter in future courses and professional work. Understanding where these basics lead can motivate you to build strong habits now.

How basic refactoring skills connect to professional practices
What You Learn NowWhere It LeadsWhy It Matters
Rename variables and functionsDesign patterns and API naming conventionsProfessional codebases have strict naming standards that entire teams follow
Extract functions for single tasksSingle Responsibility Principle (SOLID)In advanced OOP, every class and module should have one reason to change
Format consistentlyLinters (ESLint) and formatters (Prettier)Industry teams use automated tools that enforce formatting rules across thousands of files
Test after each changeTest-Driven Development (TDD) and CI/CDProfessional teams write automated tests that run every time code changes

As you progress in computer science, you'll also encounter automated refactoring tools built into code editors like VS Code. These tools can rename a variable everywhere it appears with a single shortcut (F2 in VS Code), extract a selection into a function automatically, and flag code smells with colored underlines. Learning to refactor manually first gives you the judgment to know when these tools are making the right suggestions and when they're not.

Practice Problems

PROBLEM 1 — CONCEPTUAL
A classmate says, "I refactored my program and now it does something different than before." Explain why what they did is not actually refactoring, and describe what they should have done differently.
PROBLEM 2 — BASIC CALCULATION
Refactor the following variable names to be more descriptive. Do not change any logic: let a = 15; let b = a * 4; let c = b > 50; Provide your renamed version and explain each name choice.
PROBLEM 3 — INTERMEDIATE
The following function works correctly but is hard to read. Refactor it using all three techniques (rename, extract functions, format). Show your final code. function p(arr){let r=[];for(let i=0;i<arr.length;i++){if(arr[i]%2===0){r.push(arr[i]*arr[i])}}return r}
PROBLEM 4 — APPLIED
You are working on a team project and a teammate writes this code to calculate a shipping cost: function s(w, z) { let x = 0; if (w <= 5) { x = w * 2.5; } else { x = w * 4.0; } if (z) { x = x + 15; } return x; } Refactor this code so the team can understand it. Explain what each magic number probably represents and how you decided on the names.
PROBLEM 5 — CRITICAL THINKING
Consider this scenario: you extract a block of code into a new function, but the block modifies two different variables that the rest of the program relies on. Explain why this makes extraction tricky, and propose a strategy for handling it while still preserving behavior. Use a brief code example to support your answer.

Lesson Summary

Refactoring means restructuring existing code to improve its clarity without changing what the program does. The three core techniques are meaningful naming (replacing cryptic identifiers like x with descriptive ones like studentAge), extracting functions (pulling distinct tasks into their own named functions so each one does one thing well), and consistent formatting (using uniform indentation, spacing, and brace placement throughout your code).

The golden rule of refactoring is behavior preservation — the same inputs must produce the same outputs before and after every change. Recognize code smells like mystery names, long functions, magic numbers, and duplicated code as signals that refactoring is needed. Always work in small steps and test after each change to maintain the refactoring safety net. These foundational habits connect directly to professional practices like design patterns, linters, and test-driven development.

Varsity Tutors • Computer Programming 1 (JavaScript) • Refactoring for Readability