# Plugin API versioned

- **Pattern:** `ab-002088` (`plugin-extension-architecture.versioning-contracts.api-versioned`)
- **Severity:** high
- **Lifecycle:** active
- **Last modified:** 2026-04-18
- **Canonical URL:** https://auditbuffet.com/patterns/ab-002088
- **License:** CC-BY-4.0 — attribute to AuditBuffet Pattern Catalog (https://auditbuffet.com/patterns/ab-002088)

## Why it matters

Without a declared plugin API version, every host release is a potential silent breaking change for the entire plugin ecosystem. Plugins have no way to declare compatibility, so the host cannot warn before loading an incompatible plugin, and plugin authors have no signal to know which host version their code targets. ISO 25010 maintainability.modifiability requires that the impact of changes be assessable before they are made; API versioning is the mechanism that makes that assessment possible at the plugin-host boundary.

## Severity rationale

High because an undeclared plugin API version prevents compatibility checking at load time, causing hook signature changes in host updates to break all installed plugins silently and without warning.

## Remediation

Declare a `PLUGIN_API_VERSION` constant in the host and require plugins to specify their compatible version range in their manifest. Reject plugins at load time when the range is not satisfied.

```typescript
// Host declares its API version:
export const PLUGIN_API_VERSION = '2.0.0';

// plugin.json:
{ "name": "my-plugin", "engines": { "host": "^2.0.0" } }

// Host checks on load:
if (!semver.satisfies(PLUGIN_API_VERSION, plugin.manifest.engines.host)) {
  throw new PluginIncompatibleError(
    plugin.name, plugin.manifest.engines.host, PLUGIN_API_VERSION
  );
}
```

## Detection

- **ID:** `api-versioned`
- **Severity:** `high`
- **What to look for:** Check whether the host declares a version for its plugin API, separate from the host application version. This allows the host to ship UI changes, bug fixes, and internal refactors without incrementing the plugin API version — only changes that affect plugins trigger a plugin API version bump. Look for:
  1. An explicit `pluginApiVersion`, `extensionEngineVersion`, or `apiVersion` field in host configuration or manifest
  2. A version check during plugin loading (e.g., "this plugin requires host API v2.x, current is v3.x — incompatible")
  3. Documentation that distinguishes "host version" from "plugin API version"
  Real-world examples:
  - VS Code: `"engines": { "vscode": "^1.70.0" }` in extension package.json
  - Chrome extensions: `"manifest_version": 3` in manifest.json
  - WordPress: `Requires at least: 6.0` and `Tested up to: 6.4` in plugin headers
  - Minecraft: `"min_engine_version": "1.20.0"` in addon manifest
- **Pass criteria:** Count all API version identifiers. The host declares a plugin API version that plugins can check against. The version is distinct from the host application version (or the host version IS the API contract and is documented as such). Plugins can declare which API version they require.
- **Fail criteria:** No plugin API version exists. Plugins have no way to declare compatibility. The host can change its plugin API at any time and plugins break silently without any version mismatch warning.
- **Skip (N/A) when:** The plugin system is internal-only (all plugins live in the same repository as the host and are versioned together). Also skip for plugin systems with fewer than 2 published plugins where versioning hasn't become necessary yet.
- **Detail on fail:** `"No plugin API version is declared anywhere in the host. Plugins have no way to specify which version of the host they're compatible with. When the host changes its hook signatures or removes a hook, all installed plugins break with no warning or compatibility check."`
- **Remediation:** Plugin API versioning is the contract between host and plugin authors. Without it, every host update is a potential breaking change for the entire plugin ecosystem.

  ```typescript
  // Host declares its API version:
  export const PLUGIN_API_VERSION = '2.0.0';

  // Plugin declares its requirement:
  // plugin.json:
  { "name": "my-plugin", "engines": { "host": "^2.0.0" } }

  // Host checks on load:
  if (!semver.satisfies(PLUGIN_API_VERSION, plugin.manifest.engines.host)) {
    throw new PluginIncompatibleError(plugin.name, plugin.manifest.engines.host, PLUGIN_API_VERSION);
  }
  ```

- **Cross-reference:** For backward compatibility across versions, see the `backwards-compat` check below.

## External references

- iso-25010 maintainability.modifiability

Taxons: code-quality

HTML version: https://auditbuffet.com/patterns/ab-002088
