One guideline to test them all — Part 2

The Anatomy of a Product

Mehmet Yatkı
5 min readSep 23, 2019
Photo by Austin Ban on Unsplash

Maybe you have heard of Hero’s journey. If you haven’t; it’s a template which was created by anthropologist Edward Burnett Tylor, based on his observations of common patterns in plots of heroes’ journeys. You can simply take any tale or story that involves a hero, take all different stages that hero goes through and fit it into this template.

Image is based on The Hero with a Thousand Faces by Joseph Campbell

Inspired by that and based on my observations, but also knowing that “All models are wrong”, I wanted to create a similar template for our industry. If all these complex, emotional stories can fit into a template, why not the same thing can’t be done for our deterministic products? Hence, the anatomy of the product model is here.

I’d like to simply say every product has 5 types of essential components: a public interface, a business logic, an internal state, a global state, and I/O connectors. Yes, that’s pretty much it. But of course, this may not make sense if we don’t define what a product is. So let’s do this first.

Product

A Product is a piece of software that solves a business requirement. It can be a simple Javascript library like camelcase, lodash, or a framework like Express, can be a microservice, CLI tool, frontend application, a backend service or even a single executable, or a background service in an operating system. It can be open source or closed source, free or non-free.

A Product with all its components

A product has 5 components

  • Internal State
  • Global State
  • I/O Connectors
  • Public Interface
  • Business Logic

Components of a Product

Internal state is any information that we store in memory on runtime with local variables that were created and can be modified only by business logic. Thus we access the internal state via Javascript variables.

Global state refers to any information that is stored/handled by external sources, and the product’s business logic depends on it. It is possible that the global state can be modified outside of the business logic’s control. Therefore, the business logic may need to verify the integrity of the information. Also please note that config files and time are part of the global state.

I/O connector is usually a third-party solution on which business logic depends to make an I/O operation.

I/O operation refers to any interaction that is performed by the business logic to read or modify the global state. Generally, the most expensive operations in a product are I/O operations since they can rely on a file system, network, or memory. Also, hardware limitations can affect the time needed to perform I/O operations For example, having slower file read/write speed on an old HDD. Besides that, I/O connectors could also be provided (injected) to business logic through its public interface To give an example, if a public method is a high order function, the global state can be updated with the first-order function which is provided as an argument. An I/O operation doesn’t always have to be async.

A public interface is the gateway for the users to use the service that the product provides. Every interaction with the product must be done through its public interface and public interface must have a clear and complete documentation for users.

A business logic is an algorithm that solves a business requirement by optionally providing a public interface and optionally using a global and/or internal state. For instance: solving a math problem, or transforming data and saving that data somewhere. Business logic usually needs to work fast and can be always optimized since developers have control over it. Business logic can sometimes do expensive operations too. For instance, encryption/decryption, iteration on big data, or regex searches, etc.

Lastly, a product may not need all the components above. This means a product may not have a business logic or an internal state, or may not depend on a global state thus may not be doing any I/O operations. Although, a product cannot have an internal state without having a business logic.

Now, before designing a product, we have to think about which of these components are required and which technologies are going to be used to achieve these structures. Even though these definitions can apply to any type of software, I’ll be mainly focusing on Javascript in this article. So, I’m assuming your business logic is always going to be written with Javascript. The internal state is always stored in the application memory. However, the public interface, I/O connectors, and global state can vary based on the type of product we are developing.

A global state:

  • Could be stored in application memory. In that case, the I/O connector is the Javascript API. For instance: global, process, console, require, window, document.
  • Could be stored in the file system. In that case, the I/O connector is a third party javascript module. For instance: fs, fs-extra.
  • Could be stored in persistent or temporary storage. If that’s the case, the I/O connector is a javascript client for that storage technology. For instance: mongod, mongoose, mysql, pg, redis, window.localStorage.
  • Could be a web service. In that case, the I/O connector could be an HTTP or WebSocket client. For instance: axios, got, node-rest-client, socket.io-client.
  • Could be the state of other processes or microservices. In that case, I/O connector could be an IPC or RPC client. For example, node-ipc, pm2-axon.
  • Could be a mail service that we use to send/read emails. In that case, I/O connector is an email client. For instance: nodemailer.
  • could be the wall clock or CPU time. In that case, the I/O connector is either Javascript’s Date API or setTimeout/setInterval functions.

A public interface:

  • for a frontend application, could be a Web UI & events (click, change).
  • for a Javascript library, could be exported functions or public methods.
  • for a web service, could be HTTP or WebSocket endpoints.
  • for a command-line tool, could be arguments and flag options.

Above, I tried to give you a better understanding by categorizing possible ways to have a global state and public interface. Of course, I may have missed some of the other ways that we could interact with the global state or public interface. Please feel free to add them in the comments. However, these should be enough to help you identify what is an I/O connector and what can be defined as the global state.

Now I encourage you to take a look at your or other projects and identify these components. Especially, please focus on detecting which of the project dependencies are I/O connectors. Because they will become extremely important in the upcoming sections ;)

--

--