Skip to main content
WP HealthKit

WordPress Plugin Changelog: Best Practices and Standards

June 25, 202617 min readQualityBy Jamie

Your changelog is often the first thing users read before updating your WordPress plugin. A well-maintained changelog builds trust, communicates the value of updates, and prevents users from being surprised by breaking changes. Yet many plugin developers treat changelogs as an afterthought—if they maintain them at all.

WordPress plugin changelog best practices represent a critical but underappreciated aspect of plugin quality and user communication. A thoughtful changelog demonstrates professional development practices and respects user time by explaining exactly what changed and why.

This guide explores changelog standards, security advisory formatting, automated generation approaches, and how WordPress.org requirements shape your changelog strategy. WP HealthKit can audit your plugin's documentation practices, including whether your changelog meets WordPress standards.

Table of Contents

  1. Why Plugin Changelogs Matter
  2. Keep a Changelog Format Standard
  3. Categorizing Plugin Changes
  4. Security Advisory Disclosure in Changelogs
  5. WordPress.org Changelog Requirements
  6. Automated Changelog Generation
  7. Version Management with Semantic Versioning
  8. Frequently Asked Questions

Why Plugin Changelogs Matter

Changelogs serve multiple audiences with different information needs:

For end users: A changelog answers the crucial question: "Should I update right now, or can I wait?" Users running production sites need to know about breaking changes, security vulnerabilities, and performance improvements. A detailed changelog builds confidence that the update is worth the risk and effort.

For developers integrating your plugin: Plugin developers who build on top of your plugin's functions, hooks, and filters need to understand API changes. A changelog explaining removed functions, renamed hooks, and altered behaviors prevents broken child installations.

For security researchers: When a security vulnerability exists, security teams research the affected versions. A detailed changelog makes it easy to identify when a fix was released, ensuring quick patching.

For your business: A professional changelog demonstrates stability, transparency, and care. It influences purchasing decisions for premium plugins and affects your reputation in the WordPress community.

The stakes are especially high for WordPress plugin changelog documentation because users make update decisions based partly on what they read there. Unclear change descriptions lead to update hesitation, missed security fixes, and increased support requests.

Keep a Changelog Format Standard

The "Keep a Changelog" format has become the industry standard for maintaining clear, structured changelogs. Following this format makes your changelog familiar to all developers who use modern software.

Here's the structure:

# Changelog

All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com),
and this project adheres to [Semantic Versioning](https://semver.org).

## [Unreleased]

### Added
- New REST API endpoint for bulk imports

### Changed
- Improved admin dashboard load time by 40%
- Updated jQuery dependency to 3.6.x

### Deprecated
- `widget_shortcode()` function (use `block_render()` instead)

### Removed
- Support for PHP 7.2 (requires PHP 7.4+)

### Fixed
- Fixed memory leak in background process queue
- Fixed typo in settings tab label

### Security
- Fixed SQL injection vulnerability in custom query builder (CVE-2026-1234)

## [2.4.0] - 2026-03-15

### Added
- Bulk user import from CSV
- Custom field mapping in import interface
- Email notification on import completion

### Changed
- Refactored database schema for better performance
- Updated admin interface to use modern JavaScript patterns

### Deprecated
- `bulk_insert_users()` function (will be removed in 3.0.0)

### Fixed
- Fixed issue where imported users had no assigned role
- Fixed character encoding in CSV import

### Security
- Fixed reflected XSS in settings page (CVE-2026-0987)
- Improved nonce verification in all admin forms

## [2.3.1] - 2026-02-28

### Fixed
- Fixed fatal error when importing from specific CSV formats
- Fixed issue where form submissions were lost on network timeout

## [2.3.0] - 2026-02-20

### Added
- REST API endpoints for import management
- Support for custom field mapping in API

### Changed
- Improved error messages in import dialog

Let's break down the key sections:

Unreleased section shows features and fixes that are in development but not yet released. This helps users see what's coming and allows your issue tracker to stay in sync with your codebase.

Added includes new functionality, new hooks, new REST endpoints, new database tables, or any meaningful additions to the plugin's capabilities.

Changed covers modifications to existing behavior. Performance improvements, UI redesigns, refactored code—anything that alters how the plugin works without removing features.

Deprecated warns that something will be removed in a future version. This is crucial for protecting developers who depend on your plugin's APIs. Give a deprecation path: "Use new_function() instead."

Removed lists functionality that's no longer present. This is the impact of deprecations from previous releases.

Fixed covers bug fixes and corrections.

Security specifically calls out security fixes, CVEs, and vulnerability patches.

Categorizing Plugin Changes

Not all changes belong in every changelog entry. Use judgment about what information matters to users.

Don't include:

  • Internal refactoring that doesn't affect behavior
  • Documentation improvements (unless a common confusion is resolved)
  • Code style cleanup
  • CI/CD pipeline changes
  • Dependencies that have no impact on users
  • Internal performance improvements users won't notice

Do include:

  • New features and hooks
  • Breaking changes
  • Security vulnerabilities
  • Performance improvements users will notice
  • Fixed bugs affecting common workflows
  • Dependency updates users might care about
  • Deprecated functions/hooks with migration paths
  • Changed behavior or APIs

Example of too-detailed changelog:

## [2.0.0] - 2026-03-15

### Changed
- Refactored ImportClass to use new structure
- Changed variable naming from $arr to $array
- Updated 47 function docblocks
- Modified internal queue system
- Adjusted CSS spacing on admin page

Example of appropriately-detailed changelog:

## [2.0.0] - 2026-03-15

### Added
- REST API for bulk imports (new `/wp-json/my-plugin/v1/imports` endpoint)

### Changed
- Database schema completely redesigned for better performance
- Admin dashboard UI migrated to modern Vue.js components

### Removed
- Legacy `$_SESSION` storage (uses WordPress options instead)
- PHP 7.2 support (requires PHP 7.4+)

### Fixed
- Fixed memory leak in background processing queue
- Fixed issue where imports would fail on large CSV files

### Security
- Fixed SQL injection in the import query builder (CVE-2026-1234)

The key difference: the second changelog tells users whether they should care about this release. The first changelog is too technical for users who don't understand your code structure.

Security Advisory Disclosure in Changelogs

Security vulnerabilities require special handling in your changelog. Users need clear information to understand the severity and urgency of security updates.

Standard security disclosure format:

### Security

- Fixed SQL injection vulnerability in custom query builder 
  (Affected versions: 1.8.0-1.9.2, Fixed in 1.9.3) 
  (CVE-2026-1234, CVSS Score: 8.6 High)
  Report credit: Jane Researcher via HackerOne

Include:

  • Brief description of what was vulnerable
  • Which versions are affected
  • Which version includes the fix
  • CVE identifier (if assigned)
  • CVSS score and severity level (if available)
  • Credit to researcher (if permitted)

Don't include:

  • Technical details of the exploit (release these after users have time to patch)
  • Reproduction steps that attackers could use
  • Proof-of-concept code
  • Vulnerability timeline details until well after patching

If a vulnerability is severe, consider a separate security advisory separate from the regular changelog:

# Security Advisory: SQL Injection in My Plugin

**Affected versions:** 1.8.0, 1.8.1, 1.9.0, 1.9.1, 1.9.2
**Fixed in:** 1.9.3 (Released 2026-03-18)
**Severity:** HIGH (CVSS 8.6)
**CVE:** CVE-2026-1234

## Summary

A SQL injection vulnerability exists in the custom query builder 
used by the import functionality. An authenticated user with manage_options 
capability could craft a malicious CSV to execute arbitrary SQL.

## Impact

This vulnerability allows a site administrator to:
- Read sensitive data from the WordPress database
- Modify data in the database
- Potentially gain code execution

Site administrators are trusted users, so the impact is limited. However, 
this should still be patched immediately.

## Mitigation

Update to version 1.9.3 immediately. No configuration changes required.

## Timeline

- 2026-03-10: Vulnerability reported via HackerOne
- 2026-03-12: Fix developed and tested
- 2026-03-18: Version 1.9.3 released with fix

WordPress.org Changelog Requirements

The official WordPress Plugin Directory has specific requirements for readme.txt changelog sections. Understanding these ensures your plugin passes review and displays correctly.

WordPress.org requires changelog information in the readme.txt file using a specific format:

== Changelog ==

= Version 2.4.0 =
* Added: New REST API endpoint for bulk imports
* Changed: Improved admin dashboard load time by 40%
* Fixed: Fixed memory leak in background process queue
* Security: Fixed SQL injection vulnerability (CVE-2026-1234)

= Version 2.3.1 =
* Fixed: Fixed fatal error in import functionality
* Fixed: Fixed form submission loss on timeout

= Version 2.3.0 =
* Added: REST API endpoints for import management
* Changed: Improved error messages in import dialog
* Security: Fixed reflected XSS in settings page

= Version 2.2.0 =
* Added: Support for custom field mapping
* Changed: Refactored database schema

WordPress.org style notes:

  1. Use = Version X.Y.Z = headers (not markdown ##)
  2. Use asterisks * for bullet points, not dashes
  3. Start each bullet with an action word: Added, Changed, Fixed, etc.
  4. Keep entries concise (one line each, wrapped at editor width)
  5. Most recent version at top
  6. List all versions that have been released
  7. WordPress.org will display the latest stable release version prominently

Best Practice: Maintain your changelog in Keep a Changelog markdown format, then generate your WordPress.org-compatible readme.txt changelog section from it. WP HealthKit can verify that your plugin's readme.txt is properly formatted and meets WordPress standards.

Audit your plugin's changelog and documentation standards with WP HealthKit.

Automated Changelog Generation

Maintaining a changelog manually is tedious. Automated generation can keep your changelog up-to-date with minimal effort.

Using Git commit messages:

Adopting a structured commit message format allows tools to generate changelogs automatically:

# Conventional Commits format
git commit -m "feat: add REST API endpoint for imports"
git commit -m "fix: resolve SQL injection in query builder"
git commit -m "docs: update installation guide"
git commit -m "refactor: improve import class structure"
git commit -m "BREAKING CHANGE: remove legacy session storage"

Tools like git-cliff or standard-version can parse these commits and generate changelog entries:

# Install standard-version
npm install --save-dev standard-version

# Generate changelog from commits
npx standard-version --release-as 2.4.0

This generates Keep a Changelog format from your commit history:

// .versionrc configuration
{
  "types": [
    {"type": "feat", "section": "Features", "hidden": false},
    {"type": "fix", "section": "Bug Fixes", "hidden": false},
    {"type": "perf", "section": "Performance", "hidden": false},
    {"type": "security", "section": "Security", "hidden": false},
    {"type": "docs", "section": "Documentation", "hidden": true},
    {"type": "style", "section": "Styling", "hidden": true},
    {"type": "refactor", "section": "Refactoring", "hidden": true}
  ]
}

Using issue tracking:

If you track features and bug fixes in GitHub Issues or similar platforms, you can generate changelogs from closed issues:

// WordPress plugin changelog generator
class ChangelogGenerator {
    private $github_token;
    
    public function __construct($github_token) {
        $this->github_token = $github_token;
    }
    
    public function generate_changelog_from_milestones($owner, $repo) {
        // Get closed issues for this release
        $response = wp_remote_get(
            "https://api.github.com/repos/{$owner}/{$repo}/issues?state=closed&labels=released",
            [
                'headers' => [
                    'Authorization' => "token {$this->github_token}",
                    'Accept' => 'application/vnd.github.v3+json',
                ],
            ]
        );
        
        $issues = json_decode(wp_remote_retrieve_body($response), true);
        
        // Group by type
        $grouped = [
            'Added' => [],
            'Changed' => [],
            'Fixed' => [],
            'Security' => [],
        ];
        
        foreach ($issues as $issue) {
            $type = $this->determine_type_from_labels($issue['labels']);
            $grouped[$type][] = $issue;
        }
        
        return $this->format_changelog($grouped);
    }
    
    private function determine_type_from_labels($labels) {
        foreach ($labels as $label) {
            if ($label['name'] === 'type:security') return 'Security';
            if ($label['name'] === 'type:feature') return 'Added';
            if ($label['name'] === 'type:improvement') return 'Changed';
            if ($label['name'] === 'type:bug') return 'Fixed';
        }
        return 'Changed';
    }
}

Using a changelog management plugin:

For WordPress-specific changelog generation, plugins like "Changelog" or "Release Notes" provide UI for managing changelogs:

// Using a changelog management library
class ChangelogWriter {
    public function add_entry($version, $type, $description) {
        $changelog = get_option('plugin_changelog', []);
        
        if (!isset($changelog[$version])) {
            $changelog[$version] = [];
        }
        
        if (!isset($changelog[$version][$type])) {
            $changelog[$version][$type] = [];
        }
        
        $changelog[$version][$type][] = $description;
        update_option('plugin_changelog', $changelog);
    }
    
    public function render_changelog_markdown() {
        $changelog = get_option('plugin_changelog', []);
        $output = "# Changelog\n\n";
        
        foreach ($changelog as $version => $types) {
            $output .= "## [$version]\n\n";
            
            foreach ($types as $type => $entries) {
                $output .= "### $type\n\n";
                foreach ($entries as $entry) {
                    $output .= "- $entry\n";
                }
                $output .= "\n";
            }
        }
        
        return $output;
    }
}

Version Management with Semantic Versioning

Semantic Versioning (SemVer) provides a clear system for version numbers that communicate the severity of changes:

Format: MAJOR.MINOR.PATCH

  • MAJOR version when you make incompatible API changes
  • MINOR version when you add functionality in a backward compatible manner
  • PATCH version when you make backward-compatible bug fixes

Examples:

  • 2.0.0: Major version (breaking changes, new API structure)
  • 2.1.0: Minor version (new features, backward compatible)
  • 2.1.3: Patch version (bug fixes only)

Why this matters for WordPress plugin changelog documentation:

When you follow SemVer, users can glance at the version number and understand update risk. Version 2.0.0 signals "I need to test this carefully before updating production." Version 2.1.1 signals "This is a safe update with just bug fixes."

// Example: When to increment each version number

// PATCH increment: bug fixes only
// 1.5.0 -> 1.5.1 (fixed typo in settings label)

// MINOR increment: new features, backward compatible
// 1.5.0 -> 1.6.0 (added new shortcode)

// MAJOR increment: breaking changes
// 1.5.0 -> 2.0.0 (removed deprecated functions, changed database schema)

class VersionChecker {
    public function get_next_version($current, $change_type) {
        list($major, $minor, $patch) = explode('.', $current);
        
        switch ($change_type) {
            case 'patch':
                $patch++;
                break;
            case 'minor':
                $minor++;
                $patch = 0;
                break;
            case 'major':
                $major++;
                $minor = 0;
                $patch = 0;
                break;
        }
        
        return "$major.$minor.$patch";
    }
}

Broader Context and Best Practices

Code quality in WordPress plugins extends far beyond aesthetic preferences or stylistic choices. Quality code is fundamentally about maintainability, which directly impacts security, performance, and reliability over time. When code is well-structured with clear separation of concerns, consistent naming conventions, and comprehensive error handling, bugs are easier to spot, fixes are faster to implement, and new features can be added without introducing regressions. The investment in code quality pays dividends throughout the entire lifecycle of a plugin, from initial development through years of maintenance and updates.

The WordPress plugin ecosystem benefits enormously from shared coding standards and conventions. When developers follow established patterns for hook usage, option storage, database operations, and API interactions, their code becomes instantly readable to other WordPress developers. This readability matters not just for open-source contributions but also for commercial plugins where team members change over time. A plugin written to WordPress coding standards can be handed off to a new developer with minimal onboarding. This consistency is why automated tooling for standards enforcement has become an essential part of the modern WordPress development workflow.

Technical debt in WordPress plugins accumulates silently until it becomes a crisis. Each shortcut taken during development, each deprecated function left in place, each test not written adds to the debt balance. Unlike financial debt, technical debt compounds unpredictably. A deprecated function might work fine for years until a WordPress core update removes it entirely, breaking the plugin for all users simultaneously. Proactive quality management through automated code analysis identifies these time bombs before they detonate, giving developers time to address issues on their own schedule rather than scrambling during an emergency.

Modern WordPress development demands a level of engineering discipline that matches the platform's maturity. Plugins that started as simple utility scripts a decade ago now handle payment processing, personal data management, and business-critical workflows. The stakes have risen accordingly. Applying professional software engineering practices like automated testing, continuous integration, dependency management, and architectural patterns isn't over-engineering for WordPress. It's meeting the responsibility that comes with code running on millions of websites, handling real users' data and real businesses' operations.

Broader Context and Best Practices

Code quality in WordPress plugins extends far beyond aesthetic preferences or stylistic choices. Quality code is fundamentally about maintainability, which directly impacts security, performance, and reliability over time. When code is well-structured with clear separation of concerns, consistent naming conventions, and comprehensive error handling, bugs are easier to spot, fixes are faster to implement, and new features can be added without introducing regressions. The investment in code quality pays dividends throughout the entire lifecycle of a plugin, from initial development through years of maintenance and updates.

The WordPress plugin ecosystem benefits enormously from shared coding standards and conventions. When developers follow established patterns for hook usage, option storage, database operations, and API interactions, their code becomes instantly readable to other WordPress developers. This readability matters not just for open-source contributions but also for commercial plugins where team members change over time. A plugin written to WordPress coding standards can be handed off to a new developer with minimal onboarding. This consistency is why automated tooling for standards enforcement has become an essential part of the modern WordPress development workflow.

Frequently Asked Questions

Should I maintain both CHANGELOG.md and readme.txt changelog?

The best approach is to maintain one "source of truth" in Keep a Changelog format, then generate the WordPress.org-compatible readme.txt changelog from it. Many projects keep CHANGELOG.md in the repository root and use build tools to generate readme.txt for distribution.

How far back should I maintain changelog history?

Keep a complete historical changelog indefinitely. Old versions help users understand when features were added and when bugs were fixed. Archive very old versions (2+ years) in a separate section if the main changelog becomes too long.

What should I do if I forget to document a change?

Add it to the changelog when you discover the omission. Include a note: "(Retroactively documented)" if it's from a previous release. It's better to document late than to leave users confused about when something changed.

If a vulnerability was discovered in an old version, still document it in the changelog for that version's entry, and consider a separate security advisory. Users running older versions deserve to know about risks.

Is automated changelog generation better than manual?

Automated generation is better for consistency and sustainability, but requires discipline with commit messages. Manual changelogs allow for better storytelling and can group related changes. Hybrid approach: commit messages provide structure, but review them before finalizing the changelog entry.

Yes. Link to:

  • GitHub commits or PRs that address the change
  • CVE identifiers and security databases
  • Related documentation
  • Issue tickets that the change resolves
- Fixed: Memory leak in background queue ([#234](https://github.com/you/plugin/issues/234))
- Security: SQL injection in import builder ([CVE-2026-1234](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2026-1234))

Can I modify old changelog entries?

Rarely. Only correct factual errors (wrong version numbers, typos). Never change historical changelog entries to rewrite history. If you need to clarify something, add a note in the current release.

Conclusion

A well-maintained WordPress plugin changelog demonstrates professionalism, helps users make confident update decisions, and provides a valuable historical record of your plugin's evolution. By following the Keep a Changelog format, using semantic versioning, and automating where possible, you can maintain your changelog sustainably.

The effort you invest in changelog quality directly impacts user satisfaction. Users who can quickly understand what's changed and why are more likely to update promptly—including critical security updates.

WP HealthKit audits your plugin's documentation practices, including changelog quality and WordPress.org compliance. Get a professional assessment of whether your changelog meets industry standards and WordPress requirements.

Upload your plugin to WP HealthKit for a complete documentation audit.

For more on WordPress plugin quality, check out our guide to version management and semantic versioning and our comprehensive readme.txt best practices guide.

Ready to audit your plugin?

WP HealthKit checks for all the issues in this article and 40+ more across 49 verification layers.

Comments

WordPress Plugin Changelog: Best Practices and Standards | WP HealthKit