The SOLID Principles
Fast recall
Quick revision
Core answer
Whether you can name the five SOLID principles and their one-line meaning, and know they're guidelines for changeable code.
Treating SOLID as laws (over-engineering) or only knowing the acronym without the meaning.
SOLID = SRP, OCP, LSP, ISP, DIP. SRP: one reason to change. OCP: extend, don't edit. LSP: subclass replaces parent. ISP: small interfaces. DIP: depend on abstractions.
Interview-ready guide
Interview answer
SOLID groups five principles for keeping changes local. Single Responsibility means a class owns one coherent responsibility or reason to change; it does not literally mean one method. An Invoice can calculate invoice totals, while PDF formatting and email delivery change for different reasons and can live elsewhere.
Open/Closed means stable code can accept a new variation through an extension point. A checkout that calls gateway.charge can work with a new gateway object without adding another provider branch. Ruby's modules and duck typing make the extension point a small message contract rather than a required interface declaration.
Liskov Substitution says any replacement must preserve what callers expect: accepted inputs, valid outputs, side effects, and errors. A subclass that rejects a normal parent operation is not a safe subtype even though inheritance compiles.
Interface Segregation says clients should depend only on the capabilities they use. In Ruby this appears as small protocols—for example, a report needs write, not an object with storage, networking, administration, and logging methods.
Dependency Inversion separates policy from details. An OrderProcessor receives a payment gateway instead of constructing Stripe inside its method, which makes another implementation and a focused test double possible. SOLID is useful when real, measurable variation and change pressure exist; applying every principle to a tiny script can add more structure than value.
The SOLID Principles
Direct Answer
SOLID is a set of five design principles: Single Responsibility, Open/Closed, Liskov Substitution, Interface Segregation, and Dependency Inversion. In Ruby they guide classes toward one reason to change, extension through small interchangeable collaborators, honest subtype contracts, narrow message protocols, and dependence on behaviour rather than concrete infrastructure. They are heuristics for reducing change cost—not rules that require an interface class, one-method objects, or a framework around simple code.
Deep Dive & Architecture
Start with reasons for change. Pricing rules, persistence, transport, and presentation may collaborate in one feature but come from different decisions. Separating them allows one decision to evolve without forcing every other concern through the same class and test setup.
An extension seam is only useful when variation is real. Passing a gateway into checkout keeps provider-specific code outside the ordering policy. The high-level object defines the few messages it needs, and adapters translate each external SDK into that local protocol.
Substitution and interface segregation protect that seam. Every gateway must give the same meaning to success, decline, and technical failure; merely responding to charge is not enough. Requiring only charge prevents checkout from becoming coupled to provider methods it never uses.
Dependency direction is about source-code knowledge. The business policy may know a PaymentGateway role, while a Stripe adapter knows both Stripe and that local role. Object construction happens at the application boundary. Ruby can express this without abstract base classes, but documentation and shared contract tests become important.
The principles are diagnostic lenses, not a scoring system. A design is improved when a likely change becomes safer and easier to understand. If an extra abstraction has no independent responsibility, variation, or boundary, simple direct code remains the better design.
Code Example
class Checkout
def initialize(gateway)
@gateway = gateway
end
def complete(order)
@gateway.charge(order.total)
order.mark_paid
end
end
class FakeGateway
attr_reader :charged_amount
def charge(amount)
@charged_amount = amount
end
end
# StripeGateway or BankGateway can implement the same charge contract.
# Checkout does not branch on or construct a provider.
Common Mistakes & Pitfalls
Treating SOLID as laws (over-engineering) or only knowing the acronym without the meaning.
Check your understanding
Answer these without looking back at the article.
- What does 'one reason to change' mean in the Single Responsibility Principle?
- How can duck typing support Open/Closed and Interface Segregation?
- What behaviour must a replacement preserve to satisfy Liskov Substitution?
Looking for a structured roadmap?
Every module on this page is also part of the full Java backend interview track.