Skip to main content

Command Palette

Search for a command to run...

20 Code Smells Every Developer Should Know

Updated
5 min readView as Markdown
B
Senior Software Architect with 30+ years of experience building enterprise systems using Java, Spring Boot, and cloud-native technologies.

"The code compiles."

That's often where the real problems begin.

Many software failures don't originate from complex algorithms or cutting-edge technologies. They stem from small design decisions that quietly accumulate over months or years until the codebase becomes difficult to understand, risky to modify, and expensive to maintain.

These warning signs are known as code smells.

A code smell isn't necessarily a bug. Instead, it's an indicator that something in the design could be improved. Left unaddressed, code smells often evolve into technical debt, slower development, more defects, and frustrated engineering teams.

After more than three decades building enterprise software, I've learned that experienced developers don't simply write code that works—they write code that remains understandable six months later.

Here are 20 code smells every developer should recognize.


1. Long Method

Smell

A method grows to hundreds of lines and performs multiple responsibilities.

Why it's dangerous

  • Difficult to understand

  • Hard to test

  • Bugs hide easily

  • Impossible to reuse

Better approach

Break large methods into smaller functions, each with one clear responsibility.


2. Large Class

Smell

A class keeps growing until it handles business logic, validation, persistence, logging, notifications, and reporting.

Why it's dangerous

Violates the Single Responsibility Principle.

Better approach

Split responsibilities into focused services or domain objects.


3. Duplicate Code

Smell

The same logic appears in multiple places.

calculateDiscount()
calculateDiscountForVIP()
calculateDiscountForMember()

Nearly identical.

Why it's dangerous

Every bug fix must be repeated.

Soon the copies begin to diverge.

Better approach

Extract common behavior into reusable components.


4. God Object

Smell

One class knows everything and controls everything.

Example:

UserService

contains:

  • authentication

  • billing

  • email

  • reporting

  • inventory

  • notifications

Better approach

Divide responsibilities into cohesive services.


5. Long Parameter List

createOrder(
 customer,
 items,
 payment,
 shipping,
 coupon,
 currency,
 tax,
 warehouse,
 locale,
 source,
 metadata)

Nobody remembers parameter order.

Better approach

Use Parameter Objects or Builder Pattern.


6. Primitive Obsession

Instead of creating domain objects:

String email
String phone
String postcode

used everywhere.

Better approach

Create meaningful value objects.

Email
PhoneNumber
Address
Money

7. Feature Envy

A method spends most of its time manipulating another object's data.

OrderService

knows more about

Customer

than Customer itself.

Better approach

Move behavior closer to the data it belongs to.


8. Data Clumps

The same variables always appear together.

street
city
postcode
country

Better approach

Create an Address object.


9. Switch Statements Everywhere

Large switch statements continue growing.

switch(type){
...
}

Better approach

Use polymorphism or the Strategy Pattern.


10. Shotgun Surgery

One business change requires edits across dozens of files.

Why it's dangerous

High risk of missing something.

Better approach

Improve modularity and encapsulation.


11. Divergent Change

One class changes for many unrelated reasons.

Today:

  • pricing

Tomorrow:

  • logging

Next week:

  • reporting

This indicates poor separation of concerns.


12. Comments Explain Bad Code

If comments constantly explain what the code does, the code probably isn't expressive enough.

Instead of:

// calculate tax

Write:

calculateTax()

Good naming beats excessive comments.


13. Magic Numbers

if(score > 87)

Why 87?

Nobody knows.

Better approach

PASSING_SCORE

14. Inappropriate Intimacy

Two classes know too much about each other's internals.

Changing one breaks the other.

Better approach

Reduce coupling through proper interfaces.


15. Message Chains

a.getB()
 .getC()
 .getD()
 .getE()

Also known as the "train wreck."

Better approach

Follow the Law of Demeter.


16. Lazy Class

A class exists but barely does anything.

Sometimes:

UserManagerFactoryHelperUtil

contains only one method.

Delete it.


17. Temporary Fields

Fields only exist during one specific workflow.

The rest of the time they remain unused.

This usually suggests missing abstraction.


18. Dead Code

Unused methods.

Unused variables.

Unused classes.

Unused APIs.

They're not harmless.

They increase cognitive load and confuse future developers.

Delete them.


19. Speculative Generality

Building abstractions for requirements that may never exist.

"We might support five databases someday."

Three years later...

Still one database.

Keep designs as simple as current requirements allow.


20. Exception Swallowing

try{
...
}catch(Exception e){
}

Nothing logged.

Nothing reported.

Failures disappear silently.

This makes production debugging a nightmare.

Always handle exceptions intentionally.


How Senior Engineers Spot Code Smells

Experienced developers don't just review syntax.

They ask questions like:

  • Is this class doing too much?

  • Can this code be understood in one minute?

  • Will future developers know why this exists?

  • How expensive will the next change be?

  • Does this design communicate intent?

Great code isn't measured by cleverness.

It's measured by clarity, maintainability, and ease of change.


Final Thoughts

Every developer writes code smells from time to time. The difference is that great engineers learn to recognize them early and refactor continuously instead of allowing them to accumulate.

Code reviews shouldn't focus only on formatting or naming—they should identify structural issues before they become technical debt.

The cleanest code isn't the shortest or the most sophisticated.

It's the code that another developer can understand, modify, and trust with confidence months or even years later.

Which code smell do you encounter most often in your projects? Share your experience in the comments—I’d love to hear how your team keeps codebases clean and maintainable.

More from this blog

B

Bill LIao's Blog

137 posts

A technical blog on modern backend development, software architecture, and practical AI agent workflows