Improve partner onboarding and documentation navigation

Lead with a complete plugin quick start and add task-oriented usage, troubleshooting, customization, and contribution guides. Preserve the broader plugin framing, correct conflicting contract guidance, support Agents folder reviews, and align repository validation. Convert existing sample references to clickable links without changing knowledge rules.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
Jesper Schulz-Wedde 2026-09-09 17:25:29 +02:00
parent a21edfec46
commit b6da405376
276 changed files with 1287 additions and 756 deletions

View file

@ -17,10 +17,10 @@ An interface variable can hold any codeunit that `implements` the interface, ass
Declare the dependency as an `Interface` variable on the consumer and supply the implementation from outside — typically setter injection through a procedure that takes an `Interface` parameter, or a parameter on the entry method. Production passes the real implementation codeunit; a test passes a test-double codeunit that implements the same interface with deterministic behaviour. Because a codeunit assigns to an interface variable directly, no enum or factory is needed for the injectable case. The consumer's logic is then verifiable in isolation.
See sample: `assign-codeunit-to-interface-for-testability.good.al`.
See sample: [`assign-codeunit-to-interface-for-testability.good.al`](assign-codeunit-to-interface-for-testability.good.al).
## Anti Pattern
A consumer that declares its dependency as a concrete `Codeunit "..."` variable and calls it directly. The collaborator cannot be substituted, so a unit test either runs the production side effects or cannot cover the consumer at all. Detection signal: a `var` of type `Codeunit "<concrete impl>"` used for a collaborator that has — or could have — an interface, especially one that performs I/O, posting, or external calls. Extract an interface, depend on the interface variable, and inject the implementation.
See sample: `assign-codeunit-to-interface-for-testability.bad.al`.
See sample: [`assign-codeunit-to-interface-for-testability.bad.al`](assign-codeunit-to-interface-for-testability.bad.al).

View file

@ -17,10 +17,10 @@ Adding a method to a shipped interface changes the contract every implementing c
On BC25 or later, declare a new interface that `extends` the published interface and add the new method there. Existing implementers remain valid for the original contract, while new implementers opt in to the extended contract. For targets BC16 through BC24, where interface inheritance is unavailable, publish a new or versioned sibling interface instead.
See sample: `extend-published-interfaces-dont-edit-them.good.al`.
See sample: [`extend-published-interfaces-dont-edit-them.good.al`](extend-published-interfaces-dont-edit-them.good.al).
## Anti Pattern
Adding a procedure directly to an interface that has already shipped. Every dependent implementation must immediately add that procedure, so an otherwise compatible app update breaks its implementers.
See sample: `extend-published-interfaces-dont-edit-them.bad.al`.
See sample: [`extend-published-interfaces-dont-edit-them.bad.al`](extend-published-interfaces-dont-edit-them.bad.al).

View file

@ -17,10 +17,10 @@ An enum ordinal can remain in persisted data after the enum extension that decla
On BC18 or later, set `UnknownValueImplementation = <Interface> = <Codeunit>;` on an enum that implements an interface and can be persisted. Use an implementation that reports a clear domain error or safely contains the unknown state. Keep `DefaultImplementation` separately when declared but unmapped values also need a fallback.
See sample: `handle-unknown-enum-ordinals-with-unknownvalueimplementation.good.al`.
See sample: [`handle-unknown-enum-ordinals-with-unknownvalueimplementation.good.al`](handle-unknown-enum-ordinals-with-unknownvalueimplementation.good.al).
## Anti Pattern
Defining only `DefaultImplementation` and assuming it also handles a stored ordinal whose enum value has disappeared. After an enum extension is uninstalled, converting that unknown ordinal to the interface can produce a technical runtime error instead of controlled handling.
See sample: `handle-unknown-enum-ordinals-with-unknownvalueimplementation.bad.al`.
See sample: [`handle-unknown-enum-ordinals-with-unknownvalueimplementation.bad.al`](handle-unknown-enum-ordinals-with-unknownvalueimplementation.bad.al).

View file

@ -17,10 +17,10 @@ When behaviour varies by a discrete "type" — a shipping method, a posting stra
Declare an `interface` with the method signatures only (no bodies). Define an `enum` that `implements` the interface and set `Implementation = <Interface> = <Codeunit>;` on each value, pointing at a codeunit that `implements` the same interface. In the consumer, declare a variable of the interface type, assign the enum value to it, and call the method — the platform dispatches to the codeunit mapped to that value. New variants plug in by adding an enum value and its implementation; existing call sites are untouched. The open/closed boundary lives at the enum, not scattered across `case` blocks.
See sample: `prefer-interface-over-case-branching.good.al`.
See sample: [`prefer-interface-over-case-branching.good.al`](prefer-interface-over-case-branching.good.al).
## Anti Pattern
A `case "Shipping Method" of` block that selects behaviour inline, duplicated across the call sites that need it. Each new method forces a synchronized edit to every block, and a missed branch is a silent gap. Detection signal: a `case` statement over an enum value whose branches choose between variant computations or strategies, especially when the same shape appears in more than one procedure. Replace the enum with one that `implements` an interface, move each branch body into an implementation codeunit, and let dispatch happen through an interface variable.
See sample: `prefer-interface-over-case-branching.bad.al`.
See sample: [`prefer-interface-over-case-branching.bad.al`](prefer-interface-over-case-branching.bad.al).

View file

@ -17,10 +17,10 @@ An `enum` that `implements` an interface maps each declared value to a codeunit
On any extensible enum that implements an interface, set `DefaultImplementation = <Interface> = <Codeunit>;` at the enum level, pointing at a safe implementation. Values with their own `Implementation` keep using it; declared values without one resolve to the default. Do not rely on this property for persisted ordinals that match no declared enum value.
See sample: `set-defaultimplementation-on-enum.good.al`.
See sample: [`set-defaultimplementation-on-enum.good.al`](set-defaultimplementation-on-enum.good.al).
## Anti Pattern
An extensible `enum ... implements <Interface>` where at least one value sets no `Implementation` and the enum declares no `DefaultImplementation`. Code that assigns that value to an interface variable and invokes a method throws at the call site, and because the enum is extensible the failing value can be introduced by a third party long after the consumer ships. Detection signal: an enum that implements an interface, has a `value(...)` with no `Implementation`, and no enum-level `DefaultImplementation`. Add a `DefaultImplementation` mapping to close the gap.
See sample: `set-defaultimplementation-on-enum.bad.al`.
See sample: [`set-defaultimplementation-on-enum.bad.al`](set-defaultimplementation-on-enum.bad.al).