Skip to content

SachoChangelogs are for users.

An opinionated changelog manager. Release notes written by humans, kept with the code, compiled at release.

A cinnabar seal bearing the character 史A cinnabar seal bearing the character 史

Install

mise use -g github:dahlia/sacho
npm install -g @sacho/sacho
cargo install sacho

Sacho is a single static binary with no runtime dependencies. It works in any repository, whatever the language, and supports Git, Jujutsu, and Mercurial. Prebuilt binaries are on GitHub Releases.

The opinions came first

Sacho is a small tool built on strong opinions about changelogs. None of them depend on Sacho: a project could follow every one with a text editor and discipline. They are worth stating before the tool is.

  1. Changelogs are for users.

    A changelog answers two questions for a person who already runs your software: what changed, and what should I do about it when I upgrade. It is not a development diary and not a record of effort.

  2. A changelog is not a commit log.

    Commit messages address collaborators in the vocabulary of internals. Changelog entries address users at the public surface. Tools that generate one from the other erase that distinction, and the output is a commit log arranged to look like a changelog.

  3. Entries travel with their commits.

    An unreleased entry describes a change that exists as a commit somewhere. Kept as a file in the repository, the entry survives merges, reverts, and cherry-picks with the same guarantees the code enjoys.

  4. One change, one file.

    A single accumulating changelog makes every pair of branches conflict at the same place. Fragments dissolve the problem: concurrent changes touch different files and merge cleanly.

  5. Machines catch what reviewers miss.

    A missing entry announces nothing. No test fails and no compiler complains, so checking for absence is work for a machine, and reviewers are freed to judge whether the words are any good.

  6. Write the history users experienced, not the history that happened.

    A function added and renamed in the same release cycle is one change to users, not two. States that never shipped are internal history, and no commit-based generator can know that.

  7. Don't confuse users.

    Every word in an entry costs every reader a moment of attention. Internal names and narrated effort tell users this document is not really for them.

Read the full argument in Philosophy.

What the practice looks like

Each user-visible change is one small Markdown file, named after the change and committed with it. Here is changes.d/clear-function.md:

 -  Added `clear()` to remove every entry at once.

If clear() is renamed before it ships, you edit this file. Users will experience one change, and the changelog will say so.

When the release is ready, one command compiles every fragment into a dated section of CHANGES.md and deletes the consumed files:

sacho release
Version 1.2.0
-------------

Released on July 19, 2026.

 -  Added `clear()` to remove every entry at once.

The released section is sealed. Sacho never rewrites it.

The release commit contains no empty unreleased section. After tagging it, sacho next 1.3.0 starts the next development cycle.

Where the tool comes in

It refuses to generate

Every entry is prose written by a human at the time of the change. Sacho collects, sorts, formats, and checks; it stops before choosing the words.

It keeps entries in the repository

Fragments follow their commits through every merge and history rewrite, so the changelog and the code cannot drift apart.

It fails the build on missing entries

sacho check runs in hooks and CI and notices when covered source changes carry no fragment, with escape hatches for changes that genuinely need no entry.

It seals released history

A release compiles the fragments into a dated section and consumes them. Released sections are never touched again.

None of this requires Sacho.

A text editor and discipline are enough.

Sacho exists because discipline does not scale.

Get started

Sacho (史草) were the draft records of Joseon Korea's court historians: written as events happened, compiled into sealed annals, then washed of their ink. The name is a description of the workflow.

If Sacho earns a place in your workflow, you can support its development through GitHub Sponsors.

Released under the GPL-3.0-only license.