My changelog read like a git log in a newsletter costume
The weekly send was merged pull-request titles with a subject line on top. This is the work of making it something a person would open: the machine assembles the facts, a human gate decides what is worth an inbox, and the whole thing gained per-issue pages, real feeds, and email that survives dark mode.
For a while my weekly update was a list of merged pull-request titles with a subject line stapled on top. It read precisely like what it was: a git log wearing a newsletter costume. Nobody wants that in their inbox, and honestly nobody should. This is the work of turning it into something a person would actually open, without pretending a machine can decide what is worth their attention.
The git-log-in-a-costume problem
The raw material is real: every week a stack of merged work, each item with a title and a description. The trap is thinking that material is the newsletter. A title like refactor: extract gcal sync into a shared lib is true and completely uninteresting to a reader. The value is not in the commit; it is in the one sentence that says what changed for someone who does not have the repo in their head. Producing that sentence is the actual job, and it is two different kinds of work wearing one label.
What the machine can extract
Part of it is mechanical, and I let the machine own it. Pulling the week's merged work, grouping it by area, dropping the noise, and laying out the shape of an issue is deterministic. The machine is good at assembling facts, so it does all of that. The output of this pass is honest but flat: correct, complete, and not yet worth reading.
The editorial layer, and the gate in front of it
The second part is judgment, and that is where the human gate lives. An editorial pass rewrites the flat facts into value-first lines, de-jargons the shipped section so a row reads as a benefit rather than a diff, and drops dead internal links that mean nothing outside the repo. Then it stops and waits for me. I read the draft, cut what does not earn the space, and only then does it send. The machine drafts; I sign. That is not ceremony, it is the editorial standard, because the one thing the machine cannot do is decide what deserves a reader's time.
A changelog with permalinks and feeds
Once an issue is worth reading, it deserves to be more than an email. Each one now gets its own page with a shareable URL, an archive groups them by month, and the whole thing publishes as a JSON feed with WebSub so a subscriber can pull it however they like. The email itself got the same care: a real hero image and per-element styling so it does not fall apart in a dark-mode client, which is where most machine-built email quietly breaks.
Why sent issues are immutable
One design choice caused a small, revealing problem. A sent issue is immutable; it is an archive of record, so once it goes out it does not change. That is correct, and it meant I had no clean way to preview a real issue before sending, because editing the live one was off the table. The fix was an email-me-a-test path that renders a specific issue to my own inbox on demand. It is a good example of a correct constraint forcing a small feature instead of a compromise.
The trade-off
The gate is a bottleneck, and it is supposed to be. An automated weekly send that never waits for a human is faster and worse, because the failure mode is not a typo, it is publishing something that should not have gone out at all, to everyone, permanently. I would remove the gate the day the drafts are good enough that I stop changing them. I have not stopped changing them, so the gate stays. The cost is that the newsletter cannot ship without me. That is a cost I want.
Get the next one
An occasional note when something genuinely new ships here — essays, free tools, projects. No schedule, no filler, easy out.
Need something like this built?
I design and ship AI tools, full-stack apps, and data pipelines — end to end, to production. Tell me the problem in a sentence; I'll give you an honest read on fit within a day.
Work with me →