Chapter 6

Refactoring and Code Quality with AI

Writing code fast is only half the job. Improving and structuring code is what makes projects sustainable. AI is extraordinarily powerful for refactoring — if you use it with the right strategy.


What Is Refactoring?

Refactoring means improving the internal structure of code without changing its external behavior. The code does the same thing before and after — but afterward it's cleaner, easier to read, easier to extend, and less likely to harbor hidden bugs.

The goals of refactoring:

Why It Matters

AI-generated code often works correctly on the first pass but has structural problems: long functions, duplicated logic, unclear naming, tangled responsibilities. Refactoring is how you transform working AI output into production-quality code. It's the step most beginners skip — and the step that separates amateur projects from professional ones.


The Beginner Trap vs. Senior Approach

Vague Request

"Improve this code"

Problems:

  • No direction — improve how?
  • AI may change behavior while "improving"
  • Results are unpredictable

Targeted Request

Refactor this component.

Goals:
- Better readability
- Remove duplicated logic
- Separate concerns
- Follow clean code principles

Explain each change.

The difference is direction. The senior approach tells AI what dimensions to improve along and asks for explanations, which ensures you understand and can verify each change.


Recognizing Code Smells

Before refactoring, you need to identify what needs improvement. Code smells are patterns that suggest deeper problems. AI is excellent at identifying these — if you ask explicitly.

Analyze this code for code smells.

For each smell you find, describe:
- What the smell is
- Where it occurs (line numbers or function names)
- Why it's a problem
- How to fix it

Don't fix anything yet — just diagnose.

Here are the most common smells AI will find:

⚠️
Long Functions
Functions over 30—40 lines that do too many things
⚠️
Deep Nesting
Multiple levels of if/else, loops inside loops
⚠️
Magic Numbers
Hardcoded values with no explanation
⚠️
Duplicated Logic
Same code pattern appearing in multiple places
🏷️
Unclear Naming
Variables like data, temp, x
⚠️
God Components
One component handling UI, logic, data, and state

Pro Tip: Diagnose Before You Operate

Always ask AI to identify problems first, fix them second. A two-step process (diagnose → fix) gives you a chance to prioritize which smells matter most, decide which to fix now vs. later, and ensure AI doesn't change behavior while restructuring. Rushing straight to "refactor this" often produces changes you didn't want.


The Refactoring Workflow

Safe, effective refactoring follows a disciplined process. Each step has a clear purpose, and AI plays a different role at each stage.

1
Show AI the code — Paste the function, component, or module you want to refactor. Include any relevant types or interfaces.
2
Ask AI to identify problems — "What code smells exist here? What would a senior developer improve?" Get the diagnosis first.
3
Choose what to improve — You decide which improvements to make. Not every smell needs fixing right now. Prioritize by impact.
4
Implement changes one at a time — "Extract the validation logic into a separate function" is better than "fix everything." Test between each change.

Clean Code Principles with AI

Three foundational clean code principles translate directly into refactoring prompts. Asking AI to apply these explicitly produces consistently better results than vague "improve" requests.

SRP

Single Responsibility

Every function and component should do exactly one thing. If you need "and" to describe it, split it.

DRY

Don't Repeat Yourself

Duplicated logic should be extracted into shared functions. Same pattern twice means extract once.

KISS

Keep It Simple

The simplest solution that works is usually the best. Complexity is the enemy of maintainability.

Refactor this component by applying:

1. Single Responsibility — break apart any function
   that does more than one thing
2. DRY — extract duplicated logic into shared helpers
3. KISS — simplify any unnecessarily complex patterns

For each change, explain which principle it applies
and why the new version is better.

Before and After: Real Refactoring

To make refactoring concrete, here's a typical before/after example. This is the kind of transformation AI can perform when given clear direction.

Before — God Function
After — Clean Separation
function handleSubmit(data) {
  // Validate
  if (!data.name) {
    setError('Name required');
    return;
  }
  if (!data.time) {
    setError('Time required');
    return;
  }
  if (!data.day) {
    setError('Day required');
    return;
  }

  // Transform
  const activity = {
    id: Date.now(),
    name: data.name.trim(),
    time: data.time,
    day: data.day,
    member: data.member || 'All',
    color: data.color || '#3b82f6',
    created: new Date(),
  };

  // Save
  setActivities(prev =>
    [...prev, activity]);
  setFormData({});
  setIsOpen(false);
  setError(null);
}
function validateActivity(data) {
  if (!data.name) return 'Name required';
  if (!data.time) return 'Time required';
  if (!data.day)  return 'Day required';
  return null;
}

function createActivity(data) {
  return {
    id: crypto.randomUUID(),
    name: data.name.trim(),
    time: data.time,
    day: data.day,
    member: data.member ?? 'All',
    color: data.color ?? '#3b82f6',
    created: new Date(),
  };
}

function handleSubmit(data) {
  const error = validateActivity(data);
  if (error) {
    setError(error);
    return;
  }

  const activity = createActivity(data);
  setActivities(prev =>
    [...prev, activity]);
  resetForm();
}

function resetForm() {
  setFormData({});
  setIsOpen(false);
  setError(null);
}

The "after" version applies all three principles: validation is a separate function (SRP), the activity creation logic is extracted and reusable (DRY), and each function is short and obvious (KISS). The behavior is identical — but the code is dramatically easier to read, test, and modify.


Refactoring Legacy Code

AI is particularly valuable when dealing with old, inherited code that nobody fully understands. Legacy codebases are intimidating — and AI's ability to analyze unfamiliar code quickly makes it an ideal partner for modernization.

This is legacy code that I didn't write.

[paste code]

Help me understand what it does, then refactor it
with these constraints:
- Do NOT change the external behavior
- Modernize syntax (ES6+, optional chaining, etc.)
- Improve naming so the intent is clear
- Add brief comments where the logic is non-obvious

Explain each change so I understand the code better.

The constraint "do NOT change external behavior" is critical with legacy code. Any refactor of code you don't fully understand should preserve behavior as its top priority. AI can also help by explaining what obscure legacy patterns were trying to accomplish, translating old idioms into modern equivalents.


Design Patterns with AI

Sometimes code doesn't just need cleanup — it needs restructuring around a better pattern. AI is a strong advisor here, suggesting design patterns that fit your specific situation.

Here is my current code structure:

[paste relevant code]

Is there a design pattern that would improve this?
Consider:
- Current pain points (hard to extend, lots of conditionals)
- My tech stack (React, TypeScript)
- Simplicity (don't over-engineer)

If you suggest a pattern, show a before/after example
using my actual code — not a generic textbook example.

The key phrase is "using my actual code." Generic pattern examples are easy to find anywhere. What's valuable is seeing how a pattern applies to your specific situation, which AI can provide because you've given it your actual code context.

Factory Pattern

When you have complex object creation logic scattered across your codebase.

Observer Pattern

When multiple parts of your app need to react to state changes.

Custom Hooks

When React components share stateful logic that should be extracted.

Composition

When inheritance hierarchies become brittle — compose behaviors instead.


Performance Refactoring

Performance refactoring is a specialized category where you're not changing what code does, but how efficiently it does it. AI can identify common performance anti-patterns, though you should always profile before optimizing.

Analyze this code for performance issues.

Focus on:
- Unnecessary re-renders (React-specific)
- Expensive computations that could be memoized
- N+1 patterns in data processing
- Large bundle size contributors

For each issue, estimate the impact (low/medium/high)
so I can prioritize.

Pro Tip: Profile First, Optimize Second

Don't ask AI to optimize code that isn't actually slow. Use browser dev tools, React Profiler, or Lighthouse to identify actual bottlenecks first. Then bring those specific areas to AI. Premature optimization guided by AI is still premature optimization — it adds complexity without measurable benefit.


Safe Refactoring Checklist

Refactoring changes code structure, which means it can introduce subtle bugs even when you're trying to improve things. This checklist keeps you safe.

Before Every Refactor

🧪 Practical Exercise

Find the largest component or function in one of your projects — the one that makes you wince a little when you open it. Then run through the full refactoring workflow:


Key Takeaways

Previous Chapter Debugging with AI
Next Chapter AI and System Design