The proximity principle
Tags: proximityprinciple, separationofconcerns, webdev, devrel
Published: 2024-02-29 23:16:39

**Design principles make for better <abbr title="Developer Experience">DX</abbr> and better code.**

_8 minutes or so, 2056 words, 5th grade_

Do you want to write high-performing code quickly, without bugs or tech debt? We hope thatʼs a rhetorical question.

If your answer is yes, then here is the trick: **reduce cognitive load.**

The more you do to keep cognitive load to a minimum, the better your code will be. The greater the cognitive load, the harder it will be to understand and reason about your code.

> Key takeaway

> There are plenty of arguments about the right way to code web applications.

> Some argue for strict “separation of concerns”. Others want to work with a single language. They prefer to generate their <abbr title="HyperText Markup Language">HTML</abbr> and <abbr title="Cascading Style Sheets">CSS</abbr> programatically.

> We beg to differ. We insist that the key to great code is to [minimize cognitive footprint.](https://craft-code.dev/methods/minimize-cognitive-footprint)

> One way to do this is to apply the proximity principle from design thinking to our code. When we do so, it becomes clear how separating HTML, CSS, and JavaScript works to reduce cognitive load.

## On this page

1. [The proximity principle explained](#the-proximity-principle-explained)
2. [Applying the proximity principle to code](#applying-the-proximity-principle-to-code)
3. [Forget separation of concerns](#forget-separation-of-concerns)
    - [HyperText Markup Language](#html) (HTML)
    - [Cascading Style Sheets](#css) (CSS)
    - [Grouping concerns](#grouping-concerns)
4. [Working with a component architecture](#working-with-component-architecture)

<h2 id="the-proximity-principle-explained">The proximity principle explained</h2>

The proximity principle instructs us to group similar items together. And to separate dissimilar items. Generally, we do this with whitespace, but we can also break code into separate files.

You can see this in the design of interfaces:

![The proximity principle at work.](https://craft-code.dev/images/proximity-principle.svg)

As you can see above, A, B, and C are quite similar. Because we have grouped them together, a viewer will immediately grasp this relationship.

X, Y, and Z are somewhat less similar, but still related. But not to A, B, and C. The clear grouping and separation makes this obvious.

The circle 0 appears unique.

But what happens if we ignore the proximity principle? Nothing good:

![Lack of grouping creates confusion and cognitive load.](https://craft-code.dev/images/unproximity-principle.svg)

This same principle applies to code. Why wouldnʼt it? The truth is that **all the design principles apply to written code as well**. Design is design and everything has a design. But is that design arbitrary and accidental? Or thoughtfull and deliberate?

<h2 id="applying-the-proximity-principle-to-code"> Applying the proximity principle to code</h2>

We can apply the **proximity principle** to our code. Most of us already do.

The first level of proximity, or better, _use of whitespace,_ is **within each line of code.**

**Withoutwhitespacecognitiveloadgoesupdramatically.**

How much longer did it take you to parse that sentence? Without spaces you have to make an effort to find the individual words. Our guess is that **the cognitive load increased by at least an order of magnitude.** Thatʼs ten times!

The letters that make up a word have a stronger relationship to each other than to the letters in an adjacent word. So we group the letters of each word and put a space between them and the next word.

Thus, it is difficult to understand why some coders prefer `num=777` to `num = 777`. There are **three** different objects here. The first is a variable name, `num`. The second is the assignment operator, `=`. The last is a number, `777`.

The whitespace makes clear that they serve different purposes. Placing them on the same line and away from other similar code makes clear that they have a relationship. Namely, that they are parts of a single statement.

It is no different in prose. We group words into phrases. We group phrases into sentences. We group sentences into paragraphs and paragraphs into larger blocks, such as sections.

These groups should not be capricious. If we move punctuation arbitrarily, clarity suffers. And although many people appear to have abandoned the idea of a paragraph as a container for a single thought, that remains a better way.

It astonishes us how many programmers argue that this is all a matter of personal style or opinion. **It is not.** The difference in cognitive load is _measurable._ What gain offsets this loss of clarity? The argument for reducing file size ended long ago.

We can also group items using blank lines. This, too, is widely ignored. Weʼre not sure what the benefit is. Failure to use the proximity principle makes for difficult code.

Why write code like this:

```ts
function getOrdinal(cardinalNumber = 0): string {
  const onesDigit = cardinalNumber % 10
  if (onesDigit === 1) { return `${cardinalNumber}st` }
  if (onesDigit === 2) { return `${cardinalNumber}nd` }
  if (onesDigit === 3) { return `${cardinalNumber}rd` }
  return `${cardinalNumber}th`
}
```

When we can write it like this:

```ts
function getOrdinal(cardinalNumber = 0): string {
  const onesDigit = cardinalNumber % 10
 
  if (onesDigit === 1) {
    return `${cardinalNumber}st`
  }

  if (onesDigit === 2) {
    return `${cardinalNumber}nd`
  }

  if (onesDigit === 3) {
    return `${cardinalNumber}rd`
  }

  return `${cardinalNumber}th`
}
```

Good use of whitespace makes code easy to read and understand.

Granted, weʼve taken our compressed example to an extreme. Few would write code quite _that_ compressed. But the point is that we already apply **design principles** to our code to make it much more readable. And that makes a big difference.

In a similar way, we use syntax highlighting for **contrast** and to show functional relationships. Who shuns syntax highlighting? We use it to make clear instantly the function of each bit of code: This is a number. This is a string. This is a keyword. This is an operator.

Way back in 1995, Robin Williams (not _that_ one) wrote [a book on design principles](https://www.goodreads.com/book/show/41597.The_Non_Designer_s_Design_Book) for developers. We at Craft Code have been applying its principles ever since.

She suggested the acronym “CRAP” as an easy way to remember four key design principles. These are **Contrast, Repetition, Alignment, and Proximity.** Misuse them and what do you get? Crap.

How much better to use _all_ the design principles in our code! How is code really any different from prose?

<h2 id="forget-separation-of-concerns"> Forget separation of concerns</h2>

Think **proximity.**

Browsers understand three types of code: HTML, CSS, and JavaScript. Weʼll leave <abbr title="web assembly">WASM</abbr> for another day.

We can send HTML, CSS, and JavaScript to the browser. Or we can send the bare minimum to load a script and then use JavaScript to manipulate the <abbr title="Document Object Model">DOM</abbr> directly.

There are times when the latter approach makes sense. But most of the time, it fails the proximity principle. HTML, CSS, and JavaScript serve different functions. As they are doing different things, it makes sense to group them and move them away from each other.

Skip the next two sections if this is all review to you.

<h3 id="html"> HyperText Markup Language (HTML)</h3>

HTML is the first language of the World Wide Web. Tim Berners-Lee created it to mark up physics documents for sharing. He based it on <abbr title="Standar Generalized Markup Language">SGML</abbr>, the Standard Generalized Markup Language. The goal of SGML is to make documents machine-readable.

HTML is all about **content.** We want to organize that content into a set of nested “boxes”. We do that by marking the start and finish of each box with sets of angle brackets we call “tags”. To make clear where a box ends, we include a slash in the closing tag: `<>content</>`.

But we also want to say whatʼs in the box. To do this, we apply a **label** to the contents. And to be extra clear, we add this label to both the opening and closing tags: `<em>emphasized</em>`. Now our box has _meaning._

We can also provide more information about what is in the “box”. We call this information **metadata** and we add it using **attributes** as key-value pairs. We add the attributes only to the opening HTML tag. For example: `<a href="https://craft-code.dev/">Craft Code</a>`.

We also allow some empty elements and a few empty attributes.

With good use of “CRAP”, we can make our HTML easy to read. We use a monospace font to ensure **alignment.** Then we use **indentation** to show nesting. This makes our code comprehensible at a glance.

```html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta
      content="width=device-width, initial-scale=1"
      name="viewport"
    />

    <title>This is a title</title>
  </head>
  <body>
    <main>
      <h1>Consectetur adipiscing elit</h1>

      <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Praesent in mauris tempus <strong>diam imperdiet gravida</strong> ut non diam. Maecenas in malesuada velit, ut porttitor libero. Proin vitae condimentum metus.</p>
    </main>
  </body>
</html>
```

Machine readable _and_ easily understood by humans.

Note the use of blank lines to group content by function.

If we deliver the above code to a browser, it will display our content. We have not provided any directions for how to style it. But the browser makers will still want to **reduce cognitive load** for the users.

So the browser will apply a **default stylesheet** to the content.

The browser will display the `h1` element in a larger font and on its own line. It will add a new line after the `p` (paragraph) element. But it will bold the contents of the `strong` element and let it flow inline.

The default stylesheet does all this. It helps to make plain HTML content [responsive and accessible.
](http://localhost:3002/essays/code/responsive-by-default)

The most common mistake made by those new to HTML is to **confuse the default stylesheet with the HTML itself.** This is an error. The `p` element **does not have a style.** Neither does the `h1` element.

It is the **default stylesheet** that has the styles.

The `p` element has a _meaning_. It represents a set of one or more sentences that express a single idea or theme.

The `h1` element also has a meaning. It represents the top-level heading (title) for a document or article. There is typically only one per web page.

We call this association of meaning with HTML elements “semantics”. Thus, [semantic HTML.
](http://localhost:3002/glossary/s#semantic-html)

The earliest versions of HTML included elements intended to control presentation. This was before CSS. Those elements (e.g., `font`) are obsolete now. Later versions of HTML abandoned them in favor of keeping the styling separate. Wise move.

<h3 id="css">Cascading Style Sheets (CSS)</h3>

Cascading Style Sheets came along less than a decade into the World Wide Web. By the turn of the millennium, they were in widespread use.

That said, **standardizing CSS** across browsers took quite a bit longer. As did fixing some poor choices. But now pretty much every web site or app uses CSS to control presentation.

As HTML is for marking up **content,** CSS is for **styling** HTML. CSS uses **selectors** to grab the HTML element (or elements) that it wants. Then it applies a set of style properties to that element.

This is easier in theory than in practice. Achieving mastery in CSS is no mean feat — as any good front-end developer will tell you.

But we can make CSS much more comprehensible! Highlight the syntax and use the proximity principle, as below. You can almost feel the cognitive load melting away.

Here is some poorly-formatted CSS:

```css
figure {
  flex-direction:column-reverse;
  align-items:stretch;
  margin:var(--space-m, 1.25rem) 0;
  display:flex;
  justify-content:space-between;
  gap:0.5rem;
}
figure > figcaption {
  padding:0;
  line-height:1;
  text-align:center;
}
figure.is-indexed figcaption::before {
  content:"Figure " counter(figures) ": ";
}
 ```

Some folks love this. But why?

Below is that same CSS. But weʼve **alphabetized** the properties. Weʼve added whitespace to make the property name and its value easier to distinguish. And blank lines separate the selectors. **The reduction in cognitive load is measurable.**

```css
figure {
  align-items: stretch;
  display: flex;
  flex-direction: column-reverse;
  gap: 0.5rem;
  justify-content: space-between;
  margin: var(--space-m, 1.25rem) 0;
}

figure > figcaption {
  line-height: 1;
  padding: 0;
  text-align: center;
}

figure.is-indexed figcaption::before {
  content: "Figure " counter(figures) ": ";
}
```

Proper whitespacing and more makes CSS much easier to read.

<h3 id="grouping-concerns">Grouping concerns</h3>

We can apply this same proximity principle to our “concerns”. We put our content, structure, and semantics in one place. Then we put our instructions for presentation somewhere separate, but nearby.

In still a third location we put our algorithmic behavior. That behavior might be transferring data to a back end with `fetch`. Or it might be altering structure, semantics, or style on the fly. Or validating user input.

We can do this grouping in a **single file,** or we can spit our code out into **files of different types.** But there is another way we can group like code: into reusable components.

<h2 id="working-with-a-component-architecture">Working with a component architecture</h2>

The aim of _all_ programming is to break complex problems down into **simpler problems.** And to continue breaking them down until they become comprehensible and solvable.

Then to compose the solutions into a single solution to the original problem.

In short, much of the skill in programming is not the coding itself. It is the problem analysis in which we decide where to make the cuts. This is **problem decomposition.**

A common guide for how to do this is the [Single Responsibility Principle.](http://localhost:3002/glossary/s#srp) You may know this as part of the [SOLID](https://en.wikipedia.org/wiki/SOLID) design principles. We can apply this principle well beyond the limits of Object-Oriented Programming (OOP).

True, the single responsibility principle comes from OOP. But it is actually about modularization of code. At the most basic level we have utility functions and **components.** On the front end, these components generally provide UI widgets.

We can then compose our components into larger groups of components. Call them modules. This enhances resuability, but it also is vital for reducing cognitive load. And each component (or function) should do **one** (1) thing well.

In [Part 2](http://localhost:3002/essays/critique/the-proximity-principle-part-2) of this article, we give a good example of what not to do.