One changelog file, three places it shows up, choom
One typed changelog file feeds an in-game badge, a Settings tab and a public website page, so release notes cannot drift between the product and its pitch.
Most products stash their release notes in three places. A CHANGELOG.md in the repo. A release note somewhere in the product, if the product has one. And a post on the store page or the marketing site.
Three copies of the same intel, written at three different moments, by a choom who is fried by the third one. They drift on day one. The store post gets the polished cut, the in-app note gets a trimmed version that drops a feature, and the repo file gets whatever the commit messages happened to say.
In Cozy Coast there is one file. shared/changelog.ts is a TypeScript module holding the whole release history, and it feeds every surface that shows release notes.
The shape
An entry carries a version name, a date, and groups. A group carries a title, an icon, and a list of one-line items.
export interface ChangelogEntry {
version: string; // "Hubs with friends", not "0.4.2"
date: string; // "20 August 2026"
groups: ChangelogGroup[];
}
export interface ChangelogGroup {
title: string;
icon: ChangelogIcon;
items: string[];
}
The version is a name rather than a number, because an angler reading a badge does not care they are on 0.4.2. They care that hubs landed.
Icons are named, not pathed
This is the small call that makes the file portable across surfaces. ChangelogIcon is a union of names: chest, heart, fish, star, palm, gear, and so on. The file never holds a path.
Each surface resolves the name through its own map. The game looks up palm and gets sprites/palm.png out of its bundled assets. The website looks up palm and gets /img/palm.png out of its public folder. Same art, different plumbing, one word in the source.
Had the changelog stored paths, it would have had to know which surface was reading it, and that is the exact coupling that stops a shared file from being shared, choom.
The icons are always game sprites, never emoji. That is a taste rule, but the type enforces it: there is no way to jam an emoji into an icon field.
The badge fires once
The save records changelogSeen, the version whose notes were last read. On launch the game checks that against CHANGELOG[0], the newest entry, because the array runs newest first.
If they differ, a badge pops in the top right. Read the notes and the save updates. The badge never comes back for that release.
Everything past that is the Settings tab, permanent, holding the whole history. Two surfaces, one that interrupts and one that waits, both driven by one field on the save.
The rules live in the file
At the top of changelog.ts sits a comment block that is really a style guide:
Plain angler-facing sentences. One line per item, never a paragraph. No version numbers inside items. No internal jargon. And an entry that already shipped is never rewritten.
That last one matters more than it looks. A shipped changelog entry is a record of what an angler was told at the time. Going back to tidy the wording on a release from two months ago quietly rewrites the past for anyone reading the archive. It also breaks the guarantee that the website and a copy of the game installed back in June say the same thing.
Writing the rules where the entries get written means I actually follow them. A style guide buried in a wiki is a style guide no choom opens.
Why this generalises
The specifics here are a fishing game, but the shape holds anywhere the same words have to hit a product surface and a public page.
The conditions are worth naming, because it does not always work. The two surfaces have to want the same content, not overlapping scraps. They have to share a language or a build step, which for me is TypeScript across the Electron app and the Astro site. And somebody has to hold the line on keeping presentation out of the source, which is what the icon-name indirection is really enforcing.
When those hold, one file beats three every time. When they do not, run a generator instead of a shared module cosplaying as one.
You can see the output side of it at cozycoast.website.