Refine self-improvement review guidance

Narrow IsHandled, label-scope, UI-handler, checkpoint, and bulk-operation guidance to evidence-backed false-positive boundaries.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
wenjiefan 2026-08-18 11:17:14 +02:00
parent 841b4e7cab
commit 5f1cff2fb6
21 changed files with 99 additions and 155 deletions

View file

@ -6,14 +6,12 @@ codeunit 50241 "IsHandled Init Bad Sample"
DiscountPct: Decimal;
IsHandled: Boolean;
begin
// IsHandled is never initialized before the first raise, so flow depends
// on the variable's default rather than an explicit, documented intent.
OnBeforeApplyHeaderDiscount(SalesHeader, DiscountPct, IsHandled);
if not IsHandled then
DiscountPct := 5;
// Bug: IsHandled is not reset. If the first subscriber set it true, the
// payment-discount default below is silently skipped too.
// Bug: execution continues when the first event set IsHandled to true,
// and that stale value is passed to a different publisher.
OnBeforeApplyPaymentDiscount(SalesHeader, DiscountPct, IsHandled);
if not IsHandled then
DiscountPct += 2;

View file

@ -6,17 +6,17 @@ codeunit 50240 "IsHandled Init Good Sample"
DiscountPct: Decimal;
IsHandled: Boolean;
begin
IsHandled := false;
// A freshly declared local Boolean is false.
OnBeforeApplyHeaderDiscount(SalesHeader, DiscountPct, IsHandled);
if not IsHandled then
DiscountPct := 5;
if IsHandled then
exit;
DiscountPct := 5;
// Reset before reusing the same variable for the next event so a
// subscriber that handled the first raise can't suppress this one.
IsHandled := false;
// Reaching this point proves that IsHandled is still false.
OnBeforeApplyPaymentDiscount(SalesHeader, DiscountPct, IsHandled);
if not IsHandled then
DiscountPct += 2;
if IsHandled then
exit;
DiscountPct += 2;
end;
[IntegrationEvent(false, false)]

View file

@ -7,20 +7,20 @@ countries: [w1]
application-area: [all]
---
# Initialize IsHandled to false before publishing
# Reset IsHandled before publishing only when its value can carry over
## Description
A routine that raises an `OnBefore…` integration event with a `var IsHandled: Boolean` parameter passes that variable in by reference, so its incoming value decides whether the default logic is skipped. A freshly declared Boolean starts as `false`, but the same variable is frequently reused to raise several events in one routine, and after the first raise it may already be `true`. Assigning `IsHandled := false;` on the line immediately before every raise makes the control flow deterministic and self-documenting, and prevents a stale `true` from silently suppressing logic the author never meant to make skippable. Generated code often reuses one `IsHandled` across several raises without resetting it.
A routine that raises an `OnBefore…` integration event with a `var IsHandled: Boolean` parameter passes that variable by reference, so a pre-existing `true` can affect the following control flow. AL [automatically initializes Boolean variables to `false`](https://learn.microsoft.com/en-us/dynamics365/business-central/dev-itpro/developer/devenv-al-variables#initialization), so a freshly declared local Boolean passed to one event is already deterministic. The same is true when control flow proves the variable is `false`; for example, reaching a second raise after `if IsHandled then exit;` proves that the first raise did not leave it `true`.
## Best Practice
Set `IsHandled := false;` immediately before each `OnBeforeX(…, IsHandled)` raise, then guard the default logic with `if IsHandled then exit;` or `if not IsHandled then …`. Do this even when the variable was just declared: the explicit reset documents intent and stays correct if a second event raise is added to the routine later. This applies only to events that carry a `var IsHandled: Boolean`; an `OnBefore` event with no `IsHandled` parameter needs no reset.
Reset `IsHandled := false;` before a raise only when the value might otherwise carry over as `true`: the same variable is reused after an earlier raise without a control-flow proof that it is false, the value comes from an input parameter, field, or global, or earlier code seeds it. A reset on a guaranteed-false fresh local can be retained for readability, but its absence is not a correctness finding.
See sample: `initialize-ishandled-to-false-before-publishing.good.al`.
## Anti Pattern
Raising `OnBeforeX(…, IsHandled)` with a variable whose value carries over from an earlier raise, so a subscriber that handled the first event unintentionally suppresses the second routine's default logic. Detection: an `IsHandled` variable passed to more than one event in a routine without an intervening `IsHandled := false;`, or any `OnBefore…` raise that passes an `IsHandled` variable without an intervening `IsHandled := false;`.
Raising `OnBeforeX(…, IsHandled)` when the variable can still be `true` from an earlier raise or another source, so the new publisher call starts with stale state. Do not match a single raise using a fresh local Boolean, or a later raise reached only after `if IsHandled then exit;`.
See sample: `initialize-ishandled-to-false-before-publishing.bad.al`.