Might like to wear cotton, might like to wear silk
Might like to drink whiskey, might like to drink milk
You might like to eat caviar, you might like to eat bread
You may be sleeping on the floor, sleeping in a king-sized bed
But you’re gonna have to serve somebody, yes indeed
You’re gonna have to serve somebody
Well, it may be the devil or it may be the Lord
But you’re gonna have to serve somebody
——– Gotta Serve Somebody, Bob Dylan, 1979
Whatever our product is, whatever its design is, it goes to serve somebody. Somebody who our product provides experiences for. In product management and system engineering, that somebody is usually called an Actor.
In software engineering the term client-server is frequently used, a technical tuple describing a relationship between two applications, and maybe a chained one between multiple applications. Unfortunately, the client is not necessarily a customer-facing application. We also tend to use the tuple of frontend-backend, but the first is mostly referring to the discipline of Web Development, where not all customer-facing applications are web applications.
In this series, to overcome some ambiguity, we’re going to borrow the term Actor to describe our most forward facing application. The applications who represent our customers, exposes user interfaces to them, for the two to interact with each other, over an experience.
In this chapter, we’re going to start a very long journey to serve our Actors with the most Reliable experience. Starting with why it is not so easy to do.
The Theatrics
One of Silo’s primary functions was Food Management, and it was a shared one between three Actors:
- A physical device – a physical entity manufactured in multiple factories in China. It had four applications running adjacently. One was coded in Embedded C++, running on a microcontroller and controlling all hardware elements. Another was the updater, and the main one. Both were coded in C++ 14. The last one was Amazon Alexa.
- A mobile application – an Android or iOS application, distributed through app stores, coded in React Native (JavaScript).
- An Alexa Skill – a wild animal. It was coded in Node.JS (JavaScript), running in our backend, but its output was played to our customers by several Amazon Echo Dot physical devices. Silo’s was only one out of many.
Later in this series we’ll be defining what Food Management and primary functions are, for now we’ll keep it abstract to focus on the principles. Whatever it is, no matter how many User Journeys or Flows we will come up with, we already know that each Actor is limited in how it can participate in an experience due to each one’s unique input interface. Alexa Skill is voice operated, the mobile application is touch operated, and the physical device is button operated with only 4 of those plus an LCD screen (don’t ask why).
Abstractly, let’s think about textual input. In mobile and web applications, a text is typed in character after character via a keyboard. Either a physical one or a virtual one. To ease it up and avoid typos, most products provide some kind of auto-complete mechanism. Similar but different, in voice operated devices the textual input is a series of spoken terms. Auto-complete is physically impossible without a screen, but speaking out loud did-you-mean is a viable option. Unfortunately, no kind of textual input is available from a physical device with just 4 buttons.
Within Food Management, there was a User Journey named Food Labeling. In it, a customer would give a textual name to a food item. As each Actor’s interface is different, each Actor’s implementation of Food Labeling was different.
It would be different whether he’d type it in via his mobile application or if he’d be shouting out loud “Alexa, I have strawberries!”. So although it seems like one User Journey, it is actually three independent User Journey. Each Actor would be holding an inexact copy of Food Labeling.
There is another kind of User Journey, such as Onboarding. It is composed of multiple smaller distinct User Journeys, executed one after the other. These distinct ones might be spread and coded across multiple Actors, all Actors together participate in the bigger User Story.
If we’ll inverse the way we described the relationship between User Journey and Actors, it is safe to say each Actor would contain a set of independent User Journeys. If all of our Actors are also physically mutually exclusive, each set would also be independently deployed. The User Journeys themselves are mutually exclusive as well.
Unfortunately, our backend may not be. Not yet anyhow.
Acting Out
One of our Actors is a wild animal. The Alexa Skill is running in our backend but its output is blurted out by physical devices. Unfortunately, it’s currently residing within a design Restricting it all to one single application. The one who will eventually evolve into one big Monolith. It would contain both our Actor, and everything else.
When a product manager would come up with a Cause, and an engineer would make a Change, it would be bundled together with all the other Changes made to the Monolith.
When our engineer would unintentionally deploy an Instability in the Actor’s Module, it has the potential to bring down the entire system. It would also cause a downtime for all the other customer-facing Actors, Silo’s physical device included. Not what is expected of a smart kitchen appliance, that is expected to just work, just like your microwave.
Vice-versa, when our engineer would unintentionally introduce an Instability in a shared Module inside the Monolith, it might unintentionally introduce an Instability to the Alexa Skill. Once again a customer-facing Actor would be down, be useless for a while.
Obviously, our other two Actors are physically apart from our backend, they are already mandatorily split away from it. On the contrary, splitting away the Alexa Skill Actor from our Monolith is an optional split, with its own considerations and implications on the development workflow. With respect to Cohesion and Reliability, it’s easy to see why it would be a beneficial split.
I was wondering if an entire Actor inside a backend application was a unique scenario only for Silo and Alexa. Well, I’m going to surprise you and tell you Silo had a fourth Actor, the factory in China. Serial numbers of manufactured devices and food containers were sent and stored in our databases, to be later used as UUIDs. Come to think of it, there was a future fifth Actor, Silo’s online eCommerce store. And there was a sixth one, a company employee who was in charge of the Food Catalog.
In an imaginative but true story we’ve told before, each simple eCommerce company easily has three Actors. Shoppers, sellers and at least one admin, both can live under one hood of a backend Monolith. At RapidAPI (2021) there were two Actors, the API owners and the API consumers, and each had its backend microservices and micro-frontend applications.
So it may not be a unique scenario of Silo. Not at all.
Ensemble
Let’s apply two actions to our design. The first would be to split the Alexa Skill Actor away to its own independent application. The second would be to rename our so-called Monolith, to an application named Food Management, hinting we had split that away as well from our Monolith.
As Food Management is a closed set of experiences it also has a closed set of Causes, a Source of Change. So splitting it away from the Monolith was splitting two mutually exclusive and independent applications. Consequently, an Instability introduced to the Monolith will not affect Food Management. A more Reliable application. Unfortunately, it still serves three mutually exclusive Actors.
Our Actors, as they are physically deployed to three different planes, can’t Change together and not at once. It goes along well with the fact the three are independent products that shouldn’t Change together. This entails that for the Food Management, each Actor is also a mutually exclusive Source of Change and a Direction of Change.
Let’s recall The Force of Change. One entity’s Cause in another entity’s Change, everything is a Change-Change cycle. A Change to an Actor’s User Journey would lead to a Change in Food Management. Our Actor’s independent User Journeys are indirectly bundled together in the backend application. A Change to one Actor can lead to an Instability to another Actor. Exactly why Reliability has a lot to do with Cohesion of Change. By the end of this series, we’ll come up with a system architecture resolving this issue.
Backstage
Let’s investigate another indirect bundling between our Actors. One User Journey out of many in Food Management, is a customer navigating through his Food Inventory. Although each Actor has its own implementation of it due to different user interfaces, all three still depend on the same data persisted.
On the mobile device, in order to select and view one item out of many, we usually navigate through the list by scrolling and tapping on items. For a physical device, that would require us to continuously press on a physical button to get to the item we want, which is a horrible experience of its own. Let’s say we did “select” one, there may not be enough space on its LCD screen to show the entire item, and scrolling by pushing physical buttons is a horrible experience. So although both Actors have different experiences, they may still somehow both utilize the same view of the data. But it is not to be taken for granted.
It sounds like Food Inventory is a data structure that is infrequently Changing. It calls out to choose a relational/SQL database, and implement a few SQL queries. Maybe even multiple views when required. But maybe for some other reason, we’ve chosen a document-based database such as DynamoDB or Mongo. In both cases, we’d be running into a wall. Neither a SQL nor a Document database can create a view which supports the voice experience of our third actor, the Alexa Skill.
In an Alexa experience, it would be a customer asking out loud “Alexa, how many pieces of toast do I have left?”. In that sentence, there is no UUID for the item to be grabbed from the database. Is the item’s UUID “pieces of toast” or is it just “toast”? And what if it is actually stored as “bread”?
Contextual understanding of toast <-> bread is an inherent capability of textual search engines, such as ElasticSearch or Solr. Unfortunately, neither is suited to persist the entire Food Inventory data as NoSQL data structures are tightly coupled to the queries to answer. If we structure it around textual queries, it would be extremely hard if even possible to create views for the other Actor’s experiences. If not present ones, future unknown ones.
No one database to rule them all. Our instinctive solution would be to have two. Although inevitable, splitting and mirroring our data between multiple databases, and/or encapsulating each one with a Service, are both challenging on their own. And even so, two Actors would still be indirectly bundled through one of those databases. An unintentional Instability is still possible to be made via a Change to either the data model or to the database itself. If it goes down, it would bring down two Actors together. We’ll need to address these issues too.
These contradictions occur because the way we store and model our data is due to Technical Causes and considerations. On the contrary, the way we wish to view and mutate our data for an experience is due to Behavioral Causes and considerations, which are the User Journeys. And as these may overlap, it may lead to an incorrect dichotomy.
Thaw
Silo’s final product contained only the two most critical User Journeys, The Food Labeling and the Food Inventory. Both live under the hood of Food Management. Prior to the MVP we had a successful PoC, 30 physical devices manufactured and delivered to customers in the US. We already knew more User Journeys would come, such as Freshness Management and Notifications for when your food is about to go bad. And further up the road was Food Replenishment, whatever that is. And who knows what’s afterwards.
Food Management is an ever ending Changing product. There is barely any limit to what it is and what if would be – because it is an abstract. Just like business domains are. Consequently, our Food Management backend application and its design will continue to withstand a Throughput of Change for years to come. Will it withstand it and how soon will it evolve to a Monolith? Who knows.
What’s for sure is that because it’s an ever Changing product it will never be as Reliable as a frozen application. What’s more for sure is while Food Management is an abstract, the User Journeys aren’t. Those will be frozen, sooner than later.
In the next chapter, we’re to start resolving all of the above issues. By trying to split the Food Management to its Actors.