S: Single Responsibility Principle
A class or module should have one, and only one, reason to change. For example, separate database write operations from invoice formatting code so changes to invoice layouts do not risk breaking database connections.
O & L: Open/Closed and Liskov Substitution
Open/Closed: Software modules should be open for extension but closed for modification. Implement new capabilities by creating new classes that implement an interface rather than modifying core legacy code.
Liskov Substitution: Subclasses must be substitutable for their base class interfaces without altering correctness. Child classes should never throw unexpected exceptions or break base contract behaviors.
I & D: Interface Segregation and Dependency Inversion
Interface Segregation: Clients should not be forced to depend on methods they do not use. Split fat interfaces into smaller, cohesive roles.
Dependency Inversion: High-level modules should not import details from low-level modules; both should depend on abstractions (interfaces). Depend on an abstract Logger interface rather than a specific disk writer.
// Dependency Inversion Example
interface Database {
save(data: string): void;
}
class UserManager {
constructor(private db: Database) {}
}