Previous: STAR Method

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.

SSingle Responsibility Principle
OOpen/Closed Principle
LLiskov Substitution Principle
IInterface Segregation Principle
DDependency Inversion Principle

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.

Loading quiz...