Published on

From concepts to architecture

Authors

Let's play a board game!

Imagine that you and a bunch of friends of yours met, to play a new board game: FSM: Fantasy, Sword and Magic.

No one from the crew played before, as it's a fresh thing in the board games market, each of you need to get the mechanics of it.

Probably, it's not the first time any of you play a board game, so it should be easy.

Let's say, after one hour of collaborative manual reading and exchanging opinions, you can play the game.

Not that easy at it looks

How does "smooth jump" differ from "thief's jump"?

What is a "power move"?

What is a "haze maze" event?

These and many other questions appear both during the manual walkthrough and during the gameplay.

Words, words, words.

Of course, there's a theme behind the game, so no doubt that there will be a specific language!

Nevertheless, after some more actions and mistakes, you all grasp the underlying concepts.

Or maybe it is easy?

Suddenly, it all makes sense!

The game was designed to support some interactions while discourage other ones.

Previously misunderstood rules, words and ideas compose more and more into a cohesive whole.

As players, you stop talking in a "generic board game" language and all discussions immerse into FSM lingo.

What's more, people start getting creative with all the moves, jumps, spells, weapons, tactics and so on!

It all starts with an idea

It might sound weird and a bit to philosophical-ish - but it all starts with an idea.

Some ideas are really old, like accounting or trading stuff, and some are new - like deploying serverless lambdas.

They were designed/created based on the needs - started as a mere concept, eventually getting materialized in the "real world".

Then, each concept gave birth to more concepts, as the complexity and the needs of our worlds grew.

Why bothering?

When we are challenged to design a solution for a new functionality or grow existing one, we might feel the urge to jump into thinking about "services", "calling APIs", "doing requests" or "writing a bunch of classes".

It might be that we say "oh, it's easy" and just start implementing.

New class here, refactoring there, tests, configuration - boom! It's all done.

But it already might be too late.

You might ask: why?

The feature is done.

Users are happy.

Stakeholders are happy.

We are happy!

Everyone are happy but Damian is moaning that it's too late.

Too late?

Each area of human activity is full of such ideas.

Those ideas, interactions between them, and how they are organized, might imply certain ways of using them.

And exactly this impacts how could one model them properly within a specific context, creating solutions for business problems that aim at providing value.

The nature of the problem, the area in which our solution is going to operate and live, imposes certain qualities on us, designers.

When we jump immediately to "solutioning", we might lost our chance to learn and name concepts.

And what gets named - can be discussed, controlled, measured, understood, and so on.

Typically, in the urge to finish stuff before deadlines, it might feel desirable to focus on what has already been built and utilize it for providing next solution(s).

But is this the right approach? (note that I didn't add "always" to this question, although our mind immediately tries to generalize and implicitly puts that into "always-ness" context).

Shouldn't we firstly think, name and make visible what is possible in the area of human activity, that we are currently working with?

"Just" note it.

Without special structure, treat this as a draft.

Not bind ourselves to classes, records, discriminated unions, functions, API calls, database schemas - a mere ideas that are worth knowing.

Guided tours

Imagine that we are working in a tourism industry.

We have built a solution for guides to improve the process of creating, managing and operating a tour.

To make things easier at the beginning, we started with a single day tours.

A Tour.

Maybe with a Type property.

Easy, clean and growth-enabling model.

Eventually, our stakeholders want to support something that they call "period-based tours".

Existing structure attracts certain ways of handling this new requirement.

The easiest is to just add another value to the Type property and handle it using some ifs somewhere.

But we already know that Simple isn't easy.

Just for a while, let's forget about the classes, types and properties.

Write down two distinct concepts.

Single day tour and Period-based tour.

We could go further with exploration and try to understand "why, who, what and when" for each of them.

Eventually, we could learn that Single day tour typically involves single guide, single venue, whereas Period-based tour requires many guides, there are multiple cities and variety of venues.

How should we model it then?

Is this a "single Tour"?

A model lives to be useful and serve a purpose

If above thinking process looks as a non-scientific, art-like-looking - you are perfectly right.

It's just playing with ideas.

Working with concepts and terms.

We might still decide that a single Tour does the job, for now, and that it's still the easiest way of building a working model.

What I am trying to emphasize here is that we should carefully, with full awareness, observe when our "model" induce friction.

I believe this would be the moment of "going back to the whiteboard" and bring the concepts in front of us (you might be interested in Concept maps).

Ok, but where's the architecture?

From concepts to architecture

All the ideas (implicit or explicit) relate to each other and have specific interactions between them, at some point of time.

The "architecture" is overloaded term itself, but taking into account what we've explored in The ambiguity of software architecture:

Software architecture

A way of how we are organizing things to achieve or support X

The way we organize concepts, ideas, and terms, that thrive, live, and are used inside the context we're working in, impose certain designs and solutions.

As in our tiny example - modeling two distinct concepts - "running a single day tour" and "running a period-based tour" - with a single "entity" eventually leads to creating single API, probably using single database table.

Of course, one might say that modeling them separately is speculative and has nothing to do with reality.

But then, what is reality?

Everything is a mere, pure idea.

I am more convinced that we would experience a model friction when we would have two separate models rather than a single one.

With a single Tour, we might eventually add more and more to it.

Look ma', everyone is happy!

But the way we organize concepts, ideas, and terms, attracts/detracts or encourages/discourages certain ways of organizing technical components.

Planning?

Within our toy example, someone might decide that guided tour planning is a very important aspect to support.

At some point, an important stakeholder might decide to expand the market so that our software needs to support planning music events.

It's so tempting to say that both of those human activities are related to planning.

And that's right.

Even if it does not make any sense to clump them together, I firmly believe, dear Reader, that some people, in the name of reusability and generic solutions, would try to merge them.

And I think you could also find similar examples in your experience, dear Reader.

Representing two different concepts by the same thing - either it is a Plan, Document, Tour, Order, Book - imposes the architecture of software components that we're going to build.

The lost art of conceptualization

I feel that the more technologically advanced we are, in terms of the IT capabilities, the more we lose intuition for growing software.

With so many shiny tools at our disposal, it's easy to forget about how we organize all ideas/terms/concepts.

And organization organizes the ways we can build stuff.

How could one decompose and organize concepts so that it supports certain technical solutions and qualities?

David West, "Object Thinking"

The Taoist butcher used but a single knife, without the need to sharpen it, during his entire career of many years. When asked how he accomplished this feat, he paused, then answered, "I simply cut where the meat isn't"

Say hi to "DDD".

And no, I am not talking about aggregates, value objects or domain services.

I am talking about focusing on the language, focusing on the area of human/business activities, that we are supposed to build solution to.

In my mind, the first and the most important step is to work on and with the ideas, living in the given context.

"Cutting where the meat isn't" is where things are naturally become "decomposable".

Of course, it doesn't mean they are TOTALLY DECOUPLED.

Concepts and ideas collide, interact and throw parties too.

"The law of eventual composition" still applies (what's that? I will soon publish an article on this topic).

Due to The ambiguity of abstraction, we might treat them immediately as technical things.

But they are ideas - or if you like - abstractions.

Nowadays, we drown in the abundance of tools and our it's easy to pay attention to these.

But we have a scarcity of attention located in the software design.

If we take some books from "the old ages and centuries" (please note I am exaggerating), we might get interesting insights.

"Prefactoring"

Let's start with Prefactoring by Ken Pugh.

Ken Pugh, "Prefactoring"

Create a clearly defined name for each concept in a system.

Then he follows:

Ken Pugh, "Prefactoring"

Sometimes it is hard to determine whether you have two independent concepts or one. Try making up a one-line definition for a name. If it is difficult to create a simple definition, go ahead and use two names. Later on, if you find that the distinction was meaningless, you can always declare the two names to be synonyms.

Also, Ken discusses something that he calls "Splitters Versus Lumpers":

Ken Pugh, "Prefactoring" - splitters vs lumpers

It is easier to combine two concepts than it is to separate them. Splitters can be lumped more easily than lumpers can be split.

And lumping means:

Ken Pugh, "Prefactoring" - lumping definition

Lumping involves using a single name for two different concepts. Lumping can hide relevant distinction between concepts.

Thanks to the separation, we eventually might notice that there's a friction between two "splitters".

What's more, designing two "splitters" might eventually be labeled as "duplication".

But if we remind ourselves Modularity Uncertainty Heuristic:

Modularity Uncertainty Heuristic

(Re)Assigning responsibilities and noticing the high coupling in a system (here: between concepts) cannot be applied simultaneously with absolute ease

Of course, no one states that combining two concepts would be really easy (as there might be structure that grew around), but it becomes visible and explicit!

"99 Bottles of OOP"

Now, let's move to 99 Bottles of OOP by Sandi Metz.

Just by searching for a word "concept" gives us so many results back. Here's one that I find really interesting:

Sandi Metz, "99 Bottles of OOP"

Combining many ideas into a small section of code makes it difficult to isolate and name any single concept.

We all are taught to avoid duplication, but in fact mindful duplication might be our friend:

Sandi Metz, "99 Bottles of OOP"

Duplication is useful when it supplies independent, specific examples of a general concept that you don't yet understand.

As you might guess, I am advocating for total duplication. (yes, I am exaggerating.)

What I am trying to convey here is that thinking about concepts and ideas gives us space when exploring, analysing and then eventually modeling.

Knowledge duplication might make space for growing understanding by deferring lumping two concepts together.

Sandi Metz, "99 Bottles of OOP"

By definition, future readers of your code know less than you know now. They are swimming in murky water, bumping into dimly perceived concepts, searching for clarity.

How verbose and explicit one needs to be to make the understanding clear for future oneself and others?

"Essential Skills For The Agile Developer"

And finally, let's look into Essential Skills For The Agile Developer by Net Objectives.

One of the chapters and section names states that:

"Essential Skills For The Agile Developer"

Using Nouns and Verbs as a Guide: Warning Danger Ahead

There might be various meanings but my interpretation, especially in the context of this tiny tale, is that we might firstly focus on what is primal - concepts, terms and ideas.

Based on their analysis, we eventually might suggest models and solutions.

Finally, Commonality and Variability Analysis (CVA shortly) gets introduced.

In my eyes, this is really interesting and profound "method" for looking at concepts, living in the area of business/human activity, we are currently working on.

"Essential Skills For The Agile Developer"

Jim Coplien's work describes an approach to finding variations in the problem domain and identifying what is common across domain. Identify where things vary ("Commonality Analysis") and then identify how they vary ("Variability Analysis")

Then, they discuss "Commonality Analysis":

"Essential Skills For The Agile Developer"

According to Coplien, "Commonality Analysis is the search for the common elements that helps us understand how family members are the same. By "family members," Coplien means elements that are related to each other by the situation in which they appear or the function they perform

Those "common elements" might lead to formulating a cohesive idea, concept or - abstraction if you will.

And, as it is suggested, we might explore the area by looking on what is the usage of underlying terms and what role they play.

Concluding on CVA:

"Essential Skills For The Agile Developer"

Commonality-Variability Analysis offers a different way of decomposing the domain, resulting in decoupled concepts.

"Decoupled concepts" - we might not know how we are going to represent a given concept, but it is vastly important to reveal, to capture and to note the important ideas.

It all ends with an idea

The way we are going to organize concepts and represent in our system matters.

The software architecture, the solution space - the way we organize technical components to achieve our goals and satisfy business requirements - seems definitely driven by the underlaying concepts.

And here we also have design decision power - we might overdesign or underdesign representation of all the ideas that we might, or might not, "harvest".

It might not matter whether we are designing/specifying low-level classes/function or APIs, or processes - corrupted "conceptualization" will not help or make things easier to evolve.

We live in the VUCA world - Volatility, Uncertainty, Complexity and Ambiguity - and exactly that's why we need to have at least some stable ground to work as a good foundation to our technology and design choices.

We still might create new ideas, discover new concepts and bring new terms to life - and yet again this will drive our thinking and solutioning.

Not the way around.

We might eventually stop lumping Concepts, Entities, Data together and identify the ideas - just ideas.

Software architecture might become flawed, skewed and under-engineered because we didn't put enough attention to ideas that precede any technological choices.

Next time, dear Reader, when working on a problem or trying to find a good solution, give yourself a playful moment and try to ask a question:

Question 🤔

What are the ideas and concepts I am currently working with?