> For the complete documentation index, see [llms.txt](https://engineering.umh.app/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://engineering.umh.app/engineering/development-process/how-to-build/coding-standards/code-quality/simplicity.md).

# Simplicity

Which complexity our code keeps because the user's problem needs it, and how to find and remove the rest.

Owner: Jeremy

We aim for the best possible user experience. John Ousterhout calls this [pulling complexity down](https://web.stanford.edu/~ouster/cgi-bin/cs190-winter18/lecture.php?topic=modularDesign): a few developers do the hard work, so that thousands of users don't have to. This includes the small things. Each small problem looks reasonable on its own. Together they are the [thousand cuts](https://www.umh.app/insight/what-we-do-at-umh) that stretch a project from weeks into months.

Some complexity comes from the user's problem itself. Our code handles every edge case a user can run into on the [paths we offer](https://engineering.umh.app/product/product-standards/opinionated-simplicity), even a rare one. For the user who runs into it, that edge case decides whether the product works. Leaving one out to keep the code clean is a [shortcut](https://www.theocharis.dev/beliefs/).

All other complexity is complexity we added ourselves. It makes the code harder to change. Then every fix and feature reaches the user later. The rest of this page shows where it comes from, so that we can remove it.

This is the first page of Code quality. [Abstractions](/engineering/development-process/how-to-build/coding-standards/code-quality/abstractions.md), [Readability](/engineering/development-process/how-to-build/coding-standards/code-quality/readability.md) and [Code comments](/engineering/development-process/how-to-build/coding-standards/code-quality/code-comments.md) build on it.

## Rules

* The user's experience comes before the simplicity of our code. Our code handles every edge case a user can run into on the [paths we offer](https://engineering.umh.app/product/product-standards/opinionated-simplicity), even when the code gets longer, so that the user does not have to.
* We remove all other complexity. We choose the simple solution over the easy one, and we decide for the user instead of adding an option.
* We follow YAGNI ("you aren't gonna need it"). We build what is needed now, because code nobody needs still has to be read, tested and kept working.
* We prepare clean [abstractions](/engineering/development-process/how-to-build/coding-standards/code-quality/abstractions.md) before we change the code. We refactor first, so that the change, or a feature we know is coming, fits cleanly.

## Simple, not easy

**Choose the simple solution over the easy one.** Rich Hickey draws this distinction in [Simple Made Easy](https://github.com/matthiasn/talk-transcripts/blob/master/Hickey_Rich/SimpleMadeEasy.md). Easy means familiar: the solution you already know, or the one everyone else uses. Easy depends on the person.

**Ask who the solution is easy for.** The next developer does not have your context. The user thinks in the terms of their own work. For both of them, the solution that was familiar to you can be hard to follow.

**Simple means no two jobs are tangled together.** A function that checks a config and saves it in one step is easy to write. After that, nothing can check a config without saving it. The editor cannot warn the user about a wrong value before they press save. A separate check function and save function let the editor check the config on every change.

## Three signs of complexity

**Complexity shows in how hard the code is to change.** John Ousterhout names [three signs](https://web.stanford.edu/~ouster/cgi-bin/cs190-winter18/lecture.php?topic=complexity) of it. He judges complexity by the person who reads the code, because the writer already knows what it does.

* **One change needs edits in many places.** Ousterhout calls this change amplification. If the same check is copied into three functions, a fix to one copy misses the other two. Move the check into one function that all three call.
* **The reader has to hold too much in their head.** Ousterhout calls this cognitive load. [Readability](/engineering/development-process/how-to-build/coding-standards/code-quality/readability.md#working-memory-and-cognitive-load) explains the limit of working memory.
* **The code has a hidden requirement.** Ousterhout calls this an unknown unknown. For example, a function only works after another one ran, but nothing in the code says so. The reader does not know the requirement exists, so they do not look for it.

**When a function only works after another one ran, let its signature say so.** Suppose the save function from [Simple, not easy](#simple-not-easy) only works on a config that was checked first. Nothing stops a developer from calling save with an unchecked config. Let the check function return a new type, `CheckedConfig`, and let save accept only that type. Then the compiler rejects a call to save with a `Config` that did not pass the check.

**Check input once, where it enters the program, and return the checked value as a new type.** A check that returns only an error leaves the input as it was. Nothing in its type shows that it was checked, so later code either repeats the check or skips it. Go's [`url.Parse`](https://pkg.go.dev/net/url#Parse) turns a string into a `*url.URL`, and later code reads the host and path from it without parsing the string again. Alexis King calls this [Parse, don't validate](https://lexi-lambda.github.io/blog/2019/11/05/parse-don-t-validate/).

**Add a new type only where a caller could otherwise pass an unchecked value or call functions in the wrong order.** Each new type is one more name for the reader to learn.

## YAGNI

**Build what is needed now.** Code for a feature nobody needs yet still has to be read, tested and kept working. Martin Fowler calls this the [cost of carry](https://martinfowler.com/bliki/Yagni.html) in his essay on YAGNI ("you aren't gonna need it"). He writes that the extra complexity "makes it harder to modify and debug that software".

## Prepare clean abstractions

**Refactor the code first, so that the change fits cleanly.** A refactoring changes the structure of the code and none of its behaviour. Martin Fowler calls this [preparatory refactoring](https://martinfowler.com/articles/preparatory-refactoring-example.html) and quotes Kent Beck: "make the change easy, then make the easy change". In his [essay on YAGNI](https://martinfowler.com/bliki/Yagni.html), he writes that YAGNI "does not apply to effort to make the software easier to modify". [Abstractions](/engineering/development-process/how-to-build/coding-standards/code-quality/abstractions.md) describes what a clean abstraction is.

**Prepare the code for a feature you know is coming, as long as today's code stays easy to understand.** You can change the code now so that the feature fits cleanly, and build the feature when it is needed. Fowler draws the line here: an abstraction "that makes it harder to understand the code for current requirements is presumed guilty". Often the preparation means refactoring an [abstraction that almost fits](/engineering/development-process/how-to-build/coding-standards/code-quality/abstractions.md#reuse-before-you-add). A [spike](/engineering/development-process/how-to-build.md#before-you-write-code) shows which code to change.

## Fewer options

**Every option multiplies the cases the code has to keep correct.** An option with two values doubles the combinations the tests have to cover. [Opinionated simplicity](https://engineering.umh.app/product/product-standards/opinionated-simplicity) reaches the same decision for the product: we choose the value of every product setting ourselves. The same holds for parameters and config fields inside the code.

**Decide for the user instead of adding an option.** John Ousterhout calls configuration parameters ["voodoo constants"](https://web.stanford.edu/~ouster/cgi-bin/cs190-winter18/lecture.php?topic=modularDesign). If the developer does not know the right value, the caller or the user will not know it either. Suppose every caller of a retry function passes `maxAttempts` as 3. Set 3 inside the function and drop the parameter.

## The second pass

**The first version that works is rarely the simplest.** John Ousterhout calls this [Working isn't good enough](https://web.stanford.edu/~ouster/cgi-bin/cs190-winter18/lecture.php?topic=working). Writing the first version shows you what the problem needs. Then go over it again and remove what the problem does not need.

**A review is the second pass by someone else.** The writer knows why each part of the code is there, which makes it hard to see what could go. The reviewer reads the code without that history. Every later reader will read it the same way. The reviewer asks whether the same behaviour, with every edge case, needs fewer functions, types or options.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://engineering.umh.app/engineering/development-process/how-to-build/coding-standards/code-quality/simplicity.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
