Skip to content
Back to Ideas 4 min read

Run It by Hand Until It Is Boring

Run It by Hand Until It Is Boring

Encoding the release, part 1 of 8.

We are a small distributed team shipping open source to npm, with strict TDD and trunk-based development. Like every team, we have operations we repeat: releasing, keeping docs in step with the code, setting up a new package. And like every team, we felt the pull to automate them immediately.

We have a rule that resists that pull: every repeated operation gets written down first, run by hand until it is boring, and only then encoded.

Releasing EkoLite is the first operation to make the whole journey. It started as a document, RELEASING.md: branch release/vX.Y.Z off main, bump the version, write the changelog entry, open a PR, let CI go green, merge; cut a GitHub Release; approve the deployment gate; let the workflow publish to npm with OIDC trusted publishing, so no token ever leaves anyone's machine. Two human decisions, and the machines do the rest.

We ran that document by hand for several releases. That period was not wasted time — it was the requirements phase. Running it by hand told us which steps needed judgement (writing the changelog entry, approving the gate) and which were pure rail (everything else). It told us where mistakes actually happen: a mirror registry quietly rewriting lockfile URLs, a changelog entry forgotten until the Release notes were due. You cannot design good checks for a process you have not felt fail.

When the document was boring, we encoded it: ekohacks release <version>, a command in our operations CLI. The name of the game is that the document is the spec. The command does not do anything the manual process does not do. Its preflight is the document's "Before cutting" checklist, line for line. When we discover the process needs a new step — and part 6 of this series is about the day we did — the rule is: the process changes in RELEASING.md first, by hand, then in the tool. The document leads; the command mirrors it.

This ordering sounds bureaucratic and is the opposite. Automation that grows ahead of the process it automates becomes the process, silently, and nobody can say why it does what it does. Automation that trails a living document can always answer the question "why does the tool insist on this?" with a sentence a human wrote down after a bad afternoon.

The rest of this series is the story of building that command: how a failing test became our review artefact, how you test "wait for CI, then merge" without a CI in sight, which one decision we refused to automate — and what the first real release taught us that no test could have.

Next: The Failing Test Is the Review.

More from Ideas

·8 min read

Inversion of Control Doesn't Need a DI Container

We started designing a dependency scanner, and its needs led us to Inversion of Control. It turned out we were already doing it, without a DI container.

A
Anastasios Orfanidis
·7 min read

Unknown Is an Answer

A stub that holds no documents could not say whether a change still matched a query. Letting it answer unknown named a case the real database has too.

·7 min read

The Inbox That Delivers Nowhere

Signing in by emailed code puts a mailbox on the critical path. How we use Mailpit on Studomia, how our tests read the code, and what one question taught us.