Skip to content

Code Quality and Refactoring

Preface

Is it enough that your code just works? You may have written code like this: the feature works, but two weeks later even you can't understand it. Or someone on the team left behind a pile of "code that only God and they can understand."

This chapter helps you understand what good code is, how to identify bad code, and how to safely improve it.

What will you learn in this article?

ChapterContentCore Concepts
Chapter 1Code SmellsIdentifying common problems
Chapter 2Refactoring TechniquesSafely improving code
Chapter 3Code ReviewQuality assurance in team collaboration
Chapter 4Quality MetricsMeasuring code health with data

After reading this chapter, you will be able to identify code problems, refactor safely, and continuously improve code quality through team collaboration.


0. The Big Picture: The Lifecycle of Code

In software development, there is an often-overlooked fact: code is read far more times than it is written.

A piece of code, from creation to retirement, roughly goes through this journey:

The Life of Code

  • Writing Phase: A developer writes the first implementation. The feature works, tests pass.
  • Review Phase: Team members read the code and suggest improvements.
  • Maintenance Phase: Fixing bugs, adding features, adapting to new requirements — this phase accounts for over 80% of the code's lifecycle.
  • Refactoring Phase: When code becomes hard to maintain, the internal structure needs to be improved without changing external behavior.
  • Retirement Phase: Technology evolves, and old code is replaced by new solutions.

Martin Fowler once said in Refactoring: "Any fool can write code that a computer can understand. Good programmers write code that humans can understand."


1. Code Smells: Identifying Common Problems

1.1 What Are Code Smells?

The concept of "Code Smell" was proposed by Kent Beck. It refers to characteristics in code that are not bugs but suggest deeper design problems. It's like a strange odor in a room — it won't make you sick immediately, but it signals that something needs cleaning.

Use the interactive component below to identify some of the most common code smells:

Code Smell Detector - click to switch examples
Problem code
function processOrder(order) {
  // Validate order... (20 lines)
  // Calculate price... (15 lines)
  // Check inventory... (10 lines)
  // Send notification... (15 lines)
  // Update database... (10 lines)
  // Generate report... (10 lines)
  // 80+ lines in total!
}

📏 Long function

A function exceeds 50 lines, does too many things, and becomes hard to understand and test.

Improvement:Split the large function into focused functions such as validateOrder(), calculatePrice(), and checkInventory().

1.2 Common Code Smells Checklist

Code SmellSymptomHarm
Long MethodFunction exceeds 50 linesHard to understand, test, and reuse
Magic NumbersWriting 86400000 directly in codeUnclear meaning, easy to miss when modifying
Duplicated CodeSimilar logic in multiple placesMust sync changes everywhere, easy to miss
Deep NestingMore than 3 levels of if/forLogic like a maze, hard to follow
Long Parameter ListFunction has more than 4 parametersDifficult to call, easy to mix up order
God ClassOne class/module does too muchUnclear responsibilities, change one thing and everything breaks

Key Insight

Code smells are not "errors" — they are "signals." They tell you: the design here may need improvement. Not all smells need to be fixed immediately, but you need the ability to recognize them.


2. Refactoring Techniques: Safely Improving Code

2.1 What Is Refactoring?

The definition of Refactoring is precise: improving the internal structure of code without changing its external behavior.

The key phrase is "without changing external behavior." Refactoring is not rewriting, not adding features, not fixing bugs. It is the "organizing and tidying up" of code internals.

Use the component below to compare the before and after of common refactoring techniques:

Refactoring technique comparison - choose a technique to compare before and after
Extract Function: move a code block out of a large function into a clearly named new function.
Before
function printReport(invoice) {
  console.log("=== Invoice ===")
  // Calculate total
  let total = 0
  for (let item of invoice.items) {
    total += item.price * item.qty
  }
  console.log(`Total: ${total}`)
}
After
function printReport(invoice) {
  console.log("=== Invoice ===")
  const total = calcTotal(invoice.items)
  console.log(`Total: ${total}`)
}

function calcTotal(items) {
  return items.reduce(
    (s, i) => s + i.price * i.qty, 0
  )
}
Key point:Extract Function is one of the most common refactorings. A good function name is the best comment.

2.2 Common Refactoring Techniques

Extract Function

This is the most commonly used refactoring technique. When a piece of code can be summarized with a meaningful name, it should be extracted into a function.

javascript
// Before refactoring
function printReport(data) {
  // Calculate total price
  let total = 0
  for (const item of data.items) {
    total += item.price * item.qty
  }
  // Print...
}

// After refactoring
function calculateTotal(items) {
  return items.reduce((sum, item) => sum + item.price * item.qty, 0)
}

function printReport(data) {
  const total = calculateTotal(data.items)
  // Print...
}

Rename

Good naming is the cheapest and most effective documentation. When you need to write a comment to explain what a variable or function means, its name is not good enough.

javascript
// Before refactoring
const d = new Date() - startTime  // Elapsed time
const arr = users.filter(u => u.a) // Active users

// After refactoring
const elapsedMs = new Date() - startTime
const activeUsers = users.filter(user => user.isActive)

Replace Nested Conditional with Guard Clauses

javascript
// Before refactoring
function getPayAmount(employee) {
  if (employee.isSeparated) {
    return { amount: 0 }
  } else {
    if (employee.isRetired) {
      return { amount: employee.pension }
    } else {
      return { amount: employee.salary }
    }
  }
}

// After refactoring
function getPayAmount(employee) {
  if (employee.isSeparated) return { amount: 0 }
  if (employee.isRetired) return { amount: employee.pension }
  return { amount: employee.salary }
}

The Safety Net for Refactoring

The biggest risk of refactoring is "introducing bugs while making changes." So the prerequisite for refactoring is having test coverage. Run tests after each small refactoring step to ensure behavior hasn't changed. For code without tests, add tests first before refactoring.


3. Code Review: Quality Assurance in Team Collaboration

3.1 Why Code Review?

Code Review is one of the most effective quality assurance methods in a team. Its value goes beyond finding bugs:

  • Knowledge Sharing: Team members learn each other's code, reducing the "bus factor" (if someone gets hit by a bus, can the project continue?)
  • Consistent Style: Reviews gradually establish the team's coding standards
  • Early Detection of Design Issues: Bad architectural decisions are harder to fix than bugs
  • Mutual Learning: Reading others' code is a shortcut to improving your own programming skills

3.2 What to Review?

DimensionFocus
CorrectnessIs the logic correct? Are edge cases handled?
ReadabilityAre names clear? Is the structure easy to understand?
SecurityAre there injection risks? Is sensitive data exposed?
PerformanceAre there obvious performance issues? N+1 queries?
TestingAre there corresponding tests? Do they cover critical paths?

3.3 Review Etiquette

Good code review is a discussion about code, not criticism of people:

  • Use "we" instead of "you": "You wrote this wrong" → "Here we could consider using a guard clause"
  • Ask rather than command: "Change to const" → "Will this variable be reassigned later? If not, const would be safer"
  • Give reasons: Don't just say "not good" — explain "why it's not good" and "how to make it better"

4. Code Quality Metrics

4.1 Cyclomatic Complexity

Cyclomatic Complexity measures the number of independent paths in code. Each if, for, case, &&, || increases complexity.

ComplexityRatingRecommendation
1-10SimpleEasy to understand and test
11-20ModerateConsider splitting
21-50ComplexMust refactor
50+UnmaintainableUrgent refactoring needed

4.2 Code Coverage

Code coverage measures what proportion of code is executed by tests. Common metrics:

  • Line Coverage: Proportion of executed code lines to total lines
  • Branch Coverage: Proportion of executed conditional branches to total branches

The Coverage Trap

80% coverage does not mean good code quality. Coverage only tells you "which code hasn't been tested," not "whether the tests are meaningful." A test that only asserts expect(true).toBe(true) can increase coverage but is completely worthless.

4.3 Practical Tools

ToolPurpose
ESLintJavaScript/TypeScript static analysis
PrettierCode formatting, consistent style
SonarQubeComprehensive code quality platform
HuskyGit hooks, automatic checks before commits

5. AI-Powered: Using LLMs to Improve Code Quality

LLMs are already very practical in the code quality domain — they can serve as your "24/7 online code reviewer."

5.1 Identifying Code Smells

Prompt:

Please review the following code and identify code smells, including but not limited to:
long methods, magic numbers, duplicated code, deep nesting, long parameter lists.
For each issue, provide the specific location, description, and improvement suggestions.

[Paste your code]

5.2 Automated Refactoring

Prompt:

Please refactor the following code with these requirements:
1. Do not change external behavior
2. Use techniques like extract function, guard clauses to replace nesting
3. Improve naming, eliminate magic numbers
4. Explain the reasoning behind each refactoring step

[Paste your code]

5.3 Simulated Code Review

Prompt:

Please review this code from the perspective of a senior developer, providing feedback on these dimensions:
- Correctness: Are there logic bugs? Are edge cases handled?
- Readability: Are names clear? Is the structure easy to understand?
- Performance: Are there obvious performance issues?
- Security: Are there injection or data leakage risks?
Use a "suggestion" tone rather than "command," and provide improvement plans.

[Paste your code]

AI Usage Advice

You need to verify AI's refactoring suggestions yourself — run tests to confirm behavior hasn't changed. Treat AI as a "colleague who gives suggestions," not an "authority to trust unconditionally."


6. Summary

Looking back, we've gone from identifying problems to solving them, building a complete code quality improvement system:

  1. Identify: Learn to smell code smells, know where improvements are needed
  2. Refactor: Master safe refactoring techniques, improve in small steps under test protection
  3. Collaborate: Use code review to let the team collectively safeguard code quality
  4. Measure: Track code health with objective metrics

Final Thought

Code quality is not a one-time effort but a continuous habit. It's like keeping a room tidy — you don't wait until it's a mess to do a deep clean; you tidy up a little every day. The Boy Scout Rule says it well: leave the code cleaner than you found it.


Further Reading

  • Classic Book: Martin Fowler's Refactoring: Improving the Design of Existing Code is the bible of this field.
  • Clean Code: Robert C. Martin's Clean Code provides many practical coding principles.
  • Practical Tools: Try configuring ESLint + Prettier + Husky in your project to experience automated code quality assurance.
  • Code Review: Google's Code Review guidelines are the industry gold standard and worth studying.