SE Notes
Fundamental principles guiding good software design decisions.
Software design principles are fundamental guidelines that help developers make design decisions producing maintainable, flexible, and robust software. Unlike specific patterns that provide concrete structures for specific problems, principles are universal guidelines applicable to every design decision at every scale—from naming a variable to architecting a distributed system. They represent decades of hard-won industry wisdom about what makes software succeed or fail over its lifetime.
DRY (Don't Repeat Yourself)
"Every piece of knowledge must have a single, unambiguous, authoritative representation within a system." — Andy Hunt and Dave Thomas, The Pragmatic Programmer.
DRY goes beyond avoiding copied code. It means that any piece of knowledge—a business rule, a calculation formula, a configuration value—should exist in exactly one place. When that knowledge changes, you modify one location, and the change propagates correctly everywhere it is needed.
# Violating DRY - tax rate defined in multiple places
def calculate_order_tax(amount):
return amount * 0.08 # 8% tax
def calculate_shipping_tax(shipping_cost):
return shipping_cost * 0.08 # Same 8% - duplicated!
# Following DRY - single source of truth
TAX_RATE = 0.08
def calculate_tax(amount):
return amount * TAX_RATE # Used everywhereIf the tax rate changes to 9%, the DRY version requires one change. The duplicated version requires finding every place the rate appears—and missing even one creates an inconsistency bug.
KISS (Keep It Simple, Stupid)
The simplest solution that solves the problem is almost always the best. Complexity is the primary enemy of software maintainability. Every unnecessary abstraction, every premature optimization, every clever trick adds cognitive load for future developers (including your future self) without delivering proportional value.
# Over-engineered (violating KISS)
class AbstractStrategyFactoryProviderSingleton:
... # 200 lines of abstraction for a feature used once
# Simple and clear (following KISS)
def calculate_discount(order_total, customer_tier):
rates = {"gold": 0.15, "silver": 0.10, "bronze": 0.05}
return order_total * rates.get(customer_tier, 0)KISS does not mean writing naive code. It means choosing the simplest approach that adequately handles current and reasonably foreseeable requirements—not the simplest possible code regardless of requirements.
YAGNI (You Aren't Gonna Need It)
Do not implement functionality until it is actually needed. Developers often build infrastructure for future requirements that may never materialize. This premature generalization wastes development time, increases complexity, and makes the system harder to understand—all for features that might never be used.
YAGNI does not mean ignoring the future entirely. It means distinguishing between designing for change (good—using interfaces and clean architecture so future changes are easy) and building for speculation (bad—implementing features based on guesses about future needs).
Separation of Concerns
Each module, class, or function should address a single concern—one aspect of the system's functionality. When concerns are mixed together, changes to one concern force modifications to code handling unrelated concerns.
# Mixed concerns - UI, business logic, and data access together
def handle_submit_button():
name = text_field.get_value() # UI concern
if len(name) < 2: # Business rule
show_error("Too short") # UI concern
return
db.execute("INSERT INTO users VALUES (?)", name) # Data access
show_success("Saved!") # UI concern
# Separated concerns
class UserValidator: # Business rules only
def validate(self, name): ...
class UserRepository: # Data access only
def save(self, user): ...
class UserController: # Coordination only
def handle_submit(self): ...Law of Demeter (Principle of Least Knowledge)
A module should only communicate with its immediate collaborators—not with collaborators' collaborators. "Only talk to your friends, not to friends of friends."
# Violating Law of Demeter - reaching through objects
customer.get_address().get_city().get_zip_code()
# Following Law of Demeter - ask, don't dig
customer.get_shipping_zip_code()Composition Over Inheritance
Favor building complex behavior by combining simple objects (composition) rather than creating deep inheritance hierarchies. Inheritance creates rigid relationships—a subclass is permanently tied to its superclass. Composition creates flexible relationships—behavior can be assembled and reconfigured at runtime.
Program to Interfaces, Not Implementations
Depend on abstractions (interfaces, abstract classes) rather than concrete implementations. This enables substitution—any implementation satisfying the interface can be used, enabling testing (mock implementations), flexibility (swap implementations), and evolution (replace implementations without changing consumers).
Real-World Application
Consider designing a notification system. Applying principles:
- KISS: Start with email only. Do not build SMS, push, and Slack support until needed.
- YAGNI: Do not build a plugin architecture for notification channels until you actually have multiple channels.
- DRY: Template rendering logic exists once, used by all notification types.
- Separation of Concerns: Message content, delivery mechanism, and retry logic are separate modules.
- Interface-based: Define a NotificationChannel interface. Email, SMS, push each implement it.
- Composition: Build complex notifications by combining simple components (template + delivery + logging).
These principles are guidelines, not absolute rules. Experienced judgment determines when to follow them strictly and when pragmatic compromise is appropriate. The goal is always the same: software that works correctly today and can be economically maintained tomorrow.
Exam Focus
Revise definitions, diagrams, examples, and short-answer points for Software Design Principles.
Interview Use
Prepare one clear explanation, one practical example, and one common mistake for this Software Engineering topic.
Search Terms
software-engineering, software engineering, software, engineering, design, principles, software design principles
Related Software Engineering Topics