SOLID Principles in Python — Core Concepts

Why SOLID matters for Python developers

SOLID is a collection of five design principles introduced by Robert C. Martin. They were originally aimed at object-oriented languages like Java and C++, but Python’s flexibility makes them surprisingly natural to apply — and also surprisingly easy to violate.

Teams that follow SOLID consistently report fewer regressions, shorter code reviews, and easier onboarding for new developers.

The five principles explained

Single Responsibility Principle (SRP)

A class or module should have only one reason to change. If your UserService handles authentication, email sending, and database writes, it has three reasons to change and will break in unpredictable ways.

Practical check: Can you describe what a class does without using the word “and”? If not, split it.

Open/Closed Principle (OCP)

Software entities should be open for extension but closed for modification. In Python, this often means using base classes, protocols, or callable hooks instead of editing existing code when requirements grow.

A payment system that supports credit cards today should let you add PayPal tomorrow by writing a new class — not by adding if paypal: branches to the existing one.

Liskov Substitution Principle (LSP)

Any subclass should be usable wherever its parent class is expected, without surprises. If Square inherits from Rectangle but breaks when you set width and height independently, you have violated LSP.

Python-specific trap: Overriding a method and silently changing its return type or raising an unexpected exception breaks LSP even though Python will not complain at import time.

Interface Segregation Principle (ISP)

Clients should not be forced to depend on interfaces they do not use. Python does not have formal interfaces like Java, but the principle still applies. If a protocol or abstract base class forces implementors to define ten methods when they only need two, the interface is too fat.

Use small, focused protocols — Readable, Writable — rather than one FileHandler that demands everything.

Dependency Inversion Principle (DIP)

High-level modules should not depend on low-level modules; both should depend on abstractions. In Python, this usually means accepting dependencies as constructor arguments or function parameters rather than importing and instantiating them directly.

Instead of self.db = PostgresDB() inside a class, accept db: Database as a parameter. This makes testing trivial and swapping storage backends painless.

Common misconceptions

  • “SOLID means more classes.” Not necessarily. Sometimes a single function with a clear contract satisfies SRP better than a class hierarchy.
  • “Python’s duck typing makes SOLID unnecessary.” Duck typing makes polymorphism easier but does not prevent tangled dependencies or bloated classes.
  • “You need to apply every principle all the time.” SOLID principles are guidelines, not laws. A 50-line script does not need the same architecture as a 50,000-line system.

How the principles connect

SRP keeps pieces small. OCP and DIP keep them pluggable. LSP keeps substitutions safe. ISP keeps contracts lean. Together, they create a codebase where change is local rather than global.

The one thing to remember: SOLID is not about following five rules for their own sake — it is about making code that can evolve without collapsing under its own weight.

pythonarchitectureclean-code

See Also