DEV Community

DAVID JORDAN ANAMPA PANCCA
DAVID JORDAN ANAMPA PANCCA

Posted on

Design Principles of Software: How I Apply Them in Python

When I first started coding, I thought writing software was just about making it work. But soon I realized that making it work is only half the battle—making it clean, flexible, and maintainable is just as important. That’s where software design principles come in.

These principles are like a roadmap for writing code that doesn’t turn into a nightmare as your project grows. In this article, I’ll explain some key principles and show a real example in Python.


Why Design Principles Are Important

Imagine you’ve built a small project, and six months later you come back to fix a bug or add a feature. If your code is messy, you’ll spend more time figuring out what’s happening than actually solving the problem. Good design principles make your code:

  • Easy to read
  • Easy to maintain
  • Flexible for future changes

The most common set of design principles is SOLID:

  1. Single Responsibility Principle (SRP) – Each class or function should have only one job.
  2. Open/Closed Principle (OCP) – Classes should be open for extension but closed for modification.
  3. Liskov Substitution Principle (LSP) – Subclasses should be able to replace parent classes without breaking the program.
  4. Interface Segregation Principle (ISP) – Don’t force classes to implement methods they don’t need.
  5. Dependency Inversion Principle (DIP) – High-level modules should depend on abstractions, not on concrete low-level modules.

A Real Example in Python

To make this concrete, let’s say I’m building a notification system where users can receive alerts via email or SMS. Here’s how I apply the design principles:

from abc import ABC, abstractmethod

# Interface defining what a Notifier should do
class Notifier(ABC):
    @abstractmethod
    def send(self, message: str):
        pass

# SRP: Each notifier has a single responsibility
class EmailNotifier(Notifier):
    def send(self, message: str):
        print(f"Sending email: {message}")

class SMSNotifier(Notifier):
    def send(self, message: str):
        print(f"Sending SMS: {message}")

# OCP & DIP: NotificationService works with any notifier
class NotificationService:
    def __init__(self, notifiers: list[Notifier]):
        self.notifiers = notifiers

    def notify_all(self, message: str):
        for notifier in self.notifiers:
            notifier.send(message)

# Using the system
email = EmailNotifier()
sms = SMSNotifier()
service = NotificationService([email, sms])
service.notify_all("Your order has been shipped!")
Enter fullscreen mode Exit fullscreen mode

How the Principles Are Applied

  • SRP: Each class has one job (sending email or SMS).
  • OCP: I can add new notifiers (like WhatsApp) without changing NotificationService.
  • DIP & ISP: NotificationService depends on the Notifier interface, not specific classes.

This structure keeps the system flexible, easy to extend, and clean, even as it grows.


Conclusion

Applying design principles is not just a recommendation—it is an investment in the quality and longevity of your code. Following SRP, OCP, DIP, and other SOLID principles makes your code easier to understand, safer to modify, and more scalable.

The goal is not just to make your software work today, but to build something that can grow and adapt tomorrow without complications. Adopting these principles from the start ensures cleaner projects, simpler changes, and smoother collaboration.

Top comments (1)

Collapse
 
ahmed_a_o profile image
AHMED HASAN AKHTAR OVIEDO

La integración entre teoría y código está bien lograda. Sería útil ampliar un poco la parte de “cómo identificar cuándo aplicar cada principio” para orientar a lectores con menos experiencia.