Most release notes are written for a reader who does not exist: someone technical enough to care about the version number, and non-technical enough to need the feature explained from scratch. Knowing how to write release notes starts with accepting that this reader is two people, and that almost everything which helps one of them gets in the other’s way.
An engineer integrating against your API wants the field name, the reason a change is breaking and the date it takes effect. A customer success manager wants to know what is different for her accounts and whether she has to warn anyone. Write one document for both and you get something too vague to act on and too dense to skim.
The fix is not more writing. It is one change list, split into two documents with different jobs, published in different places. The developer version is precise and complete. The customer version is short and says what to do. Both describe the same release, and neither is a summary of the other.

Why do release notes have two audiences?
Release notes have two audiences because developers and customers read them for opposite reasons. A developer reads to protect something that already works: the question is what will break and when. A customer reads to find out whether something got better: the question is what changed for me and do I have to act.
Release notes: the record a team publishes when it ships, describing what changed in a specific version and what each reader should do about it.
Those two questions do not compete for space so much as they compete for tone. The facts that make a note safe for an integrator, the exact header name, the null value that was never null before, are the facts that make a customer stop reading. The reassurance that makes a customer comfortable, “nothing you need to do”, is the sentence that gets an integration broken in January.
Collapsing them is the default because it feels efficient. One document, one publish, one link to share. The cost shows up later, in support tickets from people who were told nothing changed and in migration deadlines nobody saw. If your notes are also the file in your repository, the standard changelog format already gives you the developer half of this split.
What goes in the developer version?
The developer version contains every fact that could break code: the version number, the exact endpoint, field or header affected, why the change is breaking, the retirement date of anything being removed, and the migration step. Length is not the constraint here. Precision is.
Start with the number. Semantic Versioning says to increment the MAJOR version “when you make incompatible API changes”, the MINOR “when you add functionality in a backward compatible manner” and the PATCH “when you make backward compatible bug fixes”. A reader who trusts that rule can triage your release before reading a word of it, which is the entire point of versioning.
Then sort the changes. Keep a Changelog 2.0.0, published in June 2026, defines six types: Added, Changed, Deprecated, Removed, Fixed and Security. Its most useful rule is the test it gives for the two that get confused. “Fixed: the behavior was wrong, and is now correct. Changed: the behavior worked as intended, and now works differently.” If you cannot decide, ask whether the old behaviour was a bug.
Labelling something breaking is not enough on its own. Stripe’s API changelog attaches a reason to each breaking entry, written in terms of what the reader’s own code does, for example: “This is a breaking change because the radar_session property can now return null values in API responses, but previously it always contained a value.” A developer can read that and know in one pass whether it applies to them. “Breaking change: webhooks updated” forces every single reader to go and check.
Anything you are retiring needs an absolute date. The idea is formal enough to have its own standard: RFC 8594 defines the Sunset HTTP header field, which “indicates that a URI is likely to become unresponsive at a specified point in the future”. You do not have to send the header to borrow the principle. A deprecation notice without a date is not a notice, it is a rumour.
What goes in the customer version?
The customer version answers two questions and stops: what is different for me, and do I have to do anything. Everything that does not serve one of those two questions belongs in the developer version or in the documentation.
The constraint is attention, and it has been measured. In “How Little Do Users Read?”, Jakob Nielsen reported in 2008 that on an average web page, users “have time to read at most 28% of the words during an average visit; 20% is more likely”. A 600 word customer note is a note where roughly 120 words get read, and you do not get to pick which 120. That is an argument for ruthless ordering, not for writing less carefully.
Three rules follow from it. Lead with the outcome, not the mechanism. Name the feature with the exact words that appear on screen, so a reader can go and find it. Put “no action needed” or the action itself in the first line of each item, because that is the sentence people are scanning for.
One thing the customer version must never do is hide a breaking change. It does not explain the change, it routes it: name the deadline, say who on their side needs to act, and stop. A customer who forwards one sentence to their developer has been served properly. A customer who reads “nothing you need to do” and finds out in January has not.
The same release, written both ways
Here is one release written twice. The product is imaginary, the shape is not: version 4.2.0 adds saved filters, moves webhook signing from SHA-1 to SHA-256, and fixes a timezone bug in exports.

The developer version, in the Keep a Changelog shape:
## [4.2.0] - 2026-09-20
### Added
- Saved filters on the list endpoint: `GET /v1/items?filter_id=`.
### Changed
- Webhook signatures are now signed with HMAC-SHA256. The `X-Signature`
header carries both schemes during the transition, as
`v1=<sha1>,v2=<sha256>`. This is a breaking change if your handler
compares the whole header value instead of parsing the scheme it expects.
### Deprecated
- HMAC-SHA1 webhook signatures. Retired on 2027-01-15. Verify the `v2=`
value before that date. No change is required if you already parse the
header by scheme.
### Fixed
- CSV exports used the server timezone instead of the workspace timezone,
so rows created near midnight landed on the wrong day.
The customer version, same release:
Saved views. Any combination of filters on a list can now be saved and reused. Open a list, set your filters, and choose Save view. Nothing to turn on.
Exports now use your workspace timezone. Rows created near midnight were landing on the day before or after. Exports you run from today are correct. Anything exported before today may need running again.
One item for your developers. If your team receives our webhooks, they have until 15 January 2027 to move to the new signature. The details are in the developer changelog. Nothing changes for you.
Notice what moved and what did not. The date appears in both. The header name appears in one. The bug fix is described by its consequence for the customer and by its cause for the developer. And the breaking change is present in both documents, at different resolutions.
| Fact | Developer version | Customer version |
|---|---|---|
| Version number and release date | Required, it is the index | Drop it unless support asks customers to quote it |
| Exact endpoint, field or header name | Required, verbatim | Never |
| Why a change is breaking | Required, in terms of the reader’s own code | Replaced by the deadline and who to forward it to |
| Retirement or deprecation date | Required, absolute | Required, absolute |
| On-screen name of a new feature | Optional | Required, matching the label in the product |
| How to try the new thing | A link to the reference | Required, three steps at most |
| Fixes with no user-visible effect | List them | Leave them out |
| Known issues still open | Required | Required if a customer would hit it |
How to write release notes from one change list
Write both versions from a single list, in this order, and you never end up reconciling two accounts of the same release.
- Collect the change list once. Merged pull requests, closed tickets, whatever your team already produces at the end of a cycle. Do not write prose yet.
- Tag each change with who it affects: code, people, or both. Most changes affect exactly one, and the “both” pile is small enough to handle by hand.
- Write the developer version first. It holds the facts that are expensive to recover later, and it forces the breaking-change question while the change is fresh.
- Derive the customer version from the “people” rows, plus every “code” row that carries a date.
- Decide delivery per version. Same release, two destinations, two notification rules.
Steps two and four are where most of the work is. The rest is formatting, and formatting is the part you should stop reinventing: there are five release notes templates, one per format, including a Keep a Changelog file and an API breaking-change notice, each filled in with a worked example.
Which mistakes collapse the two versions into one?
The mistakes that collapse release notes back into a single document all come from writing once and hoping the result stretches to cover both audiences. Five do it most often.
- Shipping the commit message as the customer note. “Refactored the export pipeline” tells a customer nothing and tells a developer less than the diff already did.
- Burying the breaking change in position seven. If it needs action, it goes first, in both versions.
- Relative deadlines. “In the coming months” cannot be put in a calendar, so it will not be.
- Sending everything to everyone. The fastest way to train people to ignore your notes is to send a customer four paragraphs about a header they will never see.
- Publishing and walking away. A note nobody was notified about is a file, not a release note.
Where each version gets published
The two versions want different homes, because the audiences are already in different places. Developer notes belong next to the reference documentation, in the repository, and in whatever feed integrators already subscribe to. Customer notes belong inside the product, where the person is standing when the change affects them.
That split is a publishing decision, not a writing one. Changelog software that targets updates by segment lets the same workspace carry both: rich markdown with code blocks for the version integrators read, audience targeting so a note goes to specific plans or segments rather than to everyone.
For the customer half specifically, the delivery matters more than the prose. You can announce a release with in-app updates and an email digest so the note reaches people where they already are instead of waiting on a page they have never visited.
Start with your last release. Take the notes you actually published, and sort every line into the developer column or the customer column. Whatever ends up in both columns is the part you were writing twice and serving to neither.