Will Your Code Survive Growth?
Published on July 17, 2025
Loading...Subscribe to the Newsletter
Join other readers for the latest posts and insights on AI, MLOps, and best practices in software design.
Published on July 17, 2025
Loading...Join other readers for the latest posts and insights on AI, MLOps, and best practices in software design.
When you start learning programming, everyone who has substantion experience tells you, "Avoid long if-else chains. Use polymorphism instead." But why? Is it just a dogma, or is there a deeper reason behind this advice?
I’ve been there early on, it felt much simpler to just throw in another if
. Add a new feature? Just tack on another elif
. But as projects grow, this approach starts to fall apart. Let me explain why, and show you how a small shift in thinking can make your code not just cleaner, but also future-proof.
Picture this: You’re writing a function that handles different types of notifications in your app. Here’s the rookie version:
def send_notification(type: str, message: str):
if type == "email":
print(f"Sending EMAIL: {message}")
elif type == "sms":
print(f"Sending SMS: {message}")
elif type == "push":
print(f"Sending PUSH notification: {message}")
else:
raise ValueError("Unknown notification type")
It works. But what happens next month when your product manager says, "Can we add Slack notifications?" You update the function.
Then WhatsApp.
Then Teams.
Each time, you dig into the same function, add another branch, and increase the risk of breaking something that used to work. Maintenance gets harder. Testing becomes a pain. And reading this code after six months? Good luck.
Here’s where object-oriented programming—and specifically, polymorphism—starts to shine.
Instead of a single function deciding what to do for every notification type, each type can take care of itself:
from abc import ABC, abstractmethod
class INotificationSender(ABC):
@abstractmethod
def send(self, message: str):
pass
class EmailSender(INotificationSender):
def send(self, message: str):
print(f"Sending EMAIL: {message}")
class SMSSender(INotificationSender):
def send(self, message: str):
print(f"Sending SMS: {message}")
class PushSender(INotificationSender):
def send(self, message: str):
print(f"Sending PUSH notification: {message}")
Now, the code that wants to send a notification doesn’t care how it gets sent:
def notify(sender: NotificationSender, message: str):
sender.send(message)
Want to add Slack? Just create a new class, no changes to the rest of your system. Testing is easier, too, you can test each sender in isolation.
Of course not. For tiny scripts or cases with just a couple of conditions that will never grow, if-else is fine. But in real-world software, requirements always change. What starts small can get big, fast.
If you find yourself writing lots of if-else or switch/case statements to handle different types or behaviors, it’s a sign you might want polymorphism instead.
Start simple, but as soon as you see the pattern, refactor. Your future self and your teammates will thank you.
Great question! While this post focuses on OOP and Python, the idea is universal: avoid giant branching logic in any paradigm. In functional languages, you might use higher-order functions, pattern matching, or data-driven dispatch. The core goal remains—keep your code extensible, maintainable, and clean.
Want more practical clean code tips? Let’s connect on LinkedIn and drop a comment there if you’ve got your own “polymorphism vs if-else” war story!
Either way, thanks for being here.
– Richard