Robert C. Martin (Uncle Bob ) named five principles of object oriented programming to help create software systems that are easy to maintain and extend over time. These principles were eventually acronimized (not really a word :-) ) by Michael Feathers and called the S.O.L.I.D. principles of object oriented programming.
In the past decade or so blogging and social media made it extremely easy for people to get their ideas across to a wider audience. The downside of this was that a lot of mediocre stuff was thrown into the mass, creating a sort of information overload for everyone. But the upside is that this system soon became self governing and good information was linked to by many other people, pushing it up the heap into people's feed readers. The S.O.L.I.D. principles are one of those (really) good pieces of writing which emerged to the top not just once but several times and is still cited more than a decade after their introduction. This should give you a sense of how important and useful they are.
S.O.L.I.D. stands for five principles which I will describe briefly below:
Single responsibility principle
A class should have one and only one reason to change
This principle states that every class in your software should have one and only one distinct responsibility. Imagine a mobile phone which also doubles up as a beard trimmer. The design of this phone would have to change if people's ears got smaller (because nobody really listens nowadays), but it would also have to change if people's beard hair somehow became thick as wire. Changing the hair trimmer part of the phone, could in theory, introduce bugs in the parts of the phone that are responsible for communicating and vice versa, eventually making the system difficult to debug. The single responsibility principle helps contain responsibilities and bugs within a very well defined scope to make the system more maintainable. But that's not all, this principle also makes a system easier to change. Check this article on S.R.P by Uncle Bob to understand the depths of this principle.
Software entities should be open for extension, but closed for modification
Most software systems last beyond their first version. As a system is used over time the customer will request new features and changes to existing features. Very often one simple change can have a cascading effect on the system. Several such changes leads to a system becoming so complex that many companies prefer to rewrite an entire system after a few years because the resulting changes have made it unmaintainable. One way to avoid this problem is to design the system such that changes or extensions in requirements can be done by adding subclasses instead of changing existing code.
This article explains the open/closed principle in more detail with real world examples.
Liskov substitution principle
Derived classes must be substitutable for their base classes.
The motivation for this principle came from the attempt to understand the characteristics of good inheritance hierarchies and the traps that can cause the open/close principle to fail with certain hierarchies. This article explains the Liskov Substitution principle very well. Here's a quote that kind of gets to the heart of the matter:
The importance of this principle becomes obvious when you consider the consequences of violating it. If there is a function which does not conform to the LSP, then that function uses a pointer or reference to a base class, but must know about all the derivatives of that base class. Such a function violates the Open-Closed principle because it must be modified whenever a new derivative of the base class is created.
Interface segregation principle
Make fine grained interfaces that are client specific.
I mentioned the Single Responsibility Principle earlier in this article which suggests that every class must have only one clear responsibility. Despite such adherence it is still possible for classes to have non-cohesive interfaces (methods) such that they can be further grouped into sets of cohesive interfaces. In most cases these groups will be used by different client code. The Interface Segregation Principle states that client code should not be aware of such a non-cohesive class as one unit. The class should instead have multiple interfaces (or abstract base classes) and the client code should only be aware of the interface which is specific to its needs. Here's an article by Uncle Bob which explains this principle in greater detail.
Dependency inversion principle
Depend on abstractions, not on concretions.
At the heart of bad design is a tangle of dependencies between modules which cause the software to become rigid (difficult to change because a single change will introduce a cascade of sub changes), fragile (a change in one place can introduce bugs in a totally unknown and perhaps even seemingly decoupled place), and immobile (a module in one context cannot be reused in another context because it is heavily dependent of certain specifics in it original context which cannot be easily replicated elsewhere). The Dependency Inversion Principle attempts to prevent this mess by stipulating that entities and high level modules must not depend on concrete implementations but rather should depend only on abstractions. Here's a very good writeup by Robert C. Martin about this principle.