Spiral Energetics

Too many sections

The worst experience ever is opening a project, and finding a README.md that is 5k words long and spread over 8 different sections. You scroll and scroll, trying to find the setup instructions. But it's filled with contributor guidelines, profiling instructions, development instructions, badges advertising that the tests pass, the maintainer's grandmother's life story, deployment steps, and migration steps.

On top of making the document very long, having lots of sections forces you to nest headers very deeply. You very quickly lose track of which section you're in, when trying to navigate the (already very long) document. Is that an h4 or h3? Which h2 am I under?

Solution

If you must have lots of sections, make the top-level README.md a directory:

# Project XYZ

XYZ does ABC.

# Docs

- [Setup](./docs/SETUP.md)
- [Develop](./docs/DEVELOP.md)
- [Migrations](./docs/MIGRATIONS.md)
- [Profiling](./docs/PROFILING.md)
- [Contribute](./docs/CONTRIBUTE.md)
- [Deploy](./docs/DEPLOY.md)

Prose explosion

LLMs make this problem even more commonplace. I don't want to read 500 words to figure out how to do something. This is a real example, from a deployment document generated by 5.6 Sol:

## Build the Linux artifacts

The host is a deployment target, not a build machine. Build the locked Rust
server and operator command in an x86_64 Linux container from the repository
root:

```
sh
docker buildx build \
  --platform linux/amd64 \
  --file infrastructure/server.Dockerfile \
  --output type=local,dest=infrastructure/artifacts \
  .
```

This writes the untracked artifact expected by `deploy-backend.yml` and the
top-level `deploy.yml`:

```
infrastructure/artifacts/xyz-server
infrastructure/artifacts/xyz-admin
```

## Deploy all applications

Set the WorkOS API key without putting it in shell
history or Ansible extra variables. Public WorkOS and domain values live in
`inventory/group_vars/xyz.yml`. Confirm the private access policy above
is populated, then run the top-level deployment:

```
read -r -s WORKOS_API_KEY
export WORKOS_API_KEY
uv run ansible-playbook deploy.yml
unset WORKOS_API_KEY
```

The backend deploy completes before the website and administration releases.

Solution

What happens if we rewrite it and throw out 90% of the prose?

**Step 1**: Build image

```
sh
docker buildx build \
  --platform linux/amd64 \
  --file infrastructure/server.Dockerfile \
  --output type=local,dest=infrastructure/artifacts \
  .
```

**Step 2**: Deploy image

```
read -r -s WORKOS_API_KEY
export WORKOS_API_KEY
uv run ansible-playbook deploy.yml
unset WORKOS_API_KEY
```

We link the build step and the deploy step together by giving them numbers (Step 1 and Step 2) and making the subject of both steps image. This not only makes it obvious that step 2 requires step 1, but that they are operating on the same underlying thing (the image). We didn't need any prose to do that! We're just using numbers and clever headers.

We also throw out all the LLM slop:

  • Do we need it spelled out that the the deployment target isn't building the image? No! It's obvious in how the steps are laid out.
  • Do we need it spelled out that deploy.yml depends on the built image? No! It's obvious in how the steps are laid out.
  • Do we need it spelled out that putting API keys in shell history isn't advisable? Hopefully not! The command block is setup to deal with this already.
  • Do we need to be told where ansible group vars live? No! If the group vars are at all relevant, and you don't already know about them yourself, the clanker can figure it out. It will already know how ansible works.

Platform bifurcation

Sometimes you'll get the setup instructions, and it's bifurcated by platform or use-case:

1. Install `uv`
2. Install the binary:
    - **Windows**: `xyz`
    - **MacOS**: `abc`
    - **Linux**: `foo`
3. Setup the ternary:
    - **Windows**: `xyz`
    - **MacOS**: `abc`
    - **Linux**:
```
apt-get banana
cat "cat" >> /etc/cat
```
4. Eject the CD-ROM: `bar`
5. NOTE: If using version `XYZ` OR if on MacOS, also run: `banana`.
6. Run `uv python -m apple.banana.clementine`
7. `cd /build/`
8. `cp ../blob/loaf ./loaf`
9. `./loaf`
10. You're done!

Please, don't do this. It makes following along exceptionally annoying and error-prone. If there are extensive platform-specific instructions, put them in three separate, fully-self-contained, documents. Yes, there will be some duplication between the documents. Yes, you'll now have to maintain three seprate documents instead of one. But hell yes, it's still worth it. The amount of times I've missed a platform-specific step, or accidently done a step not needed on my platform, are too big to count. Documentation really matters!

If you're really troubled by the maintenance burden, or the danger of the documents getting out of sync, just drop an AGENTS.md in the documentation directory and remind the clanker to always update all three documents if any of the steps change.