When Plugin B depends on Plugin A but the host loads plugins alphabetically or in undefined order, Plugin B crashes with a cryptic error whenever its load happens to precede Plugin A's. ISO 25010 maintainability.modularity requires that inter-component dependencies be explicit and resolvable; implicit alphabetical load order is the opposite of this. The failure is also non-deterministic across environments — it may work in development (where plugin directories happen to be in the right order) and fail in production after a deploy changes the filesystem state.
Low because undeclared plugin dependencies produce ordering-dependent runtime crashes that appear only when load sequence happens to be wrong, making them hard to reproduce and hard to attribute.
Support a dependencies field in plugin manifests and topologically sort the load order before initializing any plugin. Circular dependencies should produce an explicit error at startup, not a runtime crash.
function resolveLoadOrder(plugins: PluginManifest[]): PluginManifest[] {
const graph = new Map<string, string[]>();
for (const p of plugins) {
graph.set(p.name, Object.keys(p.dependencies ?? {}));
}
return topologicalSort(graph); // throws on cycles
}
ID: plugin-extension-architecture.versioning-contracts.dependency-resolution
Severity: low
What to look for: Check whether the plugin system handles dependencies between plugins. When Plugin B depends on Plugin A, look for:
is_plugin_active('plugin-a/plugin-a.php') but no formal dependency systemfastify-plugin with dependencies array — enforced load order"extensionDependencies": ["publisher.extension-id"] in package.jsonPass criteria: Count all plugin dependency declarations. Plugin-to-plugin dependencies can be declared and are resolved by the host. Dependent plugins load after their dependencies. Missing dependencies produce a clear error at load time (not a runtime crash).
Fail criteria: No dependency resolution — all plugins load independently. Plugin B that depends on Plugin A must handle the dependency itself (check at runtime, fail with a cryptic error, or assume Plugin A is loaded).
Skip (N/A) when: The plugin system has fewer than 5 plugins total and no plugin depends on another. Also skip for middleware systems where order is explicitly configured by the user (not declared by plugins).
Detail on fail: "No dependency resolution exists. Plugins load in alphabetical directory order. Plugin B requires Plugin A's API but has no way to declare this. If Plugin A loads after Plugin B (because 'A' < 'B' alphabetically), Plugin B crashes with 'Cannot read property of undefined' when trying to access Plugin A's exports."
Remediation: Dependency resolution prevents ordering bugs and gives clear errors when required plugins are missing.
// Topological sort for plugin load order:
function resolveLoadOrder(plugins: PluginManifest[]): PluginManifest[] {
const graph = new Map<string, string[]>();
for (const p of plugins) {
graph.set(p.name, Object.keys(p.dependencies ?? {}));
}
return topologicalSort(graph); // throws on cycles
}