Proceed with Caution: Atomic Design's Impact on Your Thought Process
Tags: react, abstraction, refactoring, atomicdesign
Published: 2023-07-30 10:26:56


In web development, [Atomic design](https://bradfrost.com/blog/post/atomic-web-design/) is a methodology for creating [design systems](https://en.wikipedia.org/wiki/Design_system).

It suggests breaking up your components into 5 levels, Atoms are your most basic building block & each level combines elements from levels below it:

1. Atoms     (made up of HTML tags)
2. Molecules (made up of atoms)
3. Organisms (made up of molecules)
4. Templates (made up of organisms)
5. Pages     (filled in template)

I believe this is a great way for *designers* to think, they can break down a design into reusable & configurable pieces. I've seen this work well for creating quick & consistent *Figma designs*.

However, I think there are some problems with how this methodology can encourage **developers to think**.

## Rigid abstraction levels, unnatural categorisation & bike-shedding

Atomic design suggests 5 core levels of abstraction, but to me this is an arbitrary set of component categories.

One problem with having these fixed categories is that it can encourage developers to break down their components into levels that are **unnatural for the domain** you may be working in, just to them fit into the suggested categories.

We've all learnt a new pattern or technique & then excitedly attempt to use it *everywhere*, when applying Atomic Design you can easily waste time trying to break down your code into these levels instead of **just adding the feature**.

The act of categorisation can itself be a task.

For example, I might want a page with this structure:
```
- Page
  - TableForFinance
    - TableRow x n
      - TableCell x n
```

The `TableCell` would be a `td` & the `TableRow` a `tr` which would make them [Atoms](https://bradfrost.com/blog/post/atomic-web-design/#atoms:~:text=Atoms%20are%20the%20basic%20building%20blocks%20of%20matter.%20Applied%20to%20web%20interfaces%2C%20atoms%20are%20our%20HTML%20tags%2C%20such%20as%20a%20form%20label%2C%20an%20input%20or%20a%20button.) in Atomic design, but `tr` tags ***must*** wrap `td` tags.

So does that make `TableRow` an Atom or Molecule? (feels like a pretty [small Molecule](https://bradfrost.com/blog/post/atomic-web-design/#:~:text=Molecules%20are%20groups%20of%20atoms%20bonded%20together%20and%20are%20the%20smallest%20fundamental%20units%20of%20a%20compound.))

The `TableForFinance` feels like an organism, but if we do decide that `TableRow` & `TableCell` are Atoms, does that make it a Molecule? (seems like a pretty [big "Molecule"](https://bradfrost.com/blog/post/atomic-web-design/#:~:text=Molecules%20are%20groups%20of%20atoms%20bonded%20together%20and%20are%20the%20smallest%20fundamental%20units%20of%20a%20compound.))

These are questions that you are forced to ask yourself constantly with this methodology; the reality is there is probably a near infinite (in theory) spectrum of abstraction levels for your components.

So why try to force it into one of five?

You & your team will consume [mental effort](https://en.wikipedia.org/wiki/Decision_fatigue) & time [bike-shedding](https://en.wikipedia.org/wiki/Law_of_triviality) about how to classify anything but the most trivial components.

I think the reason component categorisation efforts like Atomic Design exist is because people crave some form of organisation / folder structure **early in a project** & want to know where to start.

It's better to start with a simple `/components` folder & let its structure evolve naturally as patterns start to emerge.

## Encourages making half-baked components "Public"

Atomic Design's separation of components into Atoms, Molecules, Organisms etc encourages developers to create components that are abstract enough for "generic usage", again ***the idea*** is that you create composable pieces to assemble into what you need & you get to "reuse" these pieces generously.

The problem with this is when you add a new "generic" atom, molecule etc..., you typically only have a single use case to work from.

By attempting to make a "generic" component so early & with so **few** usecases you are **guessing** what the requirements of that component might be in the future and potentially adding features [you might not need.](https://en.wikipedia.org/wiki/You_aren%27t_gonna_need_it)

Whatever component you come up with is likely to be **half-baked** & will almost certainly have to change to accommodate new use cases in future.

Now, a half-baked component ***isn't necessarily a bad thing*** if its usages is *limited* (e.g. to a single file) because work on it can be completed quickly, and with limited usage you can refactor it at will.

A problem arises when you **expose** half-baked components to be used freely in the rest of your application by dropping them in a `/components/...` folder.

Take our desired page from earlier:
```
- Page
  - TableForFinance
    - TableRow x n
      - TableCell x n
```

Imagine we implement this by having a separate component file for each level:
- TableForFinance.jsx (organism)
- TableRow.jsx (molecule)
- TableCell.jsx (atom)

We've created "generic" `TableRow` & `TableCell` components that we can reuse & placed them in the appropriate Atomic Design `/components/...` folder.

Later, it *seems* correct to add 4 other tables that also make use of `TableRow` & `TableCell`:
- TableForBills
- TableForSales
- TableForEmployees
- TableForEquipment

Hurrah! This seems like a great application of [DRY](https://en.wikipedia.org/wiki/Don%27t_repeat_yourself) & a win for reuse, but what you actually have is a set of dependencies (`TableRow` / `TableCell`) that tightly couple all of the tables that use them together.

Imagine if bit by bit `TableForBills`, `TableForSales` & `TableForEmployees` start to diverge in terms of feature set & the new features require changes to `TableRow` / `TableCell`.

Any changes made to these components will affect **all** tables that use them & what typically happens at this point is implementation details about the parent component start to "leak" into the children, think `if (tableType == 'Sales') ...`. This will lead to  a more complex `TableRow` / `TableCell` component & increasingly complex test permutations.

Imagine this in a real world situation with a **much more complex** dependency chain... painful.

A better way to approach this is to:

Define a single `TableForFinance.jsx` file that has components defined **inside it** for `TableRow` & `TableCell`, these are internal to the `TableForFinance` & are considered part of its implementation, we don't expose them to the rest of the App

When we need a new similar table, e.g. `TableForBills`, we copy `TableForFinance.jsx` wholesale & change what is necessary

This allows `TableForFinance` & `TableForBills` or any other component to evolve independently without being coupled, their internal logic will be simpler & easier to test

Only when we have **more examples** to spot potential **patterns**, is when we should extract whatever logic is common between all use cases into a new component


## If you or your team is actively using Atomic Design

Come to an agreement about what should be classified as an Atom, Molecule, Organism, Template, Page, this should help limit how much time you spend on these discussions
 
Ask yourself "Am I breaking down this feature into these levels because it **makes sense** or to 'fit' into the Atomic Design structure" 

Let new components "bake" for longer by keeping them internal initially, then copy and pasting code into more use cases.

Create a better abstraction only once you have enough usecases to spot patterns. It might be handy to remind yourself that this could be refactored later with a ticket, it may or may not be done in future


## If you are considering using Atomic Design

Consider if you actually need this structure yet.

You might be able to start with a `/components` folder.

Let your components evolve naturally depending on what features you need & extract new "reusable" components once you have enough usecases to spot patterns

Once you have enough components to justify some more rigid organisation, consider adopting a more structured folder strategy (*possibly* Atomic Design)

At this point, you will hopefully have plenty of components to spot opportunities for refactoring & ways to group related components


## Finally

Organising your code into coherent files & folders can be even more mentally taxing than writing feature code & you will almost certainly get it wrong the first time - doing this is easier with hindsight. 

If you can, you should treat it as an entirely separate refactoring task & give it the appropriate time and attention.