UI? Piece a Cake
Tags: react, webdev, cleancode
Published: 2021-10-31 08:17:41

---
title: UI? Piece a Cake
published: true
description: A Layered Cake approach to author your code
tags: react, webdev, cleancode
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/17idwkokuf6yiko29zmh.png
series: Do not just code
---

One of the first `false assumptions` one could face during a long journey of becoming a developer, is that said journey is just about development, about you just writing some code. 
Like - start a new project by writing code, and finish it in the same way. 
Only later one will be told about testing, or the need to solve real customer problems, and other "business as usual" stuff, not sure which one came first. 
It's fine to start your journey in this way, everything needs a beginning, but this is not how it should continue.

This is not how it could succeed.

Our job is not about writing code, it's about writing the right code, writing it "Once and only Once",  testing it, solving problems and finishing assigned tasks.
```
It's not about creating >new< things, 
but more usually about changing the >old< ones.
```
> Software engineering is programming over time. Engineering is about considering the long-term effects of your code. Both direct and indirect. [Link](https://swizec.com/blog/what-i-learned-from-software-engineering-at-google/)

Read it this way - while moving forward think hard about what you are leaving behind and what you need to make the next step.
💡 Applicable to your life as well.

While the vast majority of information you can find out there is focused on how to "make" things, let's talk about future maintenance, about reducing different burdens - from the classical `technical debt` to `cognitive load`.

Let's talk about the multidimensional "Cake" approach, also known as [Multitier architecture](https://en.wikipedia.org/wiki/Multitier_architecture), also known as Onion Architecture, and how it applies to UI-based applications.

# Where is the Problem?
The problem is not only "where", the problem is also "when".

Let's imagine that you are working on a very agile project, of course you are, and you just bootstrapped a new application that already has experienced two pivots and going to have another one.
> The essence of Agile is about being reactive to the changes.

It is absolutely ok to start a random redesign, it's absolutely ok to abandon an almost complete feature and start redoing it in a little different way, it's ok to adapt for the new requirements and the only thing a developer should be focused on this point - be able to __preserve as much as possible__, about how NOT to start every time from the scratch. That happens to all of us but is not any efficient.

While the majority might understand the solution for a "Happy Live" as [Pit of Success](https://blog.codinghorror.com/falling-into-the-pit-of-success/), where _a well-designed system makes it easy to do the right things and annoying (but not impossible) to do the wrong things_, it's still about _making_ things(note "do the right thing"), not _changing_ something existing, even something created yesterday (we "agile", right 🙄?).
I reckon the Solution for the _change_ might have roots in the [Chaos Engineering](https://en.wikipedia.org/wiki/Chaos_engineering), where you have to accept that something will go wrong, a build a system resilient to it. While the essence of this philosophy is something you should always have in mind, there is another way to address the same problem.
> Chaos? This is what happens during(before or instead of) the family Christmas dinner, meetup and especially during the Gala Concert - too many things are happening at once and everybody is just running around screaming. In all cases, the path to success is to have some protocols, procedures and preparation. 
Path to Success is a proper foundation.
----

[Standing on the shoulders of Giants](https://en.wikipedia.org/wiki/Standing_on_the_shoulders_of_giants) - a general concept that _prior_ knowledge, and not only knowledge, could and should be used today 👇
> Using the understanding gained by major thinkers who have gone before in order to make intellectual progress

- every time you use `webpack`, or any other bundler, and do not create your own one - _you stand on the shoulders_
- every time you use `React`, or any other UI abstraction - _you stand on the shoulders_
- every time you use any library, not writing code from scratch - _you stand on the shoulders_

The majority of developers would use some preexisting (third party) solution to solve their problem, and would stand on the shoulders of other developers and "The Platform", but the same majority of developers are also missing the ability to __stand on their own shoulders__.

- 👉 every time you need to _change_ something, there should be a giant you can rely on. You have to _giant_ yourself.

> A digital platform is a foundation of self-service APIs, tools, services, knowledge and support which are arranged as a compelling internal product. Autonomous delivery teams can make use of the platform to deliver product features at a higher pace, with reduced coordination. [What is a 'Platform' anyway?](https://martinfowler.com/articles/talk-about-platforms.html)

![Shoulders](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vuqgqfju9rk38pt0t7pg.jpeg)

## I've seen it
We will jump into some concrete examples shortly, but let's first create some concepts to act as a foundation, let's create our first small Giant, the one you should know very well.

- 🤖 Terminator 1 -> 🦾 Terminator 2 -> 🦿Terminator 3. They all are backing plot of each other. Without the very first you cannot have the second.
- 📖Book (Hobbit) -> 💍Film (Hobbit, well 3 films). While there are many differences between the book and the film, they share the same foundation
- 🤯Total Recall(1990) -> 🤯Total Recall(2012). Those films have nothing in common, except 😉 the same foundation.

Every remake, every sequel or prequel, every film based on a book or a book based on a film are examples of `Standing on the Shoulders of Giants`

![3 spider mans](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dowea2h3a0qili5vtoda.jpeg)

Which other Giants exist?

# Layers
Before you run away let us pick some examples you definitely will understand. Probably it will be more correct to say - that a lot of people for some strange reason expect you to understand it, and once upon a time during every second interview for a JavaScript position you might be asked about this thing, and it always was not very clear for me, like it's 100% not related... until today. 

> The Layers of [OSI Model](OSI model), also known as a internet layer cake

![OSI model](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3hms0xtgh8r4lt7tdu9o.png)

Hey! I said do not run away! Look how one layer of OSI _stands on the shoulders_ of another.
There is no difference for you how _the device you are reading this information from_ is connected to the internet - Ethernet, Wifi, 2G, 4G or 5G - it just works. The top-most(7th) layer is unbound from any network hardware.

> Implementation details are abstracted into many layers and some of those layers are even interchangeable(WiFi!==Ethernet!==Cellular).

---

I hope you would like to experience the same smooth journey during UI development. Strangely, but often developers are trying to _shorten_ processes, _collapse_ layers and especially not _separate concerns_ and trying to get something valuable from it. Again and again, with no Giants supporting them.

---

Well, might be using OSI as an example was a little too much, but 
- would you consider `React` as a `layer`?
- would you consider `webpack` as a `layer`?
- would you consider `MaterialUI` as the next `layer`?
- and `NextJS` as one more extra `layer`?

For the User, there is no difference if an Application has been built with `Nuxt`, `Next` or bare `webpack`. For `webpack` there is also no difference if it is used by the application directly or hidden inside Next. 

Can you see all those giants, on the shoulder of which your application is standing?

> If you ever wonder why your `node_modules` are so huge, better to say GIGANTIC -> that's it! 😅

![node_modules](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dy3dfxverraxmcmaqrq5.png)

---

Another good example is `Redux`, as "Redux" by itself means nothing. It can be very different and you'll never know which recipe was used to bake it.

`Redux`+`Thunk`, or `Redux`+`Saga` provides a little more _context_ for an expected _taste_ of a given solution, however only [RTK](https://redux-toolkit.js.org/) looks like a properly _layered cake_. Mmmm, tasty!

## The Whole and the Parts
Speaking of Redux, there is a very common "mistake" in the understanding of the difference between "Redux" and "Context API". To be more concrete - the difference between `useReducer` + `React.Context API` and `Redux` as the latter is technically the same as "Reducer + Context".
Right after the React Context presentation many people, really many people, were wondering - 🤔 do they really need Redux or what?

Well, probably they didn't, but the more proper way to explain what is wrong with such a common and simple misconception is to refer to [Weinberg’s Law of Decomposition](https://rbcs-us.com/documents/Segue.pdf), which states _"the whole is greater than the sum of its parts"_.
```
Very easy to prove, just combine baking 🤯 soda and vinegar 💥.
```
By the fact, Redux is not only reducers, but also patterns, DevTools, and different middlewares for different use cases. 
While Redux __is__ ContextAPI + Reducer, it is BIGGER than _a sum of its parts_.
> 👉 the real work of an engineer is to see such _bigger wholes_ looking at the pieces. Some things are visible only from a distance. Take a step back, do not focus too much on low-level details.

![Cat geo-glyph](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bnx0yf7pq1t9txhnj1wu.jpeg)

An interesting moment about said law is that it simultaneously states the opposite:
> Weinberg’s Law of Decomposition is subtler. It says that if you
measure a system according to some gauge of functionality or
complexity, and then decompose it, and measure up what you
end up with, that __the sum of the parts is greater than the whole__.
Huh? This seems to contradict the Law of Composition.

The best way to read this is to accept that you are never going to _consume_ something in full, as a whole, only the required pieces. And there will be always some stuff left _unused_.
```
Very easy to prove, just combine Cola and Whiskey 🤢
```

# Foundation: the Essence and the Variables

The very first step towards our goal is the ability to... leave something behind. Separate flyes and cutlets, extract placeholders from templates, and split a single whole into `the Essence and the Variables`.

The best and the most common example for this are `DSL`s - Domain Specific Languages, including any __Template__ languages, including __React__.

> 😉 is there any difference between `<div>{{userName}}</div>` and `<User name={userName}>`?

A very important moment is that the `Essence/Variables` Separation can be performed by:
- moving the `Essence` to the Layer below (sinking functionality)
- `Variables` would be "kept"(emerge) automatically, as you will need to find a way to configure the underlying functionality.

> This concept is almost equal to [Software Product Line](https://en.wikipedia.org/wiki/Software_product_line) - methods, tools and techniques for creating a collection of similar software systems from a shared set of software assets using a common `means of production`.

This is also quite close to the `Ports and Adapters`(hexagonal architecture), where the "actual functionality"(Platform capabilities) is _hidden_ behind Adapters (Essence in this case), which are in turn _hidden_ behind Ports(Variables in this case). 

To better understand, let's create a few examples:
## Button Group
At many sites you might see `Buttons` positioned next to each other. Technically speaking they are __nothing more__ than two `Buttons` placed in one parent and separated by some `Gap`. However, does it mean that this is what you should do?
```tsx
const ActionButtons = () => (
   <div style={{display:'grid', gridGap:'16px'}}>
    <Button>Do</Button>
    <Button>Something</Button>
   </div>
)
```
How many different ways you know to create said gap, and how many different `gaps` you can use - 2px, 4px, 20px?
Probably said `gap` should be proportional to `Button` size to create a "coupling" between two buttons and let you use a larger gap to create a "distinction".

This is why it's very important to create an abstraction - `ButtonGroup`
```tsx
   <ButtonGroup /* don't think how*/>
    <Button>Do</Button>
    <Button>Something</Button>
   </ButtonGroup>
```
Or even give underlying logic more control over look-n-feel,  and create an opportunity to _collapse_ a few Buttons in one group into one `Dropdown` on mobile devices.
```tsx
  { ifMobile ? (
    <Dropdown caption="Edit">
      <Button>Edit</Button>
      <Button>Delete</Button>
    </Dropdown>
   ): (
    <ButtonGroup>
      <Button>Edit</Button>
      <Button>Delete</Button>
    </ButtonGroup>
  // ⬇️⬇️⬇️⬇️
  <ResponsiveButtonGroup
    actions={[
     { label: 'Edit', action: ... },
     { label: 'Delete', action: ... },
  /> 
```
Move one _giant shoulder_ up. There are so many _reasons_ to have buttons grouped somehow, and all those use cases can be named to be used for a known reason!

![Button groups](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ddvh30tkvfvsco67su5f.png)

## Table
`Table` is another example where second-abstractions can help you a lot.
Let's imagine you need to display a table. You have two options:
- render the table by yourself
- use some other library to do it

In the first case you might need to spend more time than needed to handle edge cases, implement virtualisation, sorting, you name it.
In the second case, you might find any particular library to not matching your expectations in some _details_ with no ability to change the pre-backed solution.

Often in such case, developers are picking the first case as the only possible, while they always need the second - some "solution" they can just use. It just has to be "as they want". 
In the `Component Approach` such a solution is known as a `Component` 🤷‍♂️, no more, no less.

So, yes, go with option one, pick your way to render HTML, not a big deal, pick the way you do(if you do) virtualisation, pick the way to handle "table data" - there are many _headless_ tables on NPM, and assemble in a way you need.
If one day later you will have another use case with slightly different requirements - create another `Component`, assembled in another way.
But it is important to have this _intermediate_ abstraction layer, which states "This is how tables are made here", as exactly this point may change in time (redesign) and you want to avoid [Shotgun surgery](https://en.wikipedia.org/wiki/Shotgun_surgery) or [Domino Effect](https://en.wikipedia.org/wiki/Domino_effect). You want a single change to a single component on UIKit/Design system side, not any client code.

You want to stand on _Giant Shoulders_.

## Modal

Modal is a combination of both cases above.
- `Modal` itself should just provide you a `ModalDialog` functionality.
- But the Application might need:
  - `ConfirmModal`, having a `Cancel button` and the `Action button`, next to each other in [some particular order](https://medium.com/theorem/button-ambiguity-alignment-order-a42736e25334) (depending on the operation system), with (probably) `Cancel` autofocused.
  - `InformationModal`, having only one `Got it button`
  - `OperationModal` to indicate some process and having no buttons.

Plus `FileModal`, that is not a "Design Primitive", but a separate experience with its own rules and principles.

![different modals](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wqg0boro4p55kceyeskd.png)

> `HTML Dialog Element` -> `JS Modal Dialog` -> `Use Case Dialog`

🤷‍♂️ We are ok to use `window.alert` and `window.confirm`, but almost no "UI-library" provide a second-abstraction over their modals to reflect the same functionality.

> Modal is a lower-level construct that is leveraged by the following components:
- Dialog
- Drawer
- Menu
- Popover

> (source [Material UI](https://mui.com/api/modal-unstyled/))

If it is ok for you to use a `Modal` in _some_ patterns, some of which do not look that _modal_, why not create more patterns that are closely relayed to the `Modal`/`Dialog`, but represent particular use case?

```
Once you have a foundation - try to build something from it
and create a foundation for the next layer. 

Then try to build something from it.
```

# Unidirection flow
Another crucial but constantly overlooked part of separating something into layers is the `controlling of the relations`.
- level above should be built on the level below
  - in no circumstances level below can know about layer above
- level above should be confident in the level below
  - level below should change less frequently

This translates into two well known and well hated and thus ignored laws:
- [Law of Demeter](https://en.wikipedia.org/wiki/Law_of_Demeter) or __principle of least knowledge__. Read it as - you can know about webpack, webpack cannot know about you. 
- [Stable Dependency Principle](https://khalilstemmler.com/wiki/stable-dependency-principle/) stating that _components dependencies should be in the direction of stability_, so you can build your code `Once and only once`, and not rewrite it every month to adjust to ever-changing platform.
  - mind [Shotgun surgery](https://en.wikipedia.org/wiki/Shotgun_surgery) and [Domino Effect](https://en.wikipedia.org/wiki/Domino_effect), there is basically no time for it.

# Middle-level architecture

So, UI is a _piece a cake_?
👉 Yes, it is if you think about it as about Cake. Layer on top of another Layer.

Are you already using Onion Architecture, where layers are separated?
👉 Of course. Look inside your `node_modules`, and think how many other packages, libraries and layers are hidden behind the ones you know about.

There are [High-Level Architecture](https://en.wikipedia.org/wiki/High_Level_Architecture)(read pictures) and [Low-Level Architecture](https://en.wikipedia.org/wiki/Low-level_programming_language)(building primitives), what is this one about?
👉 And this one is about something exactly in between - Middle-level Architecture, combining "some given blocks" to create an Application according to HLA. 
The one usually forgotten, and the one you always have to define for yourself by yourself.

# Actionable advice
Take a single component and try to find another structure inside it. Find a `Modal` behind a `Dialog`, find a `FocusLock` behind that `Modal`, go to the very end on the left (atoms) and then go back to the very right(combinations).

Think in Atoms -> Molecules -> Organisms, not from [Atomic Design](https://atomicdesign.bradfrost.com/chapter-2) point of view, but as a unidirectional _complexity flow_. 
Remember the `Table` – you should be able to have a `Complex Component A` break it into pieces and assemble into `Complex Component B`. Then go back to those pieces and break them down.
👉 That is converting a single whole to the Essence and the Variables. 

The point here - layers __should not interfere__, and should not be used in skip levels(Organism should never use Atom) that will enable their reusability and provide maintainability free from `Shotgun Surgery`.

Create a fancy cake, starting from `More Generic` layers and going to `Less Generic` ones. 

![portal cake](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hhu9pzn17x8qd12dfsju.jpeg)

And a cherry on top.