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
data,temp,xShotgun 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:
Write tests first.
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:
You are modifying the code anyway.
The code is actively slowing development.
The design blocks new features.
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:
Does it work?
Is it hard to change?
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:
UserValidatorUserMapperUserRepositoryEmailServiceKeep
UserServiceas orchestrator
2. Duplicate Logic Across Controllers or Services
Smell:
Same validation or mapping repeated in:
UserControllerAdminUserControllerBatchUserProcessor
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