Modularization and semantic versioning

Carlos Morales

Carlos Morales • 2020-11-11

Practically there is no software company or public software library that does not use modularization and semantic versioning. In this post, I briefly describe why these concept are so important and share some lessons learned while using them.

Modularization

Based on Wikipedia: ”Modular programming is a software design technique that emphasizes separating the functionality of a program into independent, interchangeable modules”. Instead of “modules”, it could have said “libraries”, “cloud services”, “frameworks”, etc.

This technique dates to early software systems. With that said, in the last years, there has been an explosion of this concept: microservice architectures and cloud architectures are heavily based on independently developed modules abstracting the underlying complexity.

Modularization is one of the most important pillars of modern architecture.

Benefits of modularization

Modularization is associated with many advantages, for me, these are the most important benefits:

Disadvantages of modularization

Although modularization has proven to be very valuable, there are some disadvantages we need to focus on. One module is less complex, the whole system with a group of independent modules becomes instantaneously more complex:

I believe these trade-offs are acceptable, the question is not whether modularization is useful or not, but how far the splitting process goes, as over-engineered systems also become unmanageable. If there is no reason to split, do not modularize until it becomes necessary. And when there are too many modules to manage, focus on orchestrating them.

Application Programming Interfaces

There is one common trait in the previous list of disadvantages: integration. Modular integration is only possible if each module has good perimeters, these perimeters are commonly known as Application Programming Interfaces (API). Our goal is to build API-focused systems.

Good public APIs enable modular integrations.

The Bezos API mandate

Jeff Bezos wrote an email to all Amazon employees in 2002. It got known as the “Bezos API Mandate” or “Amazon API Mandate”. In his six lines memo he requested all teams must provide service interfaces and ”All service interfaces, without exception, must be designed from the ground up to be externalizable.”, otherwise ”Anyone who doesn’t do this will be fired.“.

I believe that memo is the key aspect that allowed the great success of Amazon Web Services (AWS). This set the correct mentality, all the software built in-house was meant to be used by other teams, and at some point, they decided other teams could be other companies. The rest is history, it brought a real boost to cloud computing and since then almost every IT company is moving its infrastructure to the cloud. Currently, AWS is a much more profitable division than the actual Amazon store.

Building software focusing on the public interface was a great decision for Amazon. You may say ”Ok, it worked for Amazon, could it work for any other software company?” I believe yes, actually in my opinion there is no other way.

APIs allow modularization

APIs provide a reliable contract, allowing teams to produce isolated modules that can be reused by other teams. Modules are a responsibility assignment, not a subprogram.

Semantic versioning

In systems with many dependencies, releasing new package versions can quickly become a nightmare. Semantic versioning is a solution to solve this problem, it helps to understand the potential impact of updating to a new version. The usage of semantic versioning allows teams to regularly update dependencies, a key requirement for modern distributed systems with many dependencies.

As a very brief definition, semantic version divides the released version in three numbers: MAJOR.MINOR.PATCH, these numbers increment based on the public API.

It establishes a communication between the creator of the module/library/… and the consumer: as a consumer of a module, the version tells you the impact of updating the dependency. As a module producer, this tool lets you to express the changes done in the API. As a consequence, the producer tells their consumers how much effort to spend when updating the dependency.

Semantic versioning became the defacto versioning method on open-source projects for a good reason.

Lessons learned when using semantic versioning

My team is responsible for building a set of UI components that many other teams use. Around five years ago, we decided to stabilize our APIs and introduce semantic versioning. It proved to be one of the best decisions we made.

These are some of my lessons learned across these years:

Stability ensures that reusable components do not become obsolete unexpectedly.

Stability was essential for us and the ecosystem around our framework to thrive.

Conclusions

As distributed architectures become more complex, modularization is a key technique to manage and abstract that complexity.

If we manage the dependencies correctly, the modularization benefits will justify the extra complexity.

When creating reusable components, focus on healthy public APIs. My own experience proves it.

If there is one takeaway from this article: focus on well-defined APIs and prevent adding breaking changes as much as possible.