LTS and maintenance releases: release notes for versions you still support
Almost everything written about changelogs β including most of our own guides β quietly assumes one line of development: there is a latest version, it is the product, and the top entry of the changelog describes it. The day you cut your first long-term-support branch, that assumption dies. Now 2.4.8 and 5.2.0 ship in the same week, both of them are “the product” to the people running them, and a changelog that only tells the newest branch’s story has quietly stopped documenting what a large share of your users actually run.
This guide is about release notes for software with a support matrix: LTS lines, maintenance branches, security-only windows, and the version end-of-life announcements that close them. It is the operational sibling of a deprecation policy β that document promises how long things live; these entries are you keeping the promise, release by release.
Three readers, none of them on latest
Maintenance-release notes have their own audience, and it is not the audience your feature announcements were written for:
- The operator pinned to the LTS branch. They picked the old version on purpose β change-control windows, a certified stack, or simple prudence β and they read every patch announcement with one question: does this touch anything beyond the fix? They are the most loyal readers your changelog will ever have, and the easiest to lose.
- The security triager. They arrive from an advisory, not from your home page, with three questions in a fixed order: does this affect the branch we run, which release fixes it, and can we take that release without taking anything else? Every sentence that is not an answer to one of those is in their way.
- The upgrade planner. They read across branches: what has 5.x fixed that 2.x will never get, when does 2.x actually die, and how big is the jump? Your maintenance notes and your breaking-change announcements are the two documents their migration plan is built from.
Notice what none of them wants: enthusiasm. The right register for maintenance notes is closer to a pharmacist’s label than a launch post β precise, complete, calm. The tone guide calls this sliding vocabulary with a constant stance; a maintenance branch is the far end of that slider.
Publish the support table β it is part of the changelog
The most-asked question about any maintained software is not “what changed?” It is “is my version still supported?” Answer it once, in a table, at a stable URL, and link that table from every maintenance entry you ever publish:
Version Status Until Notes 5.x Active β all fixes and features 4.x Maintenance 2027-04-30 bug and security fixes 2.x LTS Security only 2026-12-31 security fixes only 1.x End of life 2025-06-30 no fixes, including security
Two rules make the table trustworthy. First, define the statuses operationally, the way a deprecation policy defines “deprecated”: “maintenance” means bug and security fixes but no features; “security only” means exactly that, and a data-loss bug on that branch will not be fixed β say so before it happens, not in the ticket where you decline it. Second, put a date in every row. “Supported for now” is not a status; it is a promise to disappoint someone later. The projects people trust with decade-long deployments publish the whole schedule up front β Node.js announces each line’s active and maintenance windows the day it ships, and Ubuntu’s release cycle page has made five-year windows a purchasable fact. You do not need their scale to copy the habit: the day a branch is born is the cheapest day to announce when it dies.
One fix, many branches: the backport entry
The defining move of maintenance releases is that one fix lands in several branches at once. The rule for writing it up: every branch release gets its own entry, and no reader should ever have to read another branch’s notes to understand their own patch. Concretely:
- One entry per branch release. If the fix ships as 5.2.1, 4.1.3, and 2.4.8, that is three entries. The 2.x operator should never be pointed at the 5.x notes to “see details” β their entry carries the details.
- Name the origin. “Backport of the fix first shipped in 5.2.1” β with a link. It tells the reader the fix has already lived in production somewhere, which is exactly the reassurance an LTS operator wants.
- Cross-link the siblings. “Also released today as 5.2.1 and 4.1.3.” The security triager checking coverage should find the whole set from any one entry.
- Write the fix symptom-first, like any other fix entry: the behavior the user saw, not the internal cause.
A complete backport entry is short:
2.4.8 (LTS) β 2026-08-12 Fixed: exports timing out for workspaces with more than 10,000 archived items. Backport of the fix first shipped in 5.2.1; also released today as 4.1.3. No other changes. Safe to apply.
That last line β “No other changes. Safe to apply.” β is the sentence the LTS operator subscribes for. It is checkable (the diff is public), it is the entire value of the branch stated in six words, and after a year of entries that keep being true, your patch releases start clearing change-control on the strength of the notes alone.
Boring on purpose
A maintenance branch has a content policy, and the release notes are where that policy is enforced in public: fixes and security patches, nothing else. No features, no changed defaults, no “small” UI refinements, dependency bumps only when a fix forces one β and then named, with the reason. The notes should be almost monotonous; on a maintenance branch, monotony is the product.
Sometimes reality forces an exception β a legal requirement, an upstream API turned off, a certificate authority change. When a maintenance release must carry a behavior change, announce it with the full loudness of a breaking change: flagged in the title, explained, dated β not smuggled under “Fixed”. One smuggled change in an LTS patch and the operator re-reads every future “safe to apply” with suspicion. Predictability was the only thing the branch was selling.
Security backports: ranges, coverage, and the branch you won’t fix
Security fixes are where the support matrix stops being a documentation nicety and becomes operationally load-bearing. Three rules:
- State affected ranges in versions people can check. “Affected: 2.0.0 through 2.4.7 and 4.0.0 through 4.1.2; 1.x is not affected.” The advisory guide covers the two-document, two-clock mechanics; the matrix-specific obligation is that the ranges must speak to every branch in your table, including the ones that are fine.
- Ship the fix for every supported branch together. A fix published for latest with LTS “coming soon” is a public map to an unpatched population: the diff tells attackers where to look, and your most conservative users are the ones left exposed. If the branches cannot ship the same hour, hold the detailed writeup until they have β the same logic as the hotfix playbook’s two-pass entry.
- The branch you won’t fix is an announcement, not a silence. If 1.x is past its window and will not receive the patch, say exactly that, in the entry: “1.x reached end of life on 2025-06-30 and will not receive this fix.” It reads harsh and it is the kindest sentence available β the silent alternative is a user discovering their exposure from a CVE feed instead of from you.
Announcing a version’s end of life
Version EOL is the smaller sibling of a product sunset β lower stakes, same anatomy, and the same iron rule about dates. The ladder:
- Announce the date early β ideally at birth. If your policy fixes the window (“every LTS gets three years”), the EOL date is part of the release announcement that opens the branch. No policy? Announce now, with a generous date.
- Remind on a schedule. An entry at six months out and again at one month out, each repeating the date and linking the upgrade path. Write all of them the day you set the date; schedule the reminders rather than trusting future-you.
- Label the final release. “2.4.9 β final planned release of the 2.x line” in the title. Operators grep for exactly this later.
- Close with an EOL-day entry that says concretely what stops (no fixes, including security), what remains (downloads? docs?), and where to go β a link to the cumulative migration guide in your docs, per the docs-vs-changelog split: the changelog announces, the docs carry the how. Old versions’ notes stay up forever; archive, never delete.
And the iron rule, identical to sunsets: EOL dates move later or not at all. Extending a window builds trust. Pulling one in β even by a month, even with notice β retroactively devalues every date in your table.
One stream, or one per branch?
Structurally, a support matrix gives you two workable shapes. A single stream works while the matrix is small: put the branch in every title (“2.4.8 (LTS)” is kinder than a bare version number), cross-link siblings, and readers filter by eye or by search. A stream per supported major is better once branches have real independent histories or your operators genuinely watch only one line: each branch gets its own page and its own feed, and the LTS feed becomes what its subscribers actually want β a channel that fires four times a year, every item applicable. Feeds are the operational payoff either way: a per-branch feed is a tripwire an operator can wire straight into their patch-window tooling. What does not work is the worst of both: one stream, no branch in the titles, releases from three lines interleaved β every reader parsing version arithmetic to find out whether an entry is about their software.
Six anti-patterns
- The latest-only changelog. Backports exist but are announced nowhere; LTS users learn about their own patch from a package-manager diff. The branch most chosen for predictability gets the least predictable communication.
- “Miscellaneous security fixes” in an LTS patch. Security-window readers are precisely the people who need ranges and impact; vagueness aimed at them reads as either cover-up or not knowing. Both are worse than the truth.
- The silent EOL. A branch just stops getting releases. No entry, no date, the table never updated. Users discover their status from an unfixed CVE β the most expensive possible way for them to learn it.
- The smuggled feature. A “maintenance” release with a new toggle, a changed default, a fresh telemetry endpoint. One occurrence and “safe to apply” stops meaning anything.
- Version soup. Three branches interleaved in one stream with bare version numbers for titles and no way to follow just yours.
- The date that moves earlier. Shortening a published support window because the roadmap got impatient. Every other date you have ever published is now a guess.
Doing this with Wakelog
Wakelog fits the matrix shape directly: for a real multi-branch product, run a project per supported major (acme-2x, acme-5x) so each branch has its own page, RSS/JSON feed, and embed β the LTS feed stays gloriously quiet β with your main project carrying the cross-branch announcements like EOL dates. Permalinks give advisories a stable “fixed in” URL per branch entry; scheduled publishing lets you write the entire EOL reminder ladder the day you set the date and let it fire on schedule; the security tag keeps patch entries filterable; and the CLI or a single curl posts from each branch’s release script, so the 2.x pipeline announces 2.x. Honest caveats: Wakelog doesn’t model a support matrix β the table itself belongs in your docs at a stable URL, linked from entries; and branch identity lives in titles and project structure, since tags are a fixed set. Public-page search covers the rest: if titles carry the branch, a reader can pull up any line’s history in one query.
Give every branch a changelog operators trust Next: writing a deprecation policy →
Related guides
- How to write a deprecation policy (with template)
Decide how you retire things before the fight starts: scope, notice windows, channels, and what βdeprecatedβ actually means β with a copy-paste policy template. - Dropping platform support: announcing the end of old OSes, browsers, and runtimes
The strangest breaking change is the one where your product didnβt change β only where it runs. How to publish a support policy, announce a platform drop with a date and an exit, warn the people whoβll never read your changelog, and decide what happens to installs you leave behind. - Sunsets and shutdowns: announcing the end of a product or feature
End of life is the announcement users judge you by forever. The four reader questions in order, the date ladder, export-before-announce, honest alternatives, and the tombstone page that outlives the product.
Last updated 2026-07-31 Β· All guides