Menu
Theme

Plugin / Microkernel Architecture

What a plug-in (microkernel) architecture is, the design decisions behind contracts, loading, isolation and versioning, and how to apply it to a configuration tool with many device families. For architects designing or reviewing extensible tools.

Researched
15 Oct 2026
Reading time
7 min
Sources
25
Review due
15 Oct 2027
TL;DR
  • Small core, independent plug-ins: the core holds only what is needed to run; plug-ins add specialised behaviour behind contracts.
  • The contract is the product: design and version it first; every core change becomes an API negotiation.
  • Plug-ins declare data, the core renders it: parameter definitions, rules and UI contributions as data keep plug-ins small and safe.
  • Isolation is a spectrum: separate load contexts handle dependency conflicts; untrusted or crash-prone plug-ins need a separate process.
  • Configuration tools are a classic fit: EB tresos Studio delivers device support as Eclipse plug-ins.
On this page 10 sections
  1. The idea in one minute
  2. When it fits
  3. The design decisions
  4. Worked example: a configuration tool
  5. Where it is used in practice
  6. How it relates to other styles
  7. Gains and costs
  8. How it goes wrong
  9. Testing a plug-in system
  10. Origins

The idea in one minute

A microkernel or plug-in architecture has two kinds of component: a core system that contains only the minimal functionality needed to run, and plug-in modules that add specialised behaviour. Plug-ins must stay independent of each other. The core finds them through a registry and talks to them through contracts, and Mark Richards advises deciding a contract versioning strategy at the start.[1]

The name comes from operating systems, where microkernels such as Mach, L4, QNX and MINIX 3 run drivers and file systems outside the kernel so a crashed service can be restarted without a reboot.[24] In application design the same shape appears as Fowler’s Plugin pattern: implementations linked at run time through configuration rather than at compile time.[25]

The core owns the contracts, the registry and a generic editor; device families arrive as plug-ins that declare their parameters, rules and transport. Each plug-in states which contract versions it supports, and risky ones run in their own process.

When it fits

Fits when… Doesn’t fit when…
Variability sits on a stable, well-understood axis: device families, protocols, regional rules, file formats The variability is not understood yet, so a contract cannot be designed
New variants must ship without a core release, or partners and OEMs add them One team, no third parties, everything ships together (a modular monolith is cheaper)
The product is long-lived and its core changes slowly Scaling, not extensibility, drives the design
You can afford to treat the contract as a published API Plug-ins would constantly need each other

Richards rates the style high on agility, deployment, testability and performance, and low on scalability and ease of development.[1]

The design decisions

Contract and versioning

  • Separate contracts package. Microsoft’s plugin tutorial puts the interface in its own library so it can be published as a package.[2]
  • Semantic version ranges. OSGi distinguishes consumers from providers: a provider of an API should import it with a narrower range than a consumer.[18]
  • API stability markers and verification. IntelliJ marks internal, experimental and scheduled-for-removal APIs and ships a Plugin Verifier that checks binary compatibility across IDE versions.[16]
  • Compatibility declared up front. VS Code requires an engines field;[9] IntelliJ uses since and until build ranges.[15]

Discovery

Plug-ins describe themselves in a manifest that the core reads before loading any code. Eclipse plug-ins declare contributions to named extension points;[17] IntelliJ does the same in plugin.xml;[13] VS Code uses package.json with contribution points and activation events;[9] .NET’s MEF scans a directory and matches parts to imports by contract.[5]

Loading and isolation

  • .NET: one AssemblyLoadContext per plug-in, with a dependency resolver that reads the plug-in’s own dependency file, so plug-ins can carry conflicting versions of libraries.[2]
  • Share the contract, or types will not match. The contracts assembly must load once, from the default context; otherwise the same type loaded twice is two different types.[3]
  • A load context is not a security boundary. Untrusted code cannot be safely loaded into a trusted .NET process.[2]
  • Unloading is cooperative. A collectible context stays alive while anything still references it.[4] IntelliJ’s dynamic plugins have the same constraint: one leaked reference forces a restart.[14]
  • Process isolation for anything risky. VS Code moved its extension host into a separate process so misbehaving extensions do not affect the editor.[6][7]

Lifecycle and activation

OSGi bundles move through installed, resolved, starting, active and stopping states, and can activate lazily on first use.[19] VS Code activates extensions on events (a command, a file type, a workspace pattern) and discourages activating on startup.[8] Lazy activation keeps start-up fast when dozens of plug-ins are installed.

Trust

VS Code states that extensions run with the same permissions as the editor itself, and relies on marketplace scanning, signing and publisher trust.[11] In engineering tools, signed plug-in manifests serve the same purpose: EB tresos plug-ins ship signature manifests in META-INF.[23]

Data, not code, for UI

VS Code’s settings contributions are declared as schema-like data that the editor renders without running extension code.[10] For a configuration tool this is the model to copy: plug-ins declare parameter definitions; the core renders the editor.

Worked example: a configuration tool

An illustrative C# sketch for device families delivered as plug-ins.

// Contracts package: shared, loaded once from the default context
public interface IDeviceFamilyPlugin
{
    string FamilyId { get; }                                         // "acme.ecu.bms"
    IReadOnlyList<ParameterDefinition> GetParameters(DeviceVariant variant);
    IEnumerable<ValidationIssue> Validate(IConfigurationSnapshot config);
    IDeviceTransport CreateTransport(TransportOptions options);       // a port, as in hexagonal
}

public sealed record ParameterDefinition(
    string Key, string DisplayName, ParamType Type,
    object? Default, object? Min, object? Max, IReadOnlyList<string>? Choices, string Group);
// Host: one load context per plug-in, contracts forced to the shared copy
sealed class PluginLoadContext(string pluginPath) : AssemblyLoadContext(isCollectible: true)
{
    private readonly AssemblyDependencyResolver _resolver = new(pluginPath);

    protected override Assembly? Load(AssemblyName name) =>
        name.Name == "Acme.Configurator.Contracts" ? null      // use the host's contracts
        : _resolver.ResolveAssemblyToPath(name) is { } path ? LoadFromAssemblyPath(path)
        : null;
}
{
  "id": "acme.ecu.bms",
  "version": "2.3.0",
  "entryAssembly": "Acme.Bms.Plugin.dll",
  "contract": ">=3.1 <4.0",
  "families": ["BMS-G2", "BMS-G3"],
  "activation": ["onDeviceDetected:0x1A2B", "onProjectContains:*.bmscfg"]
}

The core reads the manifest, rejects incompatible contract ranges before loading anything, and activates the plug-in only when a matching device or project appears. A plug-in that wraps a native vendor driver runs in a helper process and talks to the core over IPC instead.

Where it is used in practice

  • EB tresos Studio (AUTOSAR ECU configuration) is built on Eclipse and extended with plug-ins.[22] Texas Instruments ships its microcontroller drivers’ configuration as tresos plug-ins: parameter definitions, presets, code-generation templates, signature manifests and a plugin.xml, copied into the tool’s plugins folder.[23] This is the same pattern applied to exactly the kind of tool this article is about.
  • Eclipse describes plug-ins as structured bundles that contribute functionality at well-defined extension points.[17]
  • VS Code combines a separate extension host process, activation events and signed marketplace packages.[6][11]
  • IntelliJ Platform offers extension points, dynamic loading and automated compatibility verification.[13][16]

How it relates to other styles

  • Hexagonal / Clean. Any device that follows a port’s protocol can be plugged into it;[20] frameworks and drivers are outer-ring details.[21] A plug-in is an adapter that ships separately and loads at run time. The difference: hexagonal adapters usually face infrastructure, while plug-ins usually carry domain variability, so their contract sits at the domain boundary and must be designed as a public API.
  • Modular monolith. Both are one deployable with internal parts. Modules are compiled in and versioned together; plug-ins are discovered at run time, versioned independently and may come from third parties.

Gains and costs

What it buys

  • Ship variants without a core release. New device families arrive as plug-ins.
  • Isolated variability. Family-specific code cannot leak into the core.
  • Ecosystem. Partners, OEMs and customers can extend the tool.
  • Testable in isolation. Each plug-in can be tested against the contract alone.
  • Lean start-up. Lazy activation loads only what a project needs.

What it costs

  • Contract churn. Every core change becomes a breaking-change discussion.
  • Dependency conflicts between plug-ins and type-identity traps across load contexts.
  • Isolation is costly. In-process is fast but fragile; out-of-process is safe but needs IPC.
  • Security. Plug-ins run with the host's rights unless you isolate them.
  • Harder to develop. Debugging across the core and many plug-ins is slower.

How it goes wrong

  1. Contract designed too early. The variability axis was not understood, so the contract changes with every new family.
  2. Plug-ins depending on plug-ins. Independence breaks and load order starts to matter.
  3. The core as a god object. Everything that does not fit a contract lands in the core, which becomes a service locator for everything.
  4. In-process vendor code. A crash or leak in one native driver takes down the whole tool or blocks unloading.[4]
  5. Eager activation. Every plug-in starts at launch and start-up time grows with each family.[8]
  6. No compatibility checks. Old plug-ins load against a new core and fail at run time instead of being rejected up front.

Testing a plug-in system

Level What How
Contract test kit Every plug-in honours the contract Abstract tests shipped with the contracts package: unique parameter keys, defaults within ranges, deterministic validation, clean disposal
Host Loading, activation, rejection, unloading A fake plug-in; check that an unloaded context is actually collected[4]
Compatibility matrix Each plug-in version against each supported core and contract version Prior art: the IntelliJ Plugin Verifier across IDE versions[16] and VS Code tests against pinned editor versions[12]
Plug-in internal Rules and parameter logic Ordinary unit tests; decoupled parts make a harness easy[5]

Origins

  • Operating systems: microkernels moved drivers and services out of the kernel; the architectural style borrows the name and the idea.[24]
  • 2002: Fowler documents the Plugin pattern in Patterns of Enterprise Application Architecture.[25]
  • 2000s: Eclipse builds a whole IDE platform on plug-ins and OSGi bundles, later the basis of engineering tools such as EB tresos Studio.[17][22]
  • 2015: Richards describes the microkernel as one of five core architecture patterns.[1]
  • 2022: VS Code completes the move of extensions into a separate process for stability.[7]

Key takeaways

  1. Use it when variability is stable and well understood (device families, protocols, regional rules) and must ship without a core release.
  2. Put the contract in its own versioned package, declare compatible ranges in a manifest, and check compatibility before loading code.
  3. Keep plug-ins independent of each other; if they need each other, they belong in the core or in one plug-in.
  4. A load context is not a security boundary; vendor DLLs and third-party code go out of process.
  5. Ship a contract test kit with the contracts package and run a compatibility matrix in CI.

Sources

  1. Software Architecture Patterns: microkernel architecture · Mark Richards, 2015Primaryoreilly.com
  2. Create a .NET Core application with plugins · Microsoft LearnPrimarylearn.microsoft.com
  3. About AssemblyLoadContext · Microsoft LearnPrimarylearn.microsoft.com
  4. How to use and debug assembly unloadability · Microsoft LearnPrimarylearn.microsoft.com
  5. Managed Extensibility Framework (MEF) · Microsoft LearnPrimarylearn.microsoft.com
  6. Extension Host · VS Code documentationPrimarycode.visualstudio.com
  7. Migrating VS Code to process sandboxing · VS Code blog, 2022Primarycode.visualstudio.com
  8. Activation events · VS Code documentationPrimarycode.visualstudio.com
  9. Extension manifest · VS Code documentationPrimarycode.visualstudio.com
  10. Contribution points · VS Code documentationPrimarycode.visualstudio.com
  11. Extension runtime security · VS Code documentationPrimarycode.visualstudio.com
  12. Testing extensions · VS Code documentationPrimarycode.visualstudio.com
  13. Extension points · IntelliJ Platform SDKPrimaryplugins.jetbrains.com
  14. Dynamic plugins · IntelliJ Platform SDKPrimaryplugins.jetbrains.com
  15. Build number ranges · IntelliJ Platform SDKPrimaryplugins.jetbrains.com
  16. Verifying plugin compatibility · IntelliJ Platform SDKPrimaryplugins.jetbrains.com
  17. Eclipse platform architecture · Eclipse documentationPrimaryhelp.eclipse.org
  18. OSGi Core 8: module layerPrimarydocs.osgi.org
  19. OSGi Core 8: life cycle layerPrimarydocs.osgi.org
  20. Hexagonal Architecture · Alistair Cockburn, 2005Primaryalistair.cockburn.us
  21. The Clean Architecture · Robert C. Martin, 2012Primaryblog.cleancoder.com
  22. EB tresos Studio · Elektrobit (vendor)Primaryelektrobit.com
  23. MCAL configurator and EB tresos · Texas Instruments (vendor)Primarysoftware-dl.ti.com
  24. Microkernel · WikipediaReferenceen.wikipedia.org
  25. Plugin · Martin Fowler, Patterns of Enterprise Application ArchitectureReferencemartinfowler.com
↑ ↓ to moveEnter to openFull-text search by Pagefind