Skip to main content

Command Palette

Search for a command to run...

The SOLID Principles Every Software Engineer 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.

"Why do some codebases become impossible to maintain after just a year, while others remain clean after a decade?"

The difference is rarely the programming language.

It's usually the design.

Whether you're building a small REST API or a distributed microservices platform, following the SOLID principles can dramatically improve maintainability, scalability, and testability.

Let's break them down with practical examples.


S — Single Responsibility Principle (SRP)

A class should have only one reason to change.

Many developers misunderstand SRP as "one method per class."

That's not the point.

A class should have one responsibility, not multiple unrelated jobs.

❌ Bad Example

public class UserService {

    public void saveUser(User user) {
        // Save to database
    }

    public void sendWelcomeEmail(User user) {
        // Send email
    }

    public void generateReport() {
        // Generate PDF report
    }
}

This class handles:

  • Persistence

  • Email

  • Reporting

Three different responsibilities.


✅ Better Design

UserService
     │
     ├── UserRepository
     ├── EmailService
     └── ReportService

Each class has one job.

Benefits:

  • Easier testing

  • Smaller changes

  • Better readability

  • Lower coupling


O — Open/Closed Principle (OCP)

Open for extension, closed for modification.

When new requirements arrive, you should add code—not rewrite existing code.

❌ Bad Example

if(payment.equals("PayPal")){
    ...
}
else if(payment.equals("Stripe")){
    ...
}
else if(payment.equals("ApplePay")){
    ...
}

Every new payment method requires editing existing logic.


✅ Better Design

interface PaymentProcessor{
    void pay();
}

Implementations:

  • StripePayment

  • PaypalPayment

  • ApplePayPayment

processor.pay();

Adding Google Pay?

Simply create:

GooglePayProcessor

No existing code changes.

That's scalability.


L — Liskov Substitution Principle (LSP)

Subclasses should be replaceable by their parent class.

If replacing a parent with a child breaks your application, you've violated LSP.

❌ Famous Example

Bird
 ├── Sparrow
 └── Penguin

If Bird.fly() exists...

Penguin cannot fly.

Bad inheritance.


✅ Better Design

Bird
 ├── FlyingBird
 │      ├── Sparrow
 │      └── Eagle
 │
 └── Penguin

Now the hierarchy reflects reality.

Inheritance models behavior—not assumptions.


I — Interface Segregation Principle (ISP)

Clients should not depend on methods they don't use.

Avoid giant interfaces.

❌ Bad Example

interface Worker {

    work();

    eat();

    sleep();

    attendMeeting();

    submitTimesheet();
}

A robot worker doesn't eat.

A contractor may not submit timesheets.


✅ Better Design

Split interfaces.

Workable

Eatable

Sleepable

MeetingParticipant

Classes implement only what they need.

Cleaner.

Smaller.

More reusable.


D — Dependency Inversion Principle (DIP)

Depend on abstractions, not concrete implementations.

This principle powers modern frameworks like Spring Boot.

❌ Tight Coupling

class OrderService{

    private MySQLRepository repo =
        new MySQLRepository();
}

Switching to PostgreSQL?

You'll modify OrderService.


✅ Better Design

interface Repository

Implementations

  • MySQLRepository

  • PostgreSQLRepository

  • MongoRepository

Inject dependency.

OrderService(Repository repo)

Spring Boot does this automatically using Dependency Injection.

This makes applications:

  • Easier to test

  • Easier to replace implementations

  • Easier to scale


How SOLID Helps in Real Projects

Imagine you're building an e-commerce platform.

Without SOLID:

❌ Every feature touches dozens of files.

❌ Regression bugs increase.

❌ Unit testing becomes painful.

❌ Adding payment providers becomes risky.

❌ New developers struggle to understand the system.


With SOLID:

✅ New features plug into existing architecture.

✅ Business logic stays isolated.

✅ Components become reusable.

✅ Testing is straightforward.

✅ Refactoring becomes much safer.


SOLID and Modern Architecture

SOLID isn't just for object-oriented programming.

The same ideas appear in:

  • Spring Boot applications

  • Microservices

  • Domain-Driven Design (DDD)

  • Clean Architecture

  • Hexagonal Architecture

  • Event-Driven Systems

  • Plugin-based systems

  • AI Agent architectures

Good architecture is simply SOLID applied at a larger scale.


Common Misconceptions

❌ SOLID means more classes

Not necessarily.

It means better separation of concerns.


❌ SOLID makes development slower

Initially, maybe.

Long term?

It dramatically reduces maintenance cost.


❌ SOLID only matters for enterprise systems

Even a side project grows.

Today's 500 lines often become tomorrow's 50,000.

Design early.


Final Thoughts

The best engineers don't just write code that works.

They write code that still works—and is still easy to understand—years later.

SOLID isn't about memorising five principles.

It's about creating software that's easier to change than to replace.

As Robert C. Martin famously said:

"The only way to go fast is to go well."

Master SOLID, and you'll build software that scales not only in traffic—but also in maintainability.


Which SOLID principle do you think is violated most often in real-world projects?

Share your experience in the comments—I’d love to hear your perspective.

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