3 Minute Guide to SOLID Principles in Python
The SOLID principles are foundational design guidelines every software engineer should know. They improve code readability, testability, and maintainability. In this post, we’ll cover:
- Definition of each principle
- Python code samples
- Edge cases / where it breaks
- Follow-ups and important considerations
- Favorite interview questions
1. Single Responsibility Principle (SRP)
Definition
A class should have only one reason to change.
Example
1
2
3
4
5
6
7
8
9
10
class Invoice:
def __init__(self, items):
self.items = items
def calculate_total(self):
return sum(item['price'] for item in self.items)
class InvoicePrinter:
def print_invoice(self, invoice):
print(f"Total: {invoice.calculate_total()}")
Where It Breaks
Combining calculation logic and printing in the same class.
Follow-ups / Key Considerations
What constitutes “one reason to change” in your domain?
How do you decompose responsibilities in microservices?
Interview Question
Describe a class you refactored to adhere to SRP and the benefits you observed.
2. Open/Closed Principle (OCP)
Definition
Software entities should be open for extension but closed for modification.
Example
1
2
3
4
5
6
7
class DiscountStrategy:
def apply_discount(self, total):
return total
class ChristmasDiscount(DiscountStrategy):
def apply_discount(self, total):
return total * 0.9
Where It Breaks
Adding conditional logic for every new discount in a monolithic function or class.
Follow-ups / Key Considerations
How would you apply new behaviors without touching existing code?
Can you illustrate this with plugin architectures or feature flags?
Interview Question
Explain how you would design a pricing engine to support multiple discount types without modifying core logic.
3. Liskov Substitution Principle (LSP)
Definition Subtypes should be substitutable for their base types without altering program correctness.
Example
1
2
3
4
5
6
class Bird:
def fly(self):
print("Flying")
class Sparrow(Bird):
pass
Where It Breaks
If you subclass Bird with Penguin and its fly() method raises an exception.
Follow-ups / Key Considerations
How do you design class hierarchies to avoid violating LSP?
When is composition preferable to inheritance?
Interview Question
- Can you describe a scenario where inheritance violated LSP and how you refactored it?
4. Interface Segregation Principle (ISP)
Definition Clients should not be forced to depend on interfaces they do not use.
Example
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Printer:
def print(self):
pass
class Scanner:
def scan(self):
pass
class MultiFunctionDevice(Printer, Scanner):
def print(self):
# printing logic
pass
def scan(self):
# scanning logic
pass
Where It Breaks
A single interface that includes print, scan, fax, etc., forcing clients to implement unused methods.
Follow-ups / Key Considerations
How do you structure interfaces (or abstract base classes) in Python to avoid this?
What role do mixins play here?
Interview Question
- How would you refactor a monolithic interface into more focused abstractions?
5. Dependency Inversion Principle (DIP)
Definition High-level modules should not depend on low-level modules; both should depend on abstractions.
Example
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from abc import ABC, abstractmethod
class DataFetcher(ABC):
@abstractmethod
def fetch(self):
pass
class APIDataFetcher(DataFetcher):
def fetch(self):
return "Data from API"
class ReportGenerator:
def __init__(self, fetcher: DataFetcher):
self.fetcher = fetcher
def generate(self):
print(f"Report: {self.fetcher.fetch()}")
Where It Breaks
Hard-coding a specific data source inside ReportGenerator instead of depending on an abstraction.
Follow-ups / Key Considerations
How do you apply DIP in Python without formal interfaces?
What patterns (e.g., factory, dependency injection frameworks) can help?
Interview Question
Describe how you would design a module to allow swapping data sources at runtime.
Interview-Favorite Questions
Which SOLID principle is hardest to apply in large microservices architectures?
How does the Open/Closed Principle relate to feature toggles?
Can you demonstrate the Dependency Inversion Principle in pure Python?
Describe a situation where you violated SRP and how you refactored the code.
What are the trade-offs between inheritance and composition regarding LSP?