Introduction

One of the most important things to follow in order to get a maintainable design/architecture is to separate the stable parts of your software from the changing parts.

                                       
                 │                     
                 │                     
 stable parts    │      changing parts 
                 │                     
                 │                     
                                       

The stable parts of your software are the parts that are not expected to change much during the lifetime of the software. The changing parts are, and frequently too.

1) Why is it important to separate the changing parts from the stable parts and 2) how does that contribute to a maintainable/extensible software design/architecture?

Maintainenace is Done on the Changing Parts

When we talk about maintainence, what are we talking about? What part are we maintaining and how? If you think about it, you will only be doing maintainence on the changing parts of the system! The stable parts, by definition, are not expected to change much. So most, if not all, maintainence/extension will be done on the changing parts.

By keeping this part, the part that is constantly being messed with, separate from the part that barely gets touched, you make maintainence/extension easier! You essentially eliminate a whole bunch of code/classes/modules that you even have to consider/look-at when you are maintaining!

                                            
                          touched           
                  │           │             
                  │           │             
                  │           │             
                  │           ▼             
  stable parts    │      changing parts     
                  │       ▲          ▲      
                  │       │          │      
                  │       │          │      
                  │       │          │      
                      touched     touched   
                                            

Let’s consider an example. Let’s say you have an application that allows users to buy and pay for some stuff. We can seperate this into an OrderTaking region and a Payment region. The way that orders are taken will not change much, users just click around and add stuff to their cart, and when they are done, they click “pay”. However, the payment details, depending on the payment vendor, will change a whole lot. Every vendor might require their own set of APIs to be called, etc.

We know that in the future, we will have to add more payment vendors. If we keep both the OrderTaking and Payment regions in the same class/module, then every time we add a new payment vendor, we will have to review/consider this entire class/module! If we keep them separate, then we only have to consider the Payment region! You are eliminating entire regions of the codebase from even being considered when you do your maintainence work!

Let’s say that half of your code is OrderTaking and half is Payment. You are eliminating half of the code from being considered!!

Software Engineering Principles/Patterns Encourage This

Most software engineering principles/patterns ultimately boil down to encouraging or offering a way to achieve this (seperation of changing/non-changing parts). Let’s take a look at a few.

Single Responsibility Principle (SRP)

We all know this one. A class/function/etc should have only one reason to change.

When a class has only one responsibility, there is one “domain reason” for it to change. By “domain reason” I mean one aspect of your domain. For example, the OrderLogic, or PaymentLogic, or LoginLogic, etc. You can kinda split your domain into distinct aspects, and each class/module should only handle one of these distinct aspects. This assumes you have broken down your domain into eventually small distinct aspects, otherwise you will still have big ol’ classes!

So, back to the topic at hand. When the people making the big-bucks comes to you for a change request, usually they are asking for a change in one of these distinct aspects. If you have followed the Single Responsibility Principle, then you will only need to consider/change one of your classes!

Finally, to go full circle, when each class has one reason to change, you aren’t exactly separating the changing parts from the stable parts, but you are separating the changing parts from one another! In other words, you are separating one reason to change from another! This is very similar!

Keep Coupling Low

Since I think by now you are getting the hang of things, I’ll keep the rest short :), I realize there is a high probability that you’re a gen z-er and thus have the attention span of a goldfish :D.

Low coupling. This principle basically states that things should depend on the fewest other things possible, and the dependencies should be shallow, just like your ex.

Every time one of the dependencies of a class changes, there is a chance that the class itself will have to change too. By keeping the number of dependencies low, you are keeping the number of things that could cause a change in your class low.

                                           
  ┌─────────────────────────────────────┐  
  │  Changes collectively               │  
  │                                     │  
  │    ┌───────┐      ┌─────────────┐   │  
  │    │       │      │             │   │  
  │    │  You  │ ───► │ Dependency1 │   │  
  │    │       │      │             │   │  
  │    └───┬───┘      │             │   │
  │        │          └─────────────┘   │  
  │        │                            │  
  │        │          ┌─────────────┐   │  
  │        │          │             │   │  
  │        └────────► │ Dependency2 │   │  
  │                   │             │   │  
  │                   │             │   │
  │                   └─────────────┘   │  
  │                                     │  
  └─────────────────────────────────────┘  
                                           

This is similar to the Single Responsibility Principle, but in the SRP the thing causing change was responsibilities/domain-reasons, in low coupling, the thing causing change is dependencies.

Either way, the end result is, you wanna keep the number of things that could cause a change in your class low!

Encapsulation

Encapsulation is basically hiding the details of a class/module. Think private stuff (no not that kinda private stuff, weirdo!!!). Think back to Low Coupling. The more dependencies a class has, the more things that could cause it to change, because every time one of its dependencies change, there is a chance the class will need to change as well.

                                           
  ┌─────────────────────────────────────┐  
  │  Changes collectively               │  
  │                                     │  
  │    ┌───────┐      ┌─────────────┐   │  
  │    │       │      │             │   │  
  │    │  You  │ ───► │ Dependency1 │   │  
  │    │       │      │             │   │  
  │    └───┬───┘      │ Internals1  │   │  
  │        │          └─────────────┘   │  
  │        │                            │  
  │        │          ┌─────────────┐   │  
  │        │          │             │   │  
  │        └────────► │ Dependency2 │   │  
  │                   │             │   │  
  │                   │ Internals2  │   │  
  │                   └─────────────┘   │  
  │                                     │  
  └─────────────────────────────────────┘  
                                           

However, if the dependencies keep most of their logic/data hidden, then there is less of a chance that a change in them will require a change in you! In other words, as long as the private/internal stuff of the dependencies changes, you don’t gotta change at all!

                                                           
  ┌────────────────────────────────────┐                   
  │  Changes collectively              │                   
  │                                    │                   
  │  ┌───────┐      ┌─────────────┐    │   ┌──────────────┐  
  │  │       │      │             │    │   │ Encapsulated │  
  │  │  You  │ ───► │ Dependency1 │ ───┼─► │ Internals1   │  
  │  │       │      │             │    │   │              │  
  │  └───┬───┘      └─────────────┘    │   └──────────────┘  
  │      │                             │                   
  │      │                             │                   
  │      │          ┌─────────────┐    │   ┌──────────────┐  
  │      │          │             │    │   │ Encapsulated │  
  │      └────────► │ Dependency2 │ ───┼─► │ Internals2   │  
  │                 │             │    │   │              │  
  │                 └─────────────┘    │   └──────────────┘  
  │                                    │                   
  └────────────────────────────────────┘                   
                                                           

Program to an Interface, Not an Implementation

By now you’re probably thinking “I get it, I get it, separate the changing parts from the stable parts, I get it!!”. But I’m not done yet! There’s more I wanna talk about!

When you program to an interface, the thing that is being programmed is the stable part, and the thing that is being abstracted away via the interface is the changing parts! In other words, the interface represents an axis of variation!

So programming to an interface is essentially how you add an axis along which you can lay the changing parts onto!

                                                             
             variation1 ...    ...    ...    ...  variationN  
                                                             
                  │      │      │      │      │      │       
                  │      │      │      │      │      │       
                  │      │      │      │      │      │       
  ┌────────────┐  │      │      │      │      │      │       
  │            │  ▼      ▼      ▼      ▼      ▼      ▼       
  │ StablePart ├─────────────────────────────────────────►   
  │            │         axis of variation                                   
  └────────────┘                           
                                                             

Liskov Substitution Principle

Ok, last one, I promise! The Liskov Substitution Principle basically states that a base class should be substitutable with any of its derived classes, without affecting the correctness of the program. Each derived class can have slightly different behavior, thus represents a change. So in essense, this, just like the principle above, aids in adding an axis of variation to your stable parts!

Conclusion

Honestly, this was very fun to write, but I’m getting a bit tired so Ima keep this short!

Basically:

  • Separate the changing parts from the stable parts
  • You usually only do maintainence on the changing parts
  • Thus, this eliminates an entire region (the stable parts) of the codebase from being even considered when you are doing maintainence
  • A slight variation is to separate the changing parts from themselves (from one another), i.e. kinda categorize and separate the different types of changing parts
  • A lot of software engineering principles/patterns encourage this (to either separate the changing parts from the non-changing parts or to separate the changing parts from one another)

That’s all for today, thanks for reading!