When to Refactor a Code

Refactoring isn’t about rewriting code because it feels “ugly.” It’s about improving design without changing behavior—at the right time.

Here’s when you decide to refactor a code.


1. When You See “Code Smells”

Refactor when you notice structural problems that make the code harder to maintain:

  • Duplication – Same logic repeated in multiple places

  • Long methods – Functions doing too many things

  • Large classes – One class handling multiple responsibilities

  • Deep nesting / complex conditionals

  • Poor naming – Variables like datatempx

  • Shotgun surgery – Small feature change requires editing many files

  • Feature envy – A method using another class’s data more than its own

If the code makes you hesitate before modifying it, that’s a signal.


2. The Best Time: While Adding or Changing Features

The ideal moment to refactor is:

When you're already touching the code.

This follows the Boy Scout Rule (popularized in Clean Code by Robert C. Martin):

“Leave the code cleaner than you found it.”

Why?

  • You already understand that part of the system.

  • You reduce future friction.

  • You avoid large risky rewrites.


3. When Change Is Becoming Expensive

Ask yourself:

  • Does every small change feel risky?

  • Are bugs appearing in unexpected places?

  • Are tests hard to write?

  • Does onboarding new developers take too long?

If yes → the design is slowing you down → refactor.


4. When You Have Tests

Refactor only when behavior is protected by tests.

If there are no tests:

  1. Write tests first.

  2. Then refactor.

Without tests, refactoring becomes guessing.


5. When Complexity Is Growing Faster Than Value

Sometimes code “works fine” but is getting harder to understand.

A useful heuristic:

  • If it takes longer to understand the code than to write it → refactor.

  • If adding a simple feature requires major mental overhead → refactor.


When NOT to Refactor

  • Right before a critical release

  • Without understanding the system

  • Just for aesthetic reasons

  • In stable legacy code that rarely changes

  • When there’s no test safety net

Refactoring is a tool—not a hobby.


A Practical Rule of Thumb

Refactor when at least one of these is true:

  1. You are modifying the code anyway.

  2. The code is actively slowing development.

  3. The design blocks new features.

  4. Bugs keep recurring in the same area.


Strategic vs Opportunistic Refactoring

Opportunistic – Small improvements during feature work
Strategic – Planned refactoring sprint to fix structural issues

Use strategic refactoring when:

  • Architecture is the bottleneck

  • Technical debt is measurable and costly

  • The team agrees it's needed


Simple Decision Framework

Ask:

  1. Does it work?

  2. Is it hard to change?

  3. Will it need to change again?

If:

  • Works + won’t change → leave it.

  • Works + will change → refactor.

  • Doesn’t work → fix first, then refactor.


Here’s when you should refactor in a Java backend, and what that usually looks like in practice.


1. Your Service Class Is Doing Too Much

Smell:

A single UserService:

  • Validates input

  • Talks to repository

  • Maps DTOs

  • Handles transactions

  • Sends emails

  • Logs metrics

That violates Single Responsibility Principle (from Clean Code by Robert C. Martin*).

Refactor when:

  • Methods exceed ~30–40 lines

  • Constructor has 6+ dependencies

  • Unit tests are painful to write

Fix:

Split into:

  • UserValidator

  • UserMapper

  • UserRepository

  • EmailService

  • Keep UserService as orchestrator


2. Duplicate Logic Across Controllers or Services

Smell:

Same validation or mapping repeated in:

  • UserController

  • AdminUserController

  • BatchUserProcessor

Refactor when:

You copy-paste code even once.

Fix:

Extract shared component:

@Component
public class UserValidator { ... }

Duplication spreads bugs.


3. Complex Conditionals (Especially in Business Logic)

Smell:

if (type.equals("A")) {
   ...
} else if (type.equals("B")) {
   ...
} else if (type.equals("C")) {
   ...
}

Refactor when:

Adding a new type requires editing this method.

Fix:

Use polymorphism or strategy pattern.

Example:

interface PricingStrategy {
    BigDecimal calculate(Order order);
}

Add new implementations instead of editing existing logic.


4. Anemic Domain Model (Very Common in Spring Apps)

If your entities are just:

class Order {
   private BigDecimal amount;
   private String status;
}

And all logic is in services:

→ That’s a sign to refactor.

Move behavior into the domain:

public void cancel() {
   if (status.equals("SHIPPED")) {
       throw new IllegalStateException();
   }
   this.status = "CANCELLED";
}

Refactor when business rules are scattered everywhere.


5. Tests Are Hard to Write

If unit tests require:

  • 5 mocks

  • Complex setup

  • Spring context loading

That’s a design smell.

Refactor toward:

  • Constructor injection

  • Small classes

  • Pure functions when possible

If you need @SpringBootTest for simple logic → refactor.


6. Transaction Boundaries Are Confusing

Common backend issue:

@Transactional
public void processOrder() {
   updateInventory();
   chargeCard();
   sendEmail();
}

If failures cause inconsistent states:

→ Time to refactor.

Split orchestration and side effects.
Consider domain events.


7. Repository Layer Is Leaking Into Business Logic

If you see this in many services:

userRepository.findById(id).orElseThrow(...)

Repeated everywhere?

Refactor into:

public User getRequiredUser(Long id)

Encapsulate repository behavior.


8. Adding Features Feels Risky

Ask yourself:

  • Does adding one endpoint require touching 5 files?

  • Does changing DB schema break many services?

  • Are regressions common?

If yes → refactor before adding more features.


When NOT to Refactor for Java Backend

Don’t refactor when:

  • System is stable and rarely changed

  • You’re days before production release

  • No tests exist

  • It’s legacy code no one touches


Practical Rule for Backend Java

Refactor when:

  • Class > 300 lines

  • Method > 40 lines

  • Constructor > 5 dependencies

  • Cyclomatic complexity feels hard to reason about

  • You fear touching the code


Senior Developer Heuristic

If reading a method requires scrolling → refactor.
If understanding a change requires jumping across many classes → refactor.
If business rules are not obvious → refactor.



Nenhum comentário:

Postar um comentário

HashMap in Java

Let’s break down how   HashMap   works internally (Java 8+ implementation). What Is a HashMap? HashMap<K, V>  is a  hash table–based  ...