A (V)ay to write understandable software

Sercan DUMANSIZ
hepsiburadatech
Published in
9 min readJan 24, 2021

As we know today’s predominant software development methodology is an AGILE.
According to the AGILE manifesto,

Simplicity — the art of maximizing the amount of work done — is essential.

In AGILE we generally start with user stories. My previous experiences show me that this is the place where simplicity should start.

A common description of user story;
A user story is an informal, general explanation of a software feature written from the perspective of the end-user. Its purpose is to articulate how a software feature will provide value to the customer. (https://www.atlassian.com/agile/project-management/user-stories)

In this article, I’m going to write from a software engineer’s perspective. So I’m not going to tell about “how we should write user stories?” but we should know that user stories are our starting point.

So let’s assume that we have a well written, vertically sliced user story.
(If you want to know what I mean by a well-written user story, you can watch this video.)

What we usually need is client-side and server-side development to ship that feature.

https://jimmybogard.com/vertical-slice-architecture/

We can describe our feature as slice like above. What actually we have done here is, we separated our feature from end to end.

Have a closer look at what I meant by ‘separated’.

https://www.biography.com/news/galileo-discoveries-theories-modern-physics-astronomy

Separate

There are software design principles called Coupling and Cohesion that help us to measure separation. We aren’t going to deep dive into these two terms so I’m going to use Wikipedia definitions which are enough for where I want to point at.

Coupling

In software engineering, the coupling is the degree of interdependence between software modules; a measure of how closely connected two routines or modules are; the strength of the relationships between modules.

Cohesion

In computer programming, cohesion refers to the degree to which the elements inside a module belong together. In one sense, it is a measure of the strength of the relationship between the methods and data of a class and some unifying purpose or concept served by that class. In another sense, it is a measure of the strength of the relationship between the class’s methods and data themselves.

“Software Design Principles 7th February 2007” — Assistant Professor Naveed Arshad LUMS

If we look at the diagram above, arrows describe coupling and the lines inside boxes describe cohesion. In general, we want to achieve low coupling and high cohesion. I would like to tell you how we can achieve low coupling and high cohesion with vertical slice architecture.

Before we get into details of vertical slice architecture I would like to continue with Encapsulation and Single Responsibility design principles. These principles also help us to achieve low coupling and high cohesion. I know there are other principles that help us too but I wanted to choose the most common ones and also from my perspective these two relate more than the others with “simplicity”.

Encapsulation

Our systems generally have different layers, different components, different topics, etc. each part of the systems should isolate their job but also these parts mostly need to work collaboratively. The thing is that while working collaboratively we should not break internal parts. Encapsulation helps us to work collaboratively without breaking internal implementations. We can apply encapsulation in different architectural patterns like n-tier, layered, hexagonal, etc.

I would like to give an example of the most common usage.

Presentation — Domain (Business) — Data

In the beginning, everything looks fine in these layered architectures. We are very confident about what should be placed where. You belong here and you belong here. We can easily walk through the code. But trust me it’s a fake smile if we don’t separate them vertically.

What I experienced in my software engineering life is while the system is getting bigger it’s getting hard to decide where we should write that code. We are starting to think about what happens if I change here which part of the code effects. God objects smiling at you like saying “It’s too late to refactor me because you have new features to implement in a small amount of time.” etc. I wanted to make this dramatic because I believe that lots of you experienced these kinds of systems.
We wanted to bring simplicity with these architectural decisions but it turns out complex and less understandable systems. Believe me, it’s not fun to maintain these kinds of systems.

We don’t want to code like this right?

https://martinfowler.com/bliki/PresentationDomainDataLayering.html

What I wanted to show with this example is, separation is not always the solution. Of course, separation of concerns is one of the most important design principles but it’s also important how we separated.

Let’s continue with the Single Responsibility principle that gives us clarity.

Single Responsibility

For our previous example, what we have done is actually we applied single responsibility at a high level to the layers.

Let’s say.

  • Presentation Layer responsible for UI.
  • Business Layer responsible for logics.
  • Data Layer responsible for the access to the database.

This gives us clarity about layers. We can understand what we are going to face in these layers. (Hopefully!)

If we apply single responsibility at a low level like say class level. What we generally do is, giving responsibility to classes by domain, topic, etc.
For example:
We have a class that contains customer business logic and we called it CustomerService. What we expect from CustomerService class is to encapsulate the customer domain. When we start to implement new features about customer domain CustomerService is getting bigger and complex.
Also, it’s getting difficult to decide if this feature really needs to be inside CustomerService, or should we create a new service to handle this? because that feature might depend on different domains so where should write the code?
We don’t want to create god objects.
We don’t want to create useless objects.
(It’s getting hard to follow the feature code when we separate everything. So it causes less understandable systems by the developer.) Even we able to manage these things at the beginning if we have a living system that is growing over time there is no way to avoid face up with less understandable more complex systems in horizontal architectures.

So what’s happening if we don’t separate our encapsulations or responsibilities vertically? First of all, we need to be extra careful in our systems. We need to smell when is the right time for separation, we need to separate before it’s too late. We also need to think about the team right? While we are deciding these things we need to know that every developer agreed on the same mindset. So it sounds like these kinds of systems open to human errors. Instead of this open to mistake systems, I prefer more protected systems that don’t allow to do mistake about separation and responsibilities. (I know there might be mistakes but I prefer the systems that are more visible and easy to track these mistakes.)

https://enterprisecraftsmanship.com/posts/cohesion-coupling-difference/

High Coupling and High Cohesion introduce us to the God Object (you might hear as Big Ball of Mud). God Object is an example of an anti-pattern that makes systems more complex, less testable, and less understandable.

So let’s have a look at how the Vertical Slice Architecture approach helps us to write understandable software.

Vertical Slice Architecture

According to Jimmy Bogard who is one of the evangelists of V Slice Architecture.

  • Things that change together, belong together.
  • Maximize cohesion along axes of change and minimize coupling between them.

Let’s look at the axes.

https://headspring.com/2020/08/18/how-vertical-slice-architecture-fulfills-clean-architectures-broken-promises/

These yellow vertical axes are our features. As I mentioned at the beginning of the article, these are also our vertically slice user stories' technical sides.

Even in this image vertically separated features are more understandable. We focus on features, not layers. So when we look at the project structure of this image it’s really easy to follow features code and their jobs. Feature oriented thinking makes developers more comfortable especially for the new member of teams because there is no rule set for the whole project each feature has its own structure and its own needs.

Let’s take a step back and look a little further.

https://lostechies.com/jimmybogard/2015/07/02/ndc-talk-on-solid-in-slices-not-layers-video-online/

So when we look from the back, horizontal layers become invisible because all these features are not dependent on the same layers. Maybe some of them don’t need a data layer or business layer etc. or they don’t have to use the same strategy.

So let’s look at features closer.

As I told before each feature has its own logic. So we don’t have to use the same ORM to access database or even not the same database. We are flexible to decide by feature. We don’t need to think about other features each of them their own encapsulation so as a result of that we can just focus on one thing at a time. We are confident that our feature which we are working on is not going to break other features. So this gives us the freedom to write code without thinking about everything at the same time.

So what we have done actually is we encapsulated our feature from other features. Our features are loosely coupled between them but they are closer inside their own business.

So this is the general structure of Vertical Slice Architecture. I believe that it’s going to be more common in near future. I know Vertical Slice Architecture is not a new approach but it takes time to change habits, right?

“The task of the software development team is to engineer the illusion of simplicity.”
— Grady Booch

To be honest, simplicity is not easy to achieve by one day it’s a process. We need to practice a lot on the way. Also, I believe that it’s not possible to achieve simplicity without complexity. At first, we need to face complexity before we achieve simplicity. Why I’m saying this because if we don’t really recognize the complexity it’s not possible to divide and conquer. So we need to have a lot of information for simpler solutions. Even if it takes time to achieve simplicity it worths a lot when you achieve it.

I would like to end my article, with a quote by Donald Knuth.

Let us change our traditional attitude to the construction of programs. Instead of imagining that our main task is to instruct a computer what to do, let us concentrate rather on explaining to human beings what we want a computer to do.
— Donald Knuth

References

--

--