by AI Changelog (LX AI)
Three common ways to produce release communication. All are real; pick by whether you need a portable file, CI automation, or a host UI.
Three common ways to produce release communication. All are real; pick by whether you need a portable file, CI automation, or a host UI.
| Approach | Primary artifact | Strength | Watch-out |
|---|---|---|---|
| AI Changelog | Human-oriented draft from commits/PRs | Fast first pass for Keep a Changelog style notes | Still requires human review before publish |
| conventional-changelog | Generated sections from commit metadata | Fits Conventional Commits + CI | Commit ≠ user-facing note; noise remains |
| GitHub Releases | Host release page | Assets, notifications, compare view | Tied to the host; Keep a Changelog prefers a file in-repo |
Rule of thumb: keep a portable changelog as source of truth, use generators as drafts, and treat hosted Releases as the announcement channel.
I start by feeding the tool the raw git history since the last tag. The tool scans each commit message and each pull request description. It looks for patterns that signal a user‑visible change: words like “add”, “fix”, “remove”, “update”, and it also checks for conventional‑commit prefixes such as feat:, fix:, perf:. When it finds a match it builds a sentence in the style of Keep a Changelog, placing the sentence under the appropriate version header.
## [1.2.0] - 2026-09-10 ### Added - Add OAuth2 login flow for GitHub and GitLab providers ### Fixed - Fix validation error when email field contains trailing spaces ### Changed - Update dependency on node-fetch to version 3.3.0
The draft is meant to be opened in an editor, reviewed, and then copied into the changelog file that lives in the repository. I never publish the draft directly; it is always a starting point for a human editor.
This tool works directly with commit metadata. It expects each commit to follow the Conventional Commits specification. When it sees a commit with type feat it creates an “Added” section, with type fix it creates a “Fixed” section, and so on. The output is a series of markdown sections that can be concatenated into a changelog file.
feat: add user profile pagefix: resolve null pointer in export functiondocs: update README with new installation stepsrefactor: split large utils module into smaller filesperf: replace O(n^2) algorithm with O(n log n) versiongit log --pretty=format:%s to get the subject linesThe output is a clean list of changes that mirrors the commit history. Because the tool does not rewrite commit messages, any noise in the commits (like “wip”, “typo”, or “fix tests”) appears verbatim in the changelog unless you filter it out with additional configuration.
I use this tool in a CI step that runs after every push to the main branch. The generated markdown is saved as an artifact and can be compared against the existing changelog file to see what needs editing.
When I push a new tag, GitHub automatically creates a release page if I have enabled the feature. The page shows the tag name, the target commit, and a rich text area where I can paste the final changelog content. I can also attach binaries, checksums, or release notes in other formats.
gh release createGitHub Releases ties the changelog to the platform. If I ever move the repository to another host, I lose the release page unless I replicate the content manually. The Keep a Changelog philosophy recommends keeping the changelog as a file in the repository so that it travels with the code.
I let AI Changelog or conventional-changelog produce a markdown draft. I review the draft, edit it for clarity, and then copy the final markdown into the release description field. The same markdown file is also committed to the repository as CHANGELOG.md. This way the file stays the source of truth and the release page is just a mirror.
Every team needs to decide where the canonical changelog lives. I have tried three patterns and observed the trade‑offs.
Pros:
Cons:
Pros:
Cons:
Pros:
Cons:
My recommendation is to keep CHANGELOG.md as the source of truth, run a generator in CI to produce a draft, and use the host’s release page only for announcing the final version.
Here is the exact sequence I use in my GitHub Actions workflow.
name: Generate changelog draft
on:
push:
branches:
- main
jobs:
changelog:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # need full history for tag comparison
- name: Determine previous tag
id: get_tag
run: |
PREV_TAG=$(git describe --tags --abbrev=0 --match "v*" $(git rev-list --tags --max-count=1))
echo "prev_tag=$PREV_TAG" >> $GITHUB_OUTPUT
- name: Run AI Changelog
uses: ai-changelog/action@v1
with:
tag_from: ${{ steps.get_tag.outputs.prev_tag }}
tag_to: ${{ github.sha }}
output_file: changelog_draft.md
The workflow uploads changelog_draft.md as an artifact. I open the artifact, review the content, and make any needed edits directly in the file.
After I am satisfied, I copy the draft into CHANGELOG.md under the correct version header. I then commit the change and push a new tag.
- name: Create GitHub Release
uses: softprops/action-gh-release@v2
with:
tag_name: v${{ steps.get_tag.outputs.prev_tag }}
name: Release v${{ steps.get_tag.outputs.prev_tag }}
body_path: CHANGELOG.md
The whole process takes under five minutes from push to published release, and the changelog file always reflects the exact content that users see.
Even with good tools, release notes can go wrong. I have seen the following issues repeatedly.
Commits like “fix typo in comment” or “update dependencies” often appear in the generated draft. If you copy them verbatim, the changelog becomes cluttered.
Solution: configure the generator to skip certain types (e.g., chore, docs) or add a filter step that removes lines matching a regex such as ^.*(typo|comment|dependencies).*$/i.
Sometimes the draft is generated without a version header, resulting in a flat list that does not conform to Keep a Changelog.
Solution: always pass the exact version string to the tool and verify that the output begins with ## [X.Y.Z]. If it does not, prepend the header manually before merging.
CHANGELOG.mdWhen multiple features are developed in parallel, two branches may both add entries under the same version header, causing a conflict on merge.
Solution: adopt a convention where each feature branch writes its changes to a temporary file named changelog/. During the release preparation step, concatenate all temporary files into the master changelog.
It is easy to run the generator, review the draft, and then get distracted before copying the result.
Solution: make the CI workflow fail if the artifact changelog_draft.md exists but the working tree does not contain a matching entry in CHANGELOG.md. A simple diff check can enforce this.
Relying only on GitHub Releases means the changelog disappears if you migrate the repository.
Solution: treat the release description as a read‑only copy. Always keep the master file in the repo and update it before creating the release.
I want to be clear about the limits of the approaches described above.
This is not a guarantee that every release note will be perfect. Human review is still required to catch ambiguous wording or missing context.
This is not a 100 % automated solution that never needs a developer to open an editor. The tools give a starting point, but the final voice and tone come from a person.
This is not a promise that you will never see noise in the changelog. Even with filters, some low‑value commits can slip through, especially when commit messages are vague.
This is not a substitute for a proper semantic versioning policy. The changelog reflects what you decide to release; it does not enforce version numbers.
This is not a claim that any one tool works for every project. Teams with strict regulatory needs may require additional approval steps that go beyond the simple workflow shown here.
This is not an endorsement of any particular host. GitHub Releases is used as an example because it is widely available, but the same ideas apply to GitLab Releases, Bitbucket Downloads, or an internal artifact repository.
This is not a guarantee that the changelog will always be read by end users. Some audiences prefer inline release notes inside the application, others rely on blog posts or newsletters.
This is not a statement that the process described is the only valid way to produce release notes. Other teams may prefer hand‑written notes, issue‑based tracking, or changelog plugins for their static site generator.