# Plugin / Extensibility / IoC

> Every principle in this category is listed as a record.

Page: Ontology · Principles
Canonical: https://banes-lab.com/ontology#architecture-category-plugin-extensibility-ioc

Listed in [Ontology · Principles](https://banes-lab.com/api/pages/ontology/principles.md), after [Streaming / Pipeline / Dataflow Processing](https://banes-lab.com/ontology/principles/architecture-category-streaming-pipeline-dataflow-processing.md) and before [Self-Healing / Recovery / Deployment Safety](https://banes-lab.com/ontology/principles/architecture-category-self-healing-recovery-deployment-safety.md).

Every principle in this category is listed as a record. Each record carries its kind, its severity, the scopes it applies at and the layer it lives in, then the edge relations that join it to other records, the records that point back at it, the contracts that answer to it and the tensions it takes part in. The descriptors say how it is violated, detected, measured, repaired and enforced. Where the record carries one, an exemplar shows the shape before and after the principle is applied.

Relations diagram

The relations inside this category.

```mermaid
flowchart LR
n_plugin_architecture["Plugin Architecture"]
n_extension_points["Extension Points"]
n_inversion_of_control["Inversion of Control (IoC)"]
n_dependency_injection["Dependency Injection"]
n_service_registry["Service Registry"]
n_registry_pattern["Registry Pattern"]
n_service_locator_pattern["Service Locator Pattern"]
n_feature_toggle["Feature Toggle"]
n_plugin_architecture --> n_extension_points
n_extension_points --> n_plugin_architecture
n_inversion_of_control --> n_dependency_injection
```

### Plugin Architecture

- Kind: [style](https://banes-lab.com/records/kind/style.md)
- Category: [Plugin / Extensibility / IoC](https://banes-lab.com/ontology/principles/architecture-category-plugin-extensibility-ioc.md)
- Severity: [contextual](https://banes-lab.com/records/vocabulary/severity-contextual.md)
- Scope: component, runtime, system
- Layer: [Extensibility Core](https://banes-lab.com/records/layer/extensibility-core.md)

Details

Definition
A convention of building a small core that discovers and loads plugins through declared extension points.

Requires
[Extension Points](https://banes-lab.com/records/architecture/extension-points.md), [Stable Interfaces](https://banes-lab.com/records/architecture/stable-interfaces.md), [Discovery](https://banes-lab.com/records/lexicon/discovery.md)

Reinforces
[Open/Closed Principle (OCP)](https://banes-lab.com/records/architecture/open-closed.md), [Modularity](https://banes-lab.com/records/architecture/modularity.md)

Enables
[Runtime Extensibility](https://banes-lab.com/records/architecture/runtime-extensibility.md)

In tension with
[Static Analysis](https://banes-lab.com/records/architecture/static-analysis.md), [Security](https://banes-lab.com/records/lexicon/security.md)

Conflicts with
[Hardcoded Extensions](https://banes-lab.com/records/lexicon/hardcoded-extensions.md)

Referenced by
[Runtime Discovery](https://banes-lab.com/records/architecture/runtime-discovery.md), [Runtime Extensibility](https://banes-lab.com/records/architecture/runtime-extensibility.md), [Modularity](https://banes-lab.com/records/architecture/modularity.md), [Composability](https://banes-lab.com/records/architecture/composability.md), [Replaceability](https://banes-lab.com/records/architecture/replaceability.md), [Open/Closed Principle (OCP)](https://banes-lab.com/records/architecture/open-closed.md), [Self-Describing Architecture](https://banes-lab.com/records/architecture/self-describing-architecture.md), [Extension Points](https://banes-lab.com/records/architecture/extension-points.md)

Tensions
[Plugin Architecture / Static Analysis](https://banes-lab.com/records/tension/plugin-architecture-static-analysis.md), [Plugin Architecture / Security](https://banes-lab.com/records/tension/plugin-architecture-security.md)

Violated by
core importing plugin implementations

Detected by
direct plugin imports, central switch for plugins

Measured by
plugin isolation score

Refactored by
Introduce SPI, Add Registry, Extract Extension Point

Enforced by
plugin contract tests, dependency rules

Before

```typescript
class FooApp {
run() { new FooExport().run(); new BarExport().run(); }
}
```

After

```typescript
interface FooPlugin { name: string; setup(app: FooApp): void; }
class FooApp {
constructor(private readonly plugins: readonly FooPlugin[]) {}
run() { this.plugins.forEach(plugin => plugin.setup(this)); }
}
```

How it is checked

Checked by
plugin contract tests, dependency rules

Population
Every extension point, plugin, registration and dependency construction site

Freshness
A verdict stands until a plugin, a registration or the composition root changes

Refusal
The dependency rule, plugin contract test or banned-API rule fails a change that hardcodes an extension or hides a dependency

Observation
Imports from the core to plugins, construction sites and registry contents, read from source and at startup

Evidence
None, because the catalog states this check as a class, so a watched run belongs to each system that adopts it

Authoritative side
The extension contract, which every plugin conforms to while the core names no plugin

Depends on
[Extension Points](https://banes-lab.com/records/architecture/extension-points.md), [Stable Interfaces](https://banes-lab.com/records/architecture/stable-interfaces.md), [Discovery](https://banes-lab.com/records/lexicon/discovery.md), [Open/Closed Principle (OCP)](https://banes-lab.com/records/architecture/open-closed.md), [Modularity](https://banes-lab.com/records/architecture/modularity.md), [Runtime Extensibility](https://banes-lab.com/records/architecture/runtime-extensibility.md)

Shape it refuses
[Hardcoded Extensions](https://banes-lab.com/records/lexicon/hardcoded-extensions.md)

### Extension Points

- Kind: [mechanism](https://banes-lab.com/records/kind/mechanism.md)
- Category: [Plugin / Extensibility / IoC](https://banes-lab.com/ontology/principles/architecture-category-plugin-extensibility-ioc.md)
- Severity: [recommended](https://banes-lab.com/records/vocabulary/severity-recommended.md)
- Scope: framework, plugin, module
- Layer: [Extensibility Core](https://banes-lab.com/records/layer/extensibility-core.md)

Details

Definition
A mechanism that exposes named hooks or interfaces where new behavior can be added without editing the core.

Requires
[Stable Interfaces](https://banes-lab.com/records/architecture/stable-interfaces.md), [Contracts](https://banes-lab.com/records/lexicon/contracts.md)

Reinforces
[Open/Closed Principle (OCP)](https://banes-lab.com/records/architecture/open-closed.md), [Plugin Architecture](https://banes-lab.com/records/architecture/plugin-architecture.md)

Enables
[Third-Party Extension](https://banes-lab.com/records/lexicon/third-party-extension.md)

In tension with
[API Surface Growth](https://banes-lab.com/records/lexicon/api-surface-growth.md)

Conflicts with
[Closed Core](https://banes-lab.com/records/lexicon/closed-core.md)

Referenced by
[Runtime Extensibility](https://banes-lab.com/records/architecture/runtime-extensibility.md), [Open/Closed Principle (OCP)](https://banes-lab.com/records/architecture/open-closed.md), [Plugin Architecture](https://banes-lab.com/records/architecture/plugin-architecture.md)

Tensions
[Extension Points / API Surface Growth](https://banes-lab.com/records/tension/api-surface-growth-extension-points.md)

Violated by
modifying internals to add behavior

Detected by
repeated core edits for variants

Measured by
extension coverage

Refactored by
Add Hook, Add SPI, Extract Interface

Enforced by
extension tests, API review

Before

```typescript
function saveFoo(foo: Foo) {
validateFoo(foo);
fooStore.save(foo);
sendFooEmail(foo);
}
```

After

```typescript
type FooHooks = { beforeSave: Array<(foo: Foo) => void>; afterSave: Array<(foo: Foo) => void> };
function saveFoo(foo: Foo, hooks: FooHooks) {
hooks.beforeSave.forEach(hook => hook(foo));
fooStore.save(foo);
hooks.afterSave.forEach(hook => hook(foo));
}
```

How it is checked

Checked by
extension tests, API review

Population
Every extension point, plugin, registration and dependency construction site

Freshness
A verdict stands until a plugin, a registration or the composition root changes

Refusal
The dependency rule, plugin contract test or banned-API rule fails a change that hardcodes an extension or hides a dependency

Observation
Imports from the core to plugins, construction sites and registry contents, read from source and at startup

Evidence
None, because the catalog states this check as a class, so a watched run belongs to each system that adopts it

Authoritative side
The extension contract, which every plugin conforms to while the core names no plugin

Depends on
[Stable Interfaces](https://banes-lab.com/records/architecture/stable-interfaces.md), [Contracts](https://banes-lab.com/records/lexicon/contracts.md), [Open/Closed Principle (OCP)](https://banes-lab.com/records/architecture/open-closed.md), [Plugin Architecture](https://banes-lab.com/records/architecture/plugin-architecture.md), [Third-Party Extension](https://banes-lab.com/records/lexicon/third-party-extension.md)

Shape it refuses
[Closed Core](https://banes-lab.com/records/lexicon/closed-core.md)

### Inversion of Control (IoC)

- Kind: [principle](https://banes-lab.com/records/kind/principle.md)
- Category: [Plugin / Extensibility / IoC](https://banes-lab.com/ontology/principles/architecture-category-plugin-extensibility-ioc.md)
- Severity: [recommended](https://banes-lab.com/records/vocabulary/severity-recommended.md)
- Scope: framework, runtime, component
- Layer: [Extensibility Core](https://banes-lab.com/records/layer/extensibility-core.md)

Details

Definition
A design rule that a framework or composition root owns object creation and control flow, and application code supplies the parts it calls.

Requires
[Abstraction](https://banes-lab.com/records/architecture/abstraction.md), [Composition Root](https://banes-lab.com/records/lexicon/composition-root.md)

Reinforces
[Dependency Inversion Principle (DIP)](https://banes-lab.com/records/architecture/dependency-inversion.md), [Dependency Injection](https://banes-lab.com/records/architecture/dependency-injection.md)

Enables
[Framework Control Flow](https://banes-lab.com/records/lexicon/framework-control-flow.md), [Plugins](https://banes-lab.com/records/lexicon/plugins.md)

In tension with
[Traceability](https://banes-lab.com/records/architecture/traceability.md)

Conflicts with
[Direct Control Ownership](https://banes-lab.com/records/lexicon/direct-control-ownership.md)

Tensions
[Inversion of Control (IoC) / Traceability](https://banes-lab.com/records/tension/inversion-of-control-ioc-traceability.md)

Violated by
application manually controlling framework-owned lifecycle

Detected by
scattered object lifecycle construction

Measured by
composition centralization

Refactored by
Introduce Container, Extract Composition Root

Enforced by
lifecycle rules

Before

```typescript
class FooJob {
run() {
const store = new SqlFooStore();
return store.save(makeFoo());
}
}
```

After

```typescript
class FooJob {
constructor(private readonly make: () => Foo, private readonly store: FooStore) {}
run() { return this.store.save(this.make()); }
}
container.run(FooJob);
```

How it is checked

Checked by
lifecycle rules

Population
Every extension point, plugin, registration and dependency construction site

Freshness
A verdict stands until a plugin, a registration or the composition root changes

Refusal
The dependency rule, plugin contract test or banned-API rule fails a change that hardcodes an extension or hides a dependency

Observation
Imports from the core to plugins, construction sites and registry contents, read from source and at startup

Evidence
None, because the catalog states this check as a class, so a watched run belongs to each system that adopts it

Authoritative side
The extension contract, which every plugin conforms to while the core names no plugin

Depends on
[Abstraction](https://banes-lab.com/records/architecture/abstraction.md), [Composition Root](https://banes-lab.com/records/lexicon/composition-root.md), [Dependency Inversion Principle (DIP)](https://banes-lab.com/records/architecture/dependency-inversion.md), [Dependency Injection](https://banes-lab.com/records/architecture/dependency-injection.md), [Framework Control Flow](https://banes-lab.com/records/lexicon/framework-control-flow.md), [Plugins](https://banes-lab.com/records/lexicon/plugins.md)

Shape it refuses
[Direct Control Ownership](https://banes-lab.com/records/lexicon/direct-control-ownership.md)

### Dependency Injection

- Kind: [pattern](https://banes-lab.com/records/kind/pattern.md)
- Category: [Plugin / Extensibility / IoC](https://banes-lab.com/ontology/principles/architecture-category-plugin-extensibility-ioc.md)
- Severity: [recommended](https://banes-lab.com/records/vocabulary/severity-recommended.md)
- Scope: class, module, component
- Layer: [Extensibility Core](https://banes-lab.com/records/layer/extensibility-core.md)

Details

Definition
A design pattern that passes an object its dependencies from outside, usually through its constructor.

Requires
[Abstraction](https://banes-lab.com/records/architecture/abstraction.md), [Composition Root](https://banes-lab.com/records/lexicon/composition-root.md)

Reinforces
[Dependency Inversion Principle (DIP)](https://banes-lab.com/records/architecture/dependency-inversion.md), [Testability](https://banes-lab.com/records/architecture/testability.md)

Enables
[Mocking](https://banes-lab.com/records/lexicon/mocking.md), [Replaceability](https://banes-lab.com/records/architecture/replaceability.md)

In tension with
[Constructor Complexity](https://banes-lab.com/records/lexicon/constructor-complexity.md)

Conflicts with
[Hardcoded Instantiation](https://banes-lab.com/records/lexicon/hardcoded-instantiation.md), [Ambient Context](https://banes-lab.com/records/architecture/ambient-context.md)

Referenced by
[Interface-Based Design](https://banes-lab.com/records/architecture/interface-based-design.md), [Dependency Inversion Principle (DIP)](https://banes-lab.com/records/architecture/dependency-inversion.md), [Singleton Pattern](https://banes-lab.com/records/architecture/singleton-pattern.md), [Inversion of Control (IoC)](https://banes-lab.com/records/architecture/inversion-of-control.md)

Tensions
[Dependency Injection / Constructor Complexity](https://banes-lab.com/records/tension/constructor-complexity-dependency-injection.md)

Distinct from
[Adapter Pattern](https://banes-lab.com/records/architecture/adapter-pattern.md): Dependency injection supplies a dependency from outside, while an adapter makes an incompatible interface fit the one expected.

Distinct from
[Composition Root](https://banes-lab.com/records/lexicon/composition-root.md): Dependency injection is passing dependencies in, while the composition root is the one place where they are wired.

Distinct from
[Ports and Adapters](https://banes-lab.com/records/lexicon/ports-and-adapters.md): Dependency injection is how one object receives its dependencies, while ports and adapters is how a whole core is isolated from its technologies.

Distinct from
[Singleton Pattern](https://banes-lab.com/records/architecture/singleton-pattern.md): Dependency injection passes a dependency in, while a singleton is reached through a global access point.

Violated by
newing dependencies inside business logic

Detected by
direct construction of external dependencies

Measured by
injected dependency ratio

Refactored by
Inject Constructor Parameter, Add Factory

Enforced by
lint rules, dependency review

Before

```typescript
class FooService {
private readonly clock = new SystemClock();
private readonly store = new SqlFooStore();
}
```

After

```typescript
class FooService {
constructor(private readonly clock: Clock, private readonly store: FooStore) {}
}
```

How it is checked

Checked by
lint rules, dependency review

Population
Every extension point, plugin, registration and dependency construction site

Freshness
A verdict stands until a plugin, a registration or the composition root changes

Refusal
The dependency rule, plugin contract test or banned-API rule fails a change that hardcodes an extension or hides a dependency

Observation
Imports from the core to plugins, construction sites and registry contents, read from source and at startup

Evidence
None, because the catalog states this check as a class, so a watched run belongs to each system that adopts it

Authoritative side
The extension contract, which every plugin conforms to while the core names no plugin

Depends on
[Abstraction](https://banes-lab.com/records/architecture/abstraction.md), [Composition Root](https://banes-lab.com/records/lexicon/composition-root.md), [Dependency Inversion Principle (DIP)](https://banes-lab.com/records/architecture/dependency-inversion.md), [Testability](https://banes-lab.com/records/architecture/testability.md), [Mocking](https://banes-lab.com/records/lexicon/mocking.md), [Replaceability](https://banes-lab.com/records/architecture/replaceability.md)

Shape it refuses
[Hardcoded Instantiation](https://banes-lab.com/records/lexicon/hardcoded-instantiation.md), [Ambient Context](https://banes-lab.com/records/architecture/ambient-context.md)

### Service Registry

- Kind: [mechanism](https://banes-lab.com/records/kind/mechanism.md)
- Category: [Plugin / Extensibility / IoC](https://banes-lab.com/ontology/principles/architecture-category-plugin-extensibility-ioc.md)
- Severity: [contextual](https://banes-lab.com/records/vocabulary/severity-contextual.md)
- Scope: runtime, service, plugin
- Layer: [Extensibility Core](https://banes-lab.com/records/layer/extensibility-core.md)

Details

Definition
A mechanism where services or plugins register under a key and are resolved by that key at runtime.

Requires
[Registration Protocol](https://banes-lab.com/records/lexicon/registration-protocol.md)

Reinforces
[Discovery](https://banes-lab.com/records/lexicon/discovery.md), [Dynamic Binding](https://banes-lab.com/records/architecture/dynamic-binding.md)

Enables
[Dynamic Resolution](https://banes-lab.com/records/lexicon/dynamic-resolution.md)

In tension with
[Registry Availability](https://banes-lab.com/records/lexicon/registry-availability.md)

Conflicts with
[Hardcoded Lookup](https://banes-lab.com/records/lexicon/hardcoded-lookup.md)

Referenced by
[Service Discovery](https://banes-lab.com/records/architecture/service-discovery.md)

Tensions
[Service Registry / Registry Availability](https://banes-lab.com/records/tension/registry-availability-service-registry.md)

Distinct from
[Health Checks](https://banes-lab.com/records/architecture/health-checks.md): A service registry resolves a key to a registered service, while health checks report whether an instance is ready to serve.

Violated by
manual endpoint/plugin lookup

Detected by
static lookup tables

Measured by
registry coverage

Refactored by
Register Service, Add Discovery Client

Enforced by
startup checks, [health checks](https://banes-lab.com/records/architecture/health-checks.md)

Before

```typescript
const fooService = new FooService(new SqlFooStore());
const barService = new BarService(new SqlBarStore());
```

After

```typescript
const services = new ServiceRegistry();
services.register("FooStore", () => new SqlFooStore());
services.register("FooService", r => new FooService(r.resolve("FooStore")));
```

How it is checked

Checked by
startup checks, health checks

Population
Every extension point, plugin, registration and dependency construction site

Freshness
A verdict stands until a plugin, a registration or the composition root changes

Refusal
The dependency rule, plugin contract test or banned-API rule fails a change that hardcodes an extension or hides a dependency

Observation
Imports from the core to plugins, construction sites and registry contents, read from source and at startup

Evidence
None, because the catalog states this check as a class, so a watched run belongs to each system that adopts it

Authoritative side
The extension contract, which every plugin conforms to while the core names no plugin

Depends on
[Registration Protocol](https://banes-lab.com/records/lexicon/registration-protocol.md), [Discovery](https://banes-lab.com/records/lexicon/discovery.md), [Dynamic Binding](https://banes-lab.com/records/architecture/dynamic-binding.md), [Dynamic Resolution](https://banes-lab.com/records/lexicon/dynamic-resolution.md)

Shape it refuses
[Hardcoded Lookup](https://banes-lab.com/records/lexicon/hardcoded-lookup.md)

### Registry Pattern

- Kind: [pattern](https://banes-lab.com/records/kind/pattern.md)
- Category: [Plugin / Extensibility / IoC](https://banes-lab.com/ontology/principles/architecture-category-plugin-extensibility-ioc.md)
- Severity: [contextual](https://banes-lab.com/records/vocabulary/severity-contextual.md)
- Scope: runtime, module, plugin
- Layer: [Extensibility Core](https://banes-lab.com/records/layer/extensibility-core.md)

Details

Definition
A design pattern that replaces a conditional over kinds with a keyed table that each kind registers itself into.

Requires
[Keyed Registration](https://banes-lab.com/records/lexicon/keyed-registration.md)

Reinforces
[Discovery](https://banes-lab.com/records/lexicon/discovery.md), [Factory Pattern](https://banes-lab.com/records/architecture/factory-pattern.md)

Enables
[Dynamic Lookup](https://banes-lab.com/records/lexicon/dynamic-lookup.md)

In tension with
[Global State](https://banes-lab.com/records/lexicon/global-state.md)

Conflicts with
[Direct Reference](https://banes-lab.com/records/lexicon/direct-reference.md)

Tensions
[Registry Pattern / Global State](https://banes-lab.com/records/tension/global-state-registry-pattern.md)

Distinct from
[Factory Pattern](https://banes-lab.com/records/architecture/factory-pattern.md): A registry replaces a conditional over kinds with a keyed table, while a factory owns how one object and its defaults are built.

Violated by
ungoverned global registry

Detected by
mutable global maps without lifecycle

Measured by
registry consistency

Refactored by
Encapsulate Registry, Add Typed Keys

Enforced by
registry validation

Before

```typescript
function makeFoo(kind: string) {
if (kind === "foo") return new Foo1();
if (kind === "bar") return new Bar();
throw new Error("unknown kind");
}
```

After

```typescript
type FooFactory = () => Foo;
const registry = new Map<string, FooFactory>();
export const registerFoo = (kind: string, factory: FooFactory) => registry.set(kind, factory);
export const makeFoo = (kind: string) => registry.get(kind)?.() ?? fail(`unknown ${kind}`);
```

How it is checked

Checked by
registry validation

Population
Every extension point, plugin, registration and dependency construction site

Freshness
A verdict stands until a plugin, a registration or the composition root changes

Refusal
The dependency rule, plugin contract test or banned-API rule fails a change that hardcodes an extension or hides a dependency

Observation
Imports from the core to plugins, construction sites and registry contents, read from source and at startup

Evidence
None, because the catalog states this check as a class, so a watched run belongs to each system that adopts it

Authoritative side
The extension contract, which every plugin conforms to while the core names no plugin

Depends on
[Keyed Registration](https://banes-lab.com/records/lexicon/keyed-registration.md), [Discovery](https://banes-lab.com/records/lexicon/discovery.md), [Factory Pattern](https://banes-lab.com/records/architecture/factory-pattern.md), [Dynamic Lookup](https://banes-lab.com/records/lexicon/dynamic-lookup.md)

Shape it refuses
[Direct Reference](https://banes-lab.com/records/lexicon/direct-reference.md)

### Service Locator Pattern

- Kind: [pattern](https://banes-lab.com/records/kind/pattern.md)
- Category: [Plugin / Extensibility / IoC](https://banes-lab.com/ontology/principles/architecture-category-plugin-extensibility-ioc.md)
- Severity: [discouraged](https://banes-lab.com/records/vocabulary/severity-discouraged.md)
- Scope: runtime, dependency access
- Layer: [Extensibility Core](https://banes-lab.com/records/layer/extensibility-core.md)

Details

Definition
A design pattern in which code fetches its dependencies from a global registry at the point of use.

Requires
[Registry](https://banes-lab.com/records/lexicon/registry.md)

Reinforces
[Runtime Lookup](https://banes-lab.com/records/lexicon/runtime-lookup.md)

Enables
[Late Resolution](https://banes-lab.com/records/lexicon/late-resolution.md)

In tension with
[Testability](https://banes-lab.com/records/architecture/testability.md), [Dependency Inversion Principle (DIP)](https://banes-lab.com/records/architecture/dependency-inversion.md), [Explicit Dependencies](https://banes-lab.com/records/lexicon/explicit-dependencies.md)

Conflicts with
none

Tensions
[Service Locator Pattern / Testability](https://banes-lab.com/records/tension/service-locator-pattern-testability.md), [Service Locator Pattern / Dependency Inversion Principle (DIP)](https://banes-lab.com/records/tension/dependency-inversion-principle-dip-service-locator-pattern.md), [Service Locator Pattern / Explicit Dependencies](https://banes-lab.com/records/tension/explicit-dependencies-service-locator-pattern.md)

Violated by
hidden dependencies through global locator

Detected by
service locator calls inside domain logic

Measured by
hidden dependency count

Refactored by
Replace with Dependency Injection

Enforced by
banned API rules

Before

```typescript
class FooController {
save(foo: Foo) {
const store = serviceLocator.resolve<FooStore>("FooStore");
return store.save(foo);
}
}
```

After

```typescript
class FooController {
constructor(private readonly store: FooStore) {}
save(foo: Foo) { return this.store.save(foo); }
}
```

How it is checked

Checked by
banned API rules

Population
Every extension point, plugin, registration and dependency construction site

Freshness
A verdict stands until a plugin, a registration or the composition root changes

Refusal
The dependency rule, plugin contract test or banned-API rule fails a change that hardcodes an extension or hides a dependency

Observation
Imports from the core to plugins, construction sites and registry contents, read from source and at startup

Evidence
None, because the catalog states this check as a class, so a watched run belongs to each system that adopts it

Authoritative side
The extension contract, which every plugin conforms to while the core names no plugin

Depends on
[Registry](https://banes-lab.com/records/lexicon/registry.md), [Runtime Lookup](https://banes-lab.com/records/lexicon/runtime-lookup.md), [Late Resolution](https://banes-lab.com/records/lexicon/late-resolution.md)

Shape it refuses
Not answered

### Feature Toggle

- Kind: [mechanism](https://banes-lab.com/records/kind/mechanism.md)
- Category: [Plugin / Extensibility / IoC](https://banes-lab.com/ontology/principles/architecture-category-plugin-extensibility-ioc.md)
- Severity: [contextual](https://banes-lab.com/records/vocabulary/severity-contextual.md)
- Scope: application, release, runtime
- Layer: [Extensibility Core](https://banes-lab.com/records/layer/extensibility-core.md)

Details

Definition
A mechanism that switches a code path on or off at runtime from externally held flag state, so a release can happen without a deploy.

Requires
[Externalized Flag State](https://banes-lab.com/records/lexicon/externalized-flag-state.md)

Reinforces
[Continuous Delivery](https://banes-lab.com/records/lexicon/continuous-delivery.md), [Runtime Extensibility](https://banes-lab.com/records/architecture/runtime-extensibility.md)

Enables
[Decoupled Deploy and Release](https://banes-lab.com/records/lexicon/decoupled-deploy-and-release.md), [Gradual Rollout](https://banes-lab.com/records/lexicon/gradual-rollout.md)

In tension with
[Flag Debt](https://banes-lab.com/records/lexicon/flag-debt.md)

Conflicts with
[Hardcoded Branch Constant](https://banes-lab.com/records/lexicon/hardcoded-branch-constant.md)

Tensions
[Feature Toggle / Flag Debt](https://banes-lab.com/records/tension/feature-toggle-flag-debt.md)

Violated by
release paths gated by a hardcoded boolean constant

Detected by
compile-time flags requiring redeploy to flip

Measured by
redeploys per behavior change

Refactored by
Introduce Runtime Feature Flags

Enforced by
release review

Before

```typescript
if (NEW_FOO_FLOW_ENABLED) runNewFooFlow();
else runOldFooFlow();
```

After

```typescript
if (featureFlags.enabled("new-foo-flow", { user, percentage: 10 })) runNewFooFlow();
else runOldFooFlow();
```

How it is checked

Checked by
release review

Population
Every extension point, plugin, registration and dependency construction site

Freshness
A verdict stands until a plugin, a registration or the composition root changes

Refusal
The dependency rule, plugin contract test or banned-API rule fails a change that hardcodes an extension or hides a dependency

Observation
Imports from the core to plugins, construction sites and registry contents, read from source and at startup

Evidence
None, because the catalog states this check as a class, so a watched run belongs to each system that adopts it

Authoritative side
The extension contract, which every plugin conforms to while the core names no plugin

Depends on
[Externalized Flag State](https://banes-lab.com/records/lexicon/externalized-flag-state.md), [Continuous Delivery](https://banes-lab.com/records/lexicon/continuous-delivery.md), [Runtime Extensibility](https://banes-lab.com/records/architecture/runtime-extensibility.md), [Decoupled Deploy and Release](https://banes-lab.com/records/lexicon/decoupled-deploy-and-release.md), [Gradual Rollout](https://banes-lab.com/records/lexicon/gradual-rollout.md)

Shape it refuses
[Hardcoded Branch Constant](https://banes-lab.com/records/lexicon/hardcoded-branch-constant.md)

## Links to

- [style](https://banes-lab.com/records/kind/style.md)
- [contextual](https://banes-lab.com/records/vocabulary/severity-contextual.md)
- [Extensibility Core](https://banes-lab.com/records/layer/extensibility-core.md)
- [Extension Points](https://banes-lab.com/records/architecture/extension-points.md)
- [Stable Interfaces](https://banes-lab.com/records/architecture/stable-interfaces.md)
- [Discovery](https://banes-lab.com/records/lexicon/discovery.md)
- [Open/Closed Principle](https://banes-lab.com/records/architecture/open-closed.md)
- [Modularity](https://banes-lab.com/records/architecture/modularity.md)
- [Runtime Extensibility](https://banes-lab.com/records/architecture/runtime-extensibility.md)
- [Static Analysis](https://banes-lab.com/records/architecture/static-analysis.md)
- [Security](https://banes-lab.com/records/lexicon/security.md)
- [Hardcoded Extensions](https://banes-lab.com/records/lexicon/hardcoded-extensions.md)
- [Runtime Discovery](https://banes-lab.com/records/architecture/runtime-discovery.md)
- [Composability](https://banes-lab.com/records/architecture/composability.md)
- [Replaceability](https://banes-lab.com/records/architecture/replaceability.md)
- [Self-Describing Architecture](https://banes-lab.com/records/architecture/self-describing-architecture.md)
- [Plugin Architecture / Static Analysis](https://banes-lab.com/records/tension/plugin-architecture-static-analysis.md)
- [Plugin Architecture / Security](https://banes-lab.com/records/tension/plugin-architecture-security.md)
- [mechanism](https://banes-lab.com/records/kind/mechanism.md)
- [recommended](https://banes-lab.com/records/vocabulary/severity-recommended.md)
- [Contracts](https://banes-lab.com/records/lexicon/contracts.md)
- [Plugin Architecture](https://banes-lab.com/records/architecture/plugin-architecture.md)
- [Third-Party Extension](https://banes-lab.com/records/lexicon/third-party-extension.md)
- [API Surface Growth](https://banes-lab.com/records/lexicon/api-surface-growth.md)
- [Closed Core](https://banes-lab.com/records/lexicon/closed-core.md)
- [Extension Points / API Surface Growth](https://banes-lab.com/records/tension/api-surface-growth-extension-points.md)
- [principle](https://banes-lab.com/records/kind/principle.md)
- [Abstraction](https://banes-lab.com/records/architecture/abstraction.md)
- [Composition Root](https://banes-lab.com/records/lexicon/composition-root.md)
- [Dependency Inversion Principle](https://banes-lab.com/records/architecture/dependency-inversion.md)
- [Dependency Injection](https://banes-lab.com/records/architecture/dependency-injection.md)
- [Framework Control Flow](https://banes-lab.com/records/lexicon/framework-control-flow.md)
- [Plugins](https://banes-lab.com/records/lexicon/plugins.md)
- [Traceability](https://banes-lab.com/records/architecture/traceability.md)
- [Direct Control Ownership](https://banes-lab.com/records/lexicon/direct-control-ownership.md)
- [Inversion of Control (IoC) / Traceability](https://banes-lab.com/records/tension/inversion-of-control-ioc-traceability.md)
- [pattern](https://banes-lab.com/records/kind/pattern.md)
- [Testability](https://banes-lab.com/records/architecture/testability.md)
- [Mocking](https://banes-lab.com/records/lexicon/mocking.md)
- [Constructor Complexity](https://banes-lab.com/records/lexicon/constructor-complexity.md)
- [Hardcoded Instantiation](https://banes-lab.com/records/lexicon/hardcoded-instantiation.md)
- [Ambient Context](https://banes-lab.com/records/architecture/ambient-context.md)
- [Interface-Based Design](https://banes-lab.com/records/architecture/interface-based-design.md)
- [Singleton Pattern](https://banes-lab.com/records/architecture/singleton-pattern.md)
- [Inversion of Control](https://banes-lab.com/records/architecture/inversion-of-control.md)
- [Dependency Injection / Constructor Complexity](https://banes-lab.com/records/tension/constructor-complexity-dependency-injection.md)
- [Adapter Pattern](https://banes-lab.com/records/architecture/adapter-pattern.md)
- [Ports and Adapters](https://banes-lab.com/records/lexicon/ports-and-adapters.md)
- [Registration Protocol](https://banes-lab.com/records/lexicon/registration-protocol.md)
- [Dynamic Binding](https://banes-lab.com/records/architecture/dynamic-binding.md)
- [Dynamic Resolution](https://banes-lab.com/records/lexicon/dynamic-resolution.md)
- [Registry Availability](https://banes-lab.com/records/lexicon/registry-availability.md)
- [Hardcoded Lookup](https://banes-lab.com/records/lexicon/hardcoded-lookup.md)
- [Service Discovery](https://banes-lab.com/records/architecture/service-discovery.md)
- [Service Registry / Registry Availability](https://banes-lab.com/records/tension/registry-availability-service-registry.md)
- [Health Checks](https://banes-lab.com/records/architecture/health-checks.md)
- [Keyed Registration](https://banes-lab.com/records/lexicon/keyed-registration.md)
- [Factory Pattern](https://banes-lab.com/records/architecture/factory-pattern.md)
- [Dynamic Lookup](https://banes-lab.com/records/lexicon/dynamic-lookup.md)
- [Global State](https://banes-lab.com/records/lexicon/global-state.md)
- [Direct Reference](https://banes-lab.com/records/lexicon/direct-reference.md)
- [Registry Pattern / Global State](https://banes-lab.com/records/tension/global-state-registry-pattern.md)
- [discouraged](https://banes-lab.com/records/vocabulary/severity-discouraged.md)
- [Registry](https://banes-lab.com/records/lexicon/registry.md)
- [Runtime Lookup](https://banes-lab.com/records/lexicon/runtime-lookup.md)
- [Late Resolution](https://banes-lab.com/records/lexicon/late-resolution.md)
- [Explicit Dependencies](https://banes-lab.com/records/lexicon/explicit-dependencies.md)
- [Service Locator Pattern / Testability](https://banes-lab.com/records/tension/service-locator-pattern-testability.md)
- [Service Locator Pattern / Dependency Inversion Principle (DIP)](https://banes-lab.com/records/tension/dependency-inversion-principle-dip-service-locator-pattern.md)
- [Service Locator Pattern / Explicit Dependencies](https://banes-lab.com/records/tension/explicit-dependencies-service-locator-pattern.md)
- [Externalized Flag State](https://banes-lab.com/records/lexicon/externalized-flag-state.md)
- [Continuous Delivery](https://banes-lab.com/records/lexicon/continuous-delivery.md)
- [Decoupled Deploy and Release](https://banes-lab.com/records/lexicon/decoupled-deploy-and-release.md)
- [Gradual Rollout](https://banes-lab.com/records/lexicon/gradual-rollout.md)
- [Flag Debt](https://banes-lab.com/records/lexicon/flag-debt.md)
- [Hardcoded Branch Constant](https://banes-lab.com/records/lexicon/hardcoded-branch-constant.md)
- [Feature Toggle / Flag Debt](https://banes-lab.com/records/tension/feature-toggle-flag-debt.md)

## Linked from

- [The layer topology](https://banes-lab.com/ontology/schema/the-layer-topology.md)
- [The membership](https://banes-lab.com/ontology/schema/the-membership.md)
