Engineering Excellence
SOLID principles and engineering best practices that distinguish senior engineers in American tech companies.
Why Engineering Principles Matter
American tech companies don't just want engineers who can code — they want engineers who can build maintainable, scalable, and testable systems.
In system design and coding interviews, interviewers evaluate your understanding of software design principles. Being able to articulate WHY you make certain design decisions is as important as the decisions themselves.
Interviewer's Perspective
I'm not looking for perfect code. I'm looking for engineers who can explain their design decisions and understand trade-offs.
The SOLID Principles
SOLID is an acronym for five design principles that help create maintainable, flexible software. Let's break down each one.
Single Responsibility Principle (SRP)
A class should have only one reason to change.
This means each class or module should focus on doing one thing well. When a class has multiple responsibilities, changes to one responsibility can affect the others.
Violating SRP
A UserService class that handles user authentication, sends welcome emails, generates reports, and manages user preferences.
Following SRP
Separate classes: AuthenticationService, EmailService, ReportGenerator, and UserPreferencesService.
Explaining SRP in an interview
“I follow the Single Responsibility Principle by ensuring each class has one clear purpose. This makes the code easier to test, understand, and modify without unintended side effects.”
Open/Closed Principle (OCP)
Software entities should be open for extension but closed for modification.
You should be able to add new functionality without changing existing code. This is typically achieved through abstraction, inheritance, or composition.
Violating OCP
Adding a new payment method requires modifying the existing PaymentProcessor class with new if/else branches.
Following OCP
Define a PaymentMethod interface. New payment methods implement this interface without touching existing code.
Explaining OCP in an interview
“I design systems to be extensible through interfaces and composition. When we needed to add new payment providers, I created a PaymentMethod interface so new providers could be added without modifying the core payment processing logic.”
Liskov Substitution Principle (LSP)
Objects of a superclass should be replaceable with objects of a subclass without breaking the application.
If class B is a subclass of class A, you should be able to use B anywhere you use A without unexpected behavior.
Classic LSP violation
A Square class that extends Rectangle but overrides setWidth/setHeight to always set both dimensions. Code expecting a Rectangle will break.
Pro Tip
If you find yourself checking the type of an object to decide what to do, you might be violating LSP.
Interface Segregation Principle (ISP)
Clients should not be forced to depend on interfaces they don't use.
Instead of one large interface, create smaller, more specific interfaces. This prevents classes from implementing methods they don't need.
Violating ISP
A Worker interface with methods: work(), eat(), sleep(). A Robot class implementing Worker must implement eat() and sleep() even though robots don't do those things.
Following ISP
Separate interfaces: Workable, Eatable, Sleepable. Classes implement only what they need.
Dependency Inversion Principle (DIP)
High-level modules should not depend on low-level modules. Both should depend on abstractions.
This principle is the foundation of Dependency Injection. Instead of creating dependencies inside a class, inject them from outside.
Violating DIP
OrderService directly instantiates MySQLDatabase inside its constructor. Changing databases requires modifying OrderService.
Following DIP
OrderService depends on a Database interface. The specific implementation (MySQL, PostgreSQL, etc.) is injected at runtime.
Explaining DIP in an interview
“I use dependency injection to decouple components. My services depend on interfaces, not concrete implementations. This makes testing easier — I can inject mock databases in tests — and allows us to swap implementations without changing business logic.”
Beyond SOLID
DRY — Don't Repeat Yourself
Every piece of knowledge should have a single, unambiguous representation in the system. But be careful — premature abstraction can be worse than duplication.
Pro Tip
The rule of three: Wait until you've duplicated code three times before abstracting. Two instances might just be coincidence.
KISS — Keep It Simple, Stupid
The simplest solution that works is usually the best. Avoid over-engineering and premature optimization.
YAGNI — You Aren't Gonna Need It
Don't build features or abstractions until you actually need them. Requirements change, and unused code is maintenance burden.
Discussing trade-offs
“I believe in starting simple and refactoring as patterns emerge. Over-engineering upfront often leads to abstractions that don't fit actual use cases.”
Discussing Principles in Interviews
Use real examples
Don't just define principles — share stories of how you've applied them and the impact they had.
Discuss trade-offs
Show that you understand when to apply principles and when simplicity trumps abstraction.
Connect to business value
Explain how good design principles lead to faster development, easier maintenance, and fewer bugs.
Knowledge Check
Test your understanding of engineering principles before moving on.