We can achieve reliability through testing. Particularly, we need to test each smallest unit of function defined in our application. To do so, we must have a framework that supports unit testing and a methodology for building test driven applications that we call test driven development (TDD). However, TDD and unit tests would be possible only if we architected our application following a model that enables us to create components working independently.
We can achieve flexibility, reusability, and maintainability altogether by applying object-oriented design principles defined by Robert C. Martin (also well- known as SOLID principles). I will explain each principle briefly and how it impacts our requirements.
The first principle is Single Responsibility Principle (SRP that is represented by S in SOLID). This principle says that each object should have only a single responsibility or reason to change. Obviously, SRP is a best fit for flexibility because if you change code of a class that is designed following SRP then you don't have to worry about code contained in other classes. That class is also fit for reusability because you just provide only one responsibility to others without concern for unexpected operations that could be called by external systems.
The second principle is Open/Closed Principe (OCP that is presented by O in SOLID). This principle says that software entities (classes, modules, functions, etc) should be opened for extension but closed for modification. We apply this principle in practice by creating contract-based design. The OCP is especially fit to maintainability because you can add or remove features without changing existed code. All you need to do is create new classes implemented using exiting interfaces or create new interfaces inherited from old interfaces and add more features.
The third principle is Liskov Substitution Principle (LSP that is represented by L in SOLID). This principle says that super types can be replaced by their sub types without altering the correctness of a program. LSP principle is also inspiration for creating contract-based design and therefore, it's also good for maintainability. Both OCP and LSP also help reusing components safely because components talk each other through interfaces.
The fourth principle is Interface Segregation Principle (ISP that is represented by I in SOLID). This principle says that one general purpose interface should be divided into many client specific interfaces. This principle is similar to SRP because it also supports easy modification of code without affecting unrelated code. So, the ISP principle is good for flexibility.
The fifth principle is Dependency Inversion Principle (DIP that is represented by D in SOLID). This principle provides a dependency relationship among using modules (high-level modules) and used modules (low-level modules) and provides a solution by reversing that dependency. Because of this, it was called Dependency Inversion or Inversion of Control. DIP is a critical mechanism to create extensible applications where it is used to enable writing an application's plug-ins or extensions. Obviously, it is useful for maintenance and testing.
So far, these principles I introduced have the same goal; that is help us to create loosely-coupled components. In other words, they were inspired from a generic philosophy called Separation of Concerns (SoC). SoC is a progress of separating a computer program into distinct features that overlap in functionality as little as possible. Typically, concerns are synonymous with features or behaviors. At a low-level, SoC can be achieved through encapsulating concerns in classes and modularizing classes in components. At a high-level, SoC is interpreted in layered designs of applications. So, what are layered designs? They are designs that require our applications to be divided into layers and tiers. The term layer has a logical meaning while the term tier has a physical meaning. For example, you are required to build an application that allows user access to data stored at a remote database. Applying SoC, you decided to divide your application into three subsystems: a database installed on a centre server, a program installed on another server and that provides services for clients, and each UI application installed on each client's computer.