Python Design Patterns
Choose the simplest solution that works. Complexity must be justified by concrete requirements
What Is This
Python Design Patterns refers to a set of foundational principles and patterns that guide the structure and organization of your Python code. This skill emphasizes practical guidelines such as KISS (Keep It Simple), Separation of Concerns, the Single Responsibility Principle (SRP), and the preference for composition over inheritance. By applying these patterns, developers can create systems that are maintainable, easy to understand, and adaptable to change.
This skill is especially relevant when architecting new services or components, refactoring monolithic code, deciding whether to introduce new abstractions, or evaluating the structural quality of code in code reviews. The central goal is to minimize unnecessary complexity, ensure clear separation of responsibilities, and foster codebases that are easy to test.
Why Use It
Modern Python projects, especially as they grow, tend to accumulate complexity that can make them difficult to maintain and evolve. Common issues such as large "God classes," tangled dependencies, or mixing business logic with I/O can all contribute to fragile codebases. By leveraging design patterns and principles outlined in this skill, you can:
- Reduce code duplication and improve reusability
- Isolate changes so that bug fixes and updates do not introduce regressions
- Improve testability by decoupling logic from external systems
- Enable multiple developers to work on the same codebase without stepping on each other's toes
- Simplify onboarding for new team members
Ultimately, this skill helps you justify every piece of complexity in your codebase, ensuring that your architecture supports your requirements without unnecessary overhead.
How to Use It
KISS (Keep It Simple)
The KISS principle advises developers to always choose the simplest implementation that fulfills the requirements. Avoid speculative abstractions or premature optimizations.
Example:
## Simple and clear function
def calculate_discount(price, percentage):
return price * (1 - percentage / 100)Avoid over-engineering:
## Unnecessarily complex for the same outcome
class DiscountCalculator:
def __init__(self, strategy):
self.strategy = strategy
def apply(self, price, percentage):
return self.strategy(price, percentage)Unless you have multiple discount strategies, the first approach is preferable.
Single Responsibility Principle (SRP)
Each module, class, or function should have one reason to change. This means separating unrelated concerns.
Example:
class UserRepository:
def save(self, user):
# Save user to database
pass
class UserEmailService:
def send_welcome(self, user):
# Send welcome email
passThis separation makes it easier to change the email logic without risking data layer regressions.
Separation of Concerns
Divide your code into distinct sections, each addressing a separate concern. This often pairs with SRP.
Example:
- Data access logic in one module
- Business logic in another
- Presentation logic in yet another
## data_access.py
def fetch_user(user_id):
# Fetch user from database
pass
## business_logic.py
def register_user(user_data):
# Register user using data_access
pass
## presentation.py
def render_user_profile(user):
# Render HTML or JSON
passComposition Over Inheritance
Favor building functionality by combining simple, reusable components instead of relying on deep inheritance hierarchies. This avoids tight coupling and makes your code more flexible.
Example:
class Logger:
def log(self, message):
print(message)
class Service:
def __init__(self, logger):
self.logger = logger
def process(self):
self.logger.log("Processing...")You can swap out Logger for a mock or a different implementation without modifying Service.
Evaluating Coupling and Abstractions
When reviewing code or considering changes, ask:
- Are modules/classes tightly coupled?
- Do small changes ripple through unrelated parts of the code?
- Is there unnecessary leakage of internal types or responsibilities?
Modularize and decouple where it adds clarity and testability, but avoid introducing abstractions before they are justified by concrete requirements.
When to Use It
Apply the Python Design Patterns skill in the following scenarios:
- Designing new components or services: Structure your code from the outset to favor simplicity, separation, and clear responsibilities.
- Refactoring monolithic or tangled code: Break down large functions or classes to improve clarity and maintainability.
- Deciding whether to introduce a new abstraction: Only create new layers or interfaces if you have a concrete use case.
- Choosing between inheritance and composition: Use composition for flexibility unless inheritance is clearly justified.
- Evaluating code during reviews: Identify issues such as tight coupling, large classes, or improper separation of concerns.
- Improving testability: Separate I/O from business logic to facilitate unit testing.
Important Notes
- Justify complexity: Only introduce new abstractions or design patterns when there is a clear and present need.
- Avoid premature optimization: Focus on clarity and correctness first, then refactor as requirements become concrete.
- Balance design rigor with pragmatism: Overuse of patterns can lead to unnecessarily complex code.
- Favor readability and maintainability: Write code that is easy for others (and your future self) to understand and modify.
- Iterate and adapt: Design patterns are guidelines, not strict rules. Adapt them to fit the context of your project.
By applying the principles in this skill, you can create Python codebases that are robust, maintainable, and scalable, while avoiding unnecessary architectural overhead.
More Skills You Might Like
Explore similar skills to enhance your workflow
mail (v1)
Send and manage emails through the Lark Mail service programmatically
Agent Protocol
Inter-agent communication protocol for C-suite agent teams. Defines invocation syntax, loop prevention, isolation rules, and response formats. Use whe
Design System
Extract a complete design system from an existing website or screenshot into a DESIGN.md file. Analyses colours, typography, component styles, spacing
Screen Reader Testing
Practical guide to testing web applications with screen readers for comprehensive accessibility validation
Director Readiness Advisor
Guide the PM-to-Director transition across preparing, interviewing, landing, and recalibrating. Use when leadership scope is changing and you need
Logo Design Guide
Logo Design Guide automation providing expert guidance for crafting memorable brand logos