Back to Blog

Understanding Code Smells: Bloaters and How to Refactor Them

February 17, 202617 min read14 views
# Refactoring Code Smells: Understanding and Fixing Bloaters (React + FAANG-Level Depth)

Clean code is not about writing less code — it’s about writing maintainable, scalable, and readable code. One of the most common categories of code smells is **Bloaters**.

In this deep-dive guide, we’ll:

- Understand what Bloaters are  
- Explore common bloater smells  
- Refactor them using JavaScript / TypeScript  
- Apply improvements in React  
- Discuss interview-level insights  
- Approach the topic with FAANG-level engineering thinking  

---

# What Are Code Smells?

Code smells are structural indicators in code that suggest deeper problems. They don’t necessarily break functionality, but they:

- Increase cognitive load  
- Slow down development  
- Increase bug probability  
- Accumulate technical debt  

Refactoring removes these smells without changing external behavior.

---

# What Are Bloaters?

Bloaters are code smells that grow excessively large over time. They make systems rigid and harder to modify.

Common types:

1. Long Method  
2. Large Class  
3. Primitive Obsession  
4. Long Parameter List  
5. Data Clumps  

Let’s break them down with real examples.

---

# 1️⃣ Long Method

## ❌ Problem

A method that tries to do too much.

### Bad Example (React + TypeScript)

```tsx
const handleCheckout = async () => {
  if (!cart.length) {
    alert("Cart is empty");
    return;
  }

  let total = 0;
  cart.forEach(item => {
    total += item.price * item.quantity;
  });

  if (user.isPremium) {
    total *= 0.9;
  }

  try {
    const response = await fetch("/api/checkout", {
      method: "POST",
      body: JSON.stringify({ total }),
    });

    const data = await response.json();

    if (data.success) {
      setOrderComplete(true);
    } else {
      alert("Payment failed");
    }
  } catch (error) {
    console.error(error);
  }
};

🚨 Why This Is a Bloater

  • Multiple responsibilities
  • Hard to test
  • Hard to reuse logic
  • Hard to maintain

✅ Refactored Version

const calculateTotal = (cart: CartItem[], isPremium: boolean): number => {
  const subtotal = cart.reduce(
    (sum, item) => sum + item.price * item.quantity,
    0
  );
  return isPremium ? subtotal * 0.9 : subtotal;
};

const processPayment = async (total: number) => {
  const response = await fetch("/api/checkout", {
    method: "POST",
    body: JSON.stringify({ total }),
  });

  return response.json();
};

const handleCheckout = async () => {
  if (!cart.length) return alert("Cart is empty");

  const total = calculateTotal(cart, user.isPremium);

  try {
    const data = await processPayment(total);
    setOrderComplete(data.success);
  } catch (error) {
    console.error("Payment error:", error);
  }
};

🎯 Improvements

  • Single Responsibility Principle
  • Testable functions
  • Reusable business logic
  • Cleaner React component

2️⃣ Large Component (Large Class Equivalent in React)

❌ Problem

A React component that handles:

  • UI
  • API calls
  • Business logic
  • State management
  • Data transformation

Example:

const Dashboard = () => {
  const [users, setUsers] = useState([]);
  const [filtered, setFiltered] = useState([]);

  useEffect(() => {
    fetch("/api/users")
      .then(res => res.json())
      .then(data => {
        setUsers(data);
        setFiltered(data.filter(u => u.active));
      });
  }, []);

  const handleSort = () => {
    setFiltered([...filtered].sort((a, b) => a.name.localeCompare(b.name)));
  };

  return (
    <div>
      <button onClick={handleSort}>Sort</button>
      {filtered.map(user => (
        <div key={user.id}>{user.name}</div>
      ))}
    </div>
  );
};

✅ Refactored (FAANG-Style Separation)

Custom Hook

const useUsers = () => {
  const [users, setUsers] = useState<User[]>([]);

  useEffect(() => {
    fetch("/api/users")
      .then(res => res.json())
      .then(setUsers);
  }, []);

  return users;
};

Utility Function

const getActiveUsers = (users: User[]) =>
  users.filter(user => user.active);

Presentation Component

const Dashboard = () => {
  const users = useUsers();
  const activeUsers = getActiveUsers(users);

  return (
    <UserList users={activeUsers} />
  );
};

🎯 Result

  • Cleaner architecture
  • Logic separated
  • Better scalability
  • Easier to test

This is how large companies structure UI systems.


3️⃣ Primitive Obsession

❌ Problem

Using primitive types instead of domain models.

type Order = {
  userId: string;
  amount: number;
  currency: string;
};

This spreads logic everywhere.


✅ Replace Primitive with Value Object

class Money {
  constructor(
    public amount: number,
    public currency: string
  ) {}

  applyDiscount(percent: number) {
    return new Money(this.amount * (1 - percent), this.currency);
  }
}

Now:

const price = new Money(100, "USD");
const discounted = price.applyDiscount(0.1);

🎯 Why This Matters in Interviews

  • Shows domain modeling skills
  • Shows abstraction thinking
  • Shows clean architecture mindset

4️⃣ Long Parameter List

❌ Problem

createUser(
  name: string,
  email: string,
  age: number,
  role: string,
  isActive: boolean,
  createdAt: Date
);

Hard to read, easy to misuse.


✅ Introduce Parameter Object

type CreateUserParams = {
  name: string;
  email: string;
  age: number;
  role: string;
  isActive: boolean;
  createdAt: Date;
};

createUser(params: CreateUserParams);

Cleaner, scalable, safer.


FAANG-Level Engineering Perspective

Senior engineers look beyond syntax.

They think about:

  • Change frequency
  • Scalability
  • Team velocity
  • Testability
  • Cognitive load

Refactoring bloaters improves:

  • Code review speed
  • Onboarding time
  • Bug reduction
  • Feature delivery velocity

Interview-Focused Takeaways

If asked:

"How do you handle messy code?"

You can say:

  • Identify code smells
  • Categorize them (Bloaters, Couplers, Dispensables)
  • Apply targeted refactoring patterns
  • Ensure behavior remains unchanged
  • Add tests if missing

This shows structured thinking.


Practical Refactoring Strategy

  1. Write tests first
  2. Extract smallest safe units
  3. Refactor incrementally
  4. Avoid rewriting everything
  5. Use linting & static analysis tools

Final Thoughts

Bloaters don’t appear overnight.

They grow silently.

Refactoring is not about perfection. It’s about reducing future complexity.

In modern React applications, managing bloaters is critical for:

  • Performance
  • Maintainability
  • Scalability
  • Team productivity

If you want to operate at FAANG-level engineering depth, mastering refactoring is non-negotiable.

Clean code is not aesthetic.

It’s strategic.

What did you think?

© 2026 Vidhya Sagar Thakur. All rights reserved.