Skip to main content

Contribution Guidelines — Bestway Business Central Extensions

Version: 1.0 Effective Date: March 2026 Prepared by: Phil McCaffrey, ERP Specialist Organization: Bestway (USA) Inc.


Purpose

This document defines the standards that all developers — internal and external — must follow when building, modifying, or delivering Business Central extensions for Bestway USA. These standards apply to AL extension code and to any supporting scripts (Python tooling, integration tests) delivered alongside extensions. These standards exist to ensure that every extension in our environment is testable, documented, maintainable, and production-ready before it ships.

Going forward, Bestway will enforce these guidelines as acceptance criteria for all extension deliveries. Work that does not meet these standards will be returned for remediation before UAT can proceed.


1. Extension Configuration

1.1. app.json Requirements

Every extension's app.json must include:

FieldRequiredDetails
nameYesShort, descriptive extension name
publisherYesPublisher organization
versionYesFour-part version (major.minor.patch.build) — must match all documentation
idRangesYesReserved, non-overlapping ID ranges for all objects
dependenciesYesComplete list of extension dependencies with correct app IDs and versions
featuresYesMust include "NoImplicitWith"
applicationInsightsConnectionStringYesBestway's shared Azure Application Insights connection string (provided upon engagement)
resourceExposurePolicyYesMust set allowDebugging, allowDownloadingSource, and includeSourceInSymbolFile to true

1.2. Object ID Ranges

Each extension has a reserved, non-overlapping range of IDs defined in idRanges. All new objects — tables, pages, codeunits, enums, permission sets — must use IDs within the assigned range. Never reuse IDs across extensions. If you are running low on available IDs, request an expanded range before proceeding.

1.3. Telemetry

Every extension must include extension-level telemetry. The Application Insights connection string will be provided by Bestway at the start of the engagement. This sends extension-emitted events to Bestway's centralized monitoring — it is not optional.

1.4. Version Numbering

Use four-part versioning: major.minor.patch.build. The version in app.json must match the version referenced in all documentation (CHANGELOG, CHANGE documents, test plans). Bump the version for every deliverable — there should never be a delivery without a corresponding version increment.


2. Repository Structure

2.1. Folder Organization

Extensions must organize AL objects by type in dedicated subfolders under src/. Use PascalCase, singular names for subfolder names:

ExtensionFolder/
├── docs/
│ ├── CHANGELOG.md
│ └── CHANGE-v{version}.md
├── src/
│ ├── Codeunit/
│ ├── Table/
│ ├── TableExt/
│ ├── Page/
│ ├── PageExt/
│ ├── Enum/
│ ├── EnumExt/
│ ├── PermissionSet/
│ ├── Report/
│ └── ReportExt/
├── app.json
└── README.md

Note: Some legacy extensions in the repository use different conventions (lowercase pluralized folders, flat structures without src/, etc.). The structure above applies to all new extensions and major refactors. When modifying an existing extension, follow the convention already established within that extension for consistency.

2.2. File Naming

Files follow the convention {Prefix}{ObjectName}.{ObjectType}.al. Examples:

  • PhoneIntegration.Codeunit.al
  • NextivaConfig.Table.al
  • NextivaSetupPage.Page.al

Maintain consistent casing and naming within an extension. Do not mix conventions.

Note: Some legacy extensions use alternative naming patterns (e.g., CU-60000.PhoneIntegration.al with object type prefixes and IDs, or POEmailDispatcher.codeunit.al with lowercase types). The convention above applies to all new extensions. When modifying an existing extension, follow the convention already established within that extension.

2.3. Namespace Declarations

Extensions targeting application: 27.0.0.0 or later should include namespace declarations (e.g., namespace BestwayUSA.bccsmapi;). Extensions targeting earlier versions may omit namespaces.


3. Code Quality Standards

3.1. AL Anti-Patterns — Must Fix

The following patterns will be flagged during code review and must be corrected before acceptance:

Anti-PatternWhy It Matters
Rec.GET(...) without checking the return valueSilent failures cause downstream runtime errors
Business logic in page triggersUntestable; belongs in codeunits
MODIFY or INSERT inside OnValidate triggersCauses unexpected side effects and breaks transactions. Legacy code may contain this pattern; new deliveries must not introduce it
Hardcoded user-facing text stringsBreaks translation support; must use labels
FINDSET / FINDFIRST without SETRANGE on multi-record tablesFull table scans cause performance degradation and lock contention. Single-record setup tables accessed via Get() or unfiltered FindFirst() are exempt
Hardcoded record IDs, enum values, or magic numbersUnreadable and fragile; use named constants or enums
COMMIT calls without documented justificationBreaks transactional integrity; use only after completing a logical unit of work. When Commit() is used to break long-running transactions (see Section 4.2), document the reason and partial-completion behavior in a code comment
REPEAT loops without proper exit conditionsRisk of infinite loops on record sets

3.2. AL Anti-Patterns — Should Fix

These patterns are flagged during review and should be corrected unless there is a documented justification:

Anti-PatternWhy It Matters
CALCFIELDS without prior filteringTriggers SQL aggregates on unfiltered data — wasteful
Using RecordRef / FieldRef when typed access is availableSlower and less readable than direct field references
Overly complex triggers (20+ lines)Hard to test, debug, and maintain — delegate to codeunits
Event subscribers without filteringFire on every record regardless of context — expensive at scale
Not using temporary records for read-only operationsRisk of unintended database writes

3.3. Architecture Expectations

  • Business logic belongs in codeunits, not page or report triggers. This is not a suggestion — it is a requirement for testability. Page triggers should call codeunit procedures, not implement logic directly.
  • Avoid deep nesting. More than 3–4 levels of indentation signals logic that should be refactored with early returns or extracted into helper procedures.
  • No dead code. Remove unreachable branches, unused variables, and commented-out blocks before delivery.
  • No hardcoded credentials or environment-specific values in source code. Configuration belongs in setup tables or environment variables.

4. Performance Standards

Performance is reviewed as part of the acceptance process. The following areas are evaluated for every delivery:

4.1. Database Access

  • SetLoadFields correctnessSetLoadFields is "sticky" on a record variable. It must only be applied to variables used for read-only queries. If the same variable is later used for Insert, Modify, or Get, partial field loading can silently corrupt data. Use separate variables for lookup and write operations.
  • FindFirst vs. FindSet — Use FindFirst when you need one record (existence checks, lookups). Use FindSet when iterating with repeat...until Next() = 0. Using FindSet for a single record, or FindFirst inside a loop, are both anti-patterns.
  • Redundant database calls — Duplicate FindFirst(), Get(), or FindSet() calls on the same variable with unchanged filters will be flagged. Each call is a database round-trip.

4.2. Locking and Concurrency

  • LockTable() scope — Table-level locks on high-traffic tables (Customer, Item, Sales Header) are treated as critical risks and must be justified. Evaluate whether record-level locking is sufficient.
  • Transaction duration — Long-running transactions that include HTTP calls, blob uploads, or other I/O hold database locks for their entire duration. Look for opportunities to break work into micro-transactions with Commit() per iteration. When using Commit() to break transactions, document the reason and the partial-completion behavior so reviewers can evaluate the tradeoff (partial batches persist if interrupted, but releasing locks prevents blocking other users).
  • Lock contention — If an API page and a Job Queue codeunit both operate on the same tables, neither should hold locks that block the other during normal operation.

4.3. Index and Key Usage

  • Secondary keys must match filter patterns — When SetRange or SetFilter targets non-primary-key fields, a secondary key covering those fields should exist. Missing indexes cause full table scans that degrade as record counts grow.
  • Index write overhead — Every secondary key adds overhead to Insert, Modify, and Delete. The access pattern must justify the index.

5. Documentation Requirements

5.1. README.md

Every extension must include a README.md in its root folder. This serves both developers and non-technical stakeholders.

Required sections:

  1. Overview — What does the extension do and why does Bestway need it?
  2. Features — Key capabilities
  3. Prerequisites — What must be set up before installation?
  4. Installation and Configuration — Step-by-step setup, including any setup tables or pages
  5. Usage — How do end users interact with the extension?
  6. Dependencies — All extension dependencies
  7. Known Limitations — Edge cases, unsupported scenarios, or accepted workarounds

Writing standard: Assume readers may not be AL developers. Explain concepts without unnecessary jargon. Use numbered steps for procedures. Include screenshots for UI changes where helpful.

5.2. Change Documentation (Two-Tier)

Every extension maintains two tiers of change documentation in docs/:

Tier 1 — docs/CHANGELOG.md (summary index)

  • One entry per released version, newest at top
  • Version numbers match app.json exactly
  • Dates in ISO 8601 format (YYYY-MM-DD)
  • Changes categorized as: Added, Changed, Fixed, Removed, Deprecated, Migration Notes
  • Each entry links to the detailed CHANGE document

Tier 2 — docs/CHANGE-v{version}.md (detailed narrative)

Each version gets a detailed change document covering:

  1. Background — Context and rationale for the changes
  2. Summary of Changes — Table of all items with severity, category, and description
  3. Detailed Changes — Per-item sections with file references, before/after behavior, and reasoning
  4. Object Inventory — All objects in the extension with IDs, types, and status (new/modified/deleted)
  5. Deployment Notes — Pre/post-deployment steps, verification procedures, and warnings
  6. Known Limitations — Accepted edge cases with justification
  7. Testing — Reference to the UAT test plan and coverage status

Rule: Every time the version in app.json is bumped, both CHANGELOG.md and the corresponding CHANGE-v{version}.md must be created or updated. No exceptions.

Reference example: See Cambay Solutions_BC Dialing Application/docs/CHANGE-v2.3.5.0.md and Cambay Solutions_BC Dialing Application/docs/CHANGELOG.md in the repository for a working example of the two-tier format.

5.3. Self-Documenting Code

  • Docstrings and type hints on all public procedures
  • Comments only where logic is not self-evident — do not comment obvious code
  • No tribal knowledge: a developer unfamiliar with the extension should be able to understand the codebase from the source and documentation alone

6. Testing Requirements

6.1. UAT Test Plans

Every extension must have a UAT test plan delivered as a Word document (.docx). The test plan must cover all user-facing behavior and be updated whenever the extension is modified.

Required content:

  • Overview — What the extension does and what this plan validates
  • Prerequisites — Conditions that must be true before testing begins
  • Test Areas — Organized by feature, each containing test cases with:
    • Test case ID (e.g., TC-1.1)
    • Title
    • Steps (numbered: action → expected result)
  • Sign-off section — Signature lines for testing and approval

Rule: Test plans are living documents. When the extension changes, the test plan must be updated to cover the new or modified functionality before delivery.

For vendors: Bestway will provide a .docx template upon engagement. Vendors may submit test plan content in markdown or another structured format; Bestway will convert to the standard template if needed. The key requirement is the content — complete test cases with IDs, steps, and expected results — not the formatting.

6.2. Automated Tests

Extensions with business logic must include AL test codeunits.

Requirements:

  • Test codeunits use the [Test] attribute
  • Each test follows a Given / When / Then structure
  • Tests validate expected behavior and edge cases
  • All tests pass in the BC Test Tool (page 130401) before delivery

What to test:

  • Business logic in helper codeunits
  • Validation logic and error conditions
  • Integration points (event subscribers, API endpoints)
  • Boundary cases (empty values, maximum lengths, concurrent access)

6.3. Test Coverage Expectation

Every user-facing behavior introduced or modified must have at least one test case — either in the UAT test plan, in an automated test codeunit, or both. Untested functionality will be flagged during review.


7. Delivery Expectations

7.1. Source Code

Source code must be delivered and committed to the Bestway repository. This is a standard deliverable, not contingent on any other milestone. Bestway requires source code access to verify implementations, troubleshoot production issues, and maintain extensions after the engagement ends.

Compiled .app packages alone do not constitute a complete delivery.

7.2. What a Complete Delivery Includes

DeliverableDescription
Source codeAll .al files committed to the repository
Compiled packageThe .app file for deployment
README.mdExtension documentation per Section 5.1
CHANGELOG.mdSummary changelog per Section 5.2
CHANGE-v{version}.mdDetailed change document per Section 5.2
UAT test planWord document per Section 6.1
Test codeunitsAutomated tests per Section 6.2 (if business logic exists)
app.jsonCorrectly configured per Section 1.1, with telemetry and version bumped

7.3. Pre-UAT Gate

UAT will not proceed until all items in Section 7.2 are delivered and the Shipment Readiness Checklist (below) is satisfied. Incomplete deliveries will be returned with a specific list of gaps to address.

7.4. Deployment Communication

Every deployment — to sandbox or production — must be accompanied by a notification to Bestway stakeholders that includes:

  • Extension name and version number
  • Summary of changes (reference the CHANGELOG)
  • Any known limitations or breaking changes
  • Verification steps for the target environment

8. Shipment Readiness Checklist

This checklist must be completed before any extension is considered ready to ship. Bestway will verify each item during the acceptance review.

Shipment Readiness Flow

Documentation

  • README.md exists and is current
  • docs/CHANGELOG.md has a new entry for this version
  • docs/CHANGE-v{version}.md exists with full detail
  • Code is self-documenting — docstrings on public procedures, no tribal knowledge

Test Plans

  • UAT test plan .docx exists and covers this version's changes
  • Every user-facing behavior has at least one test case with clear expected results

Testability

  • Business logic is in codeunits, not page/report triggers
  • Test codeunits exist for extensions with business logic
  • All tests pass in the BC Test Tool (page 130401)

Extension Configuration

  • applicationInsightsConnectionString present in app.json
  • resourceExposurePolicy present with debugging and source access enabled
  • app.json version bumped and matches all documentation
  • All object IDs are within the extension's reserved range
  • Dependencies are correct and complete
  • NoImplicitWith enabled in features
  • Permission sets defined for all tables, codeunits, and pages in the extension — users must be able to operate the extension with the delivered permission set alone (no SUPER required)

Naming and Structure

  • Files follow {Prefix}{ObjectName}.{ObjectType}.al convention
  • Objects organized in type-based folders (Codeunit/, Table/, Page/, etc.)

Delivery

  • Source code committed to the repository
  • Compiled .app package provided
  • No hardcoded credentials or environment-specific values in source

9. Review Process

Bestway conducts a multi-stage review of all delivered extensions:

  1. Shipment Readiness Verification — Every item in Section 8 is checked. Missing items are returned as a gap list.
  2. Code Review — Pattern compliance, dependency safety, edge case handling, security, and over-engineering are evaluated against the standards in this document.
  3. Performance Review — Database access patterns, locking behavior, index usage, and query optimization are evaluated per Section 4. Findings are classified as Fix (must correct), Fix if (conditional), or Accept (acknowledged, no action).
  4. Compilation and Test Verification — The extension must compile without errors and all test codeunits must pass. This is objective ground truth.
  5. Business Logic Review — Bestway stakeholders verify that the implementation satisfies the business requirement as specified in the Solution Design or requirements document.

Extensions that do not pass all five stages will be returned with a detailed remediation list. Bestway is committed to providing clear, specific feedback — not vague rejections — so that gaps can be addressed efficiently.


Appendix A: Anti-Pattern Quick Reference

Always Fix

PatternLanguageRisk
GET without error handlingALRuntime errors
Business logic in page triggersALUntestable code
MODIFY/INSERT in OnValidateALTransaction side effects
Hardcoded text stringsALBreaks translation
FINDSET without SETRANGE (multi-record tables)ALFull table scans
Hardcoded credentialsAL, PythonSecurity vulnerability
Bare except:PythonSwallowed errors
Raw string SQLPythonSQL injection

Should Fix

PatternLanguageRisk
COMMIT without documented justificationALBroken transactions
Complex triggers (20+ lines)ALMaintainability
RecordRef when typed access existsALReadability, performance
Mutable default argumentsPythonShared state mutation
Wildcard importsPythonNamespace pollution
Copy-paste codeAnyMaintenance burden
Deep nesting (4+ levels)AnyReadability
Dead codeAnyConfusion, code rot

Appendix B: Reference Documents

URLs current as of March 2026. Microsoft periodically reorganizes documentation; if a link is broken, search the document title on Microsoft Learn.

DocumentLocation / URL
This document (markdown source)docs/CONTRIBUTING.md (repo root)
UAT test plan library and usagetools/README.md (repo root)
Keep a Changelog conventionhttps://keepachangelog.com/en/1.1.0/
Microsoft AL Language documentationhttps://learn.microsoft.com/en-us/dynamics365/business-central/dev-itpro/developer/
BC Performance best practiceshttps://learn.microsoft.com/en-us/dynamics365/business-central/dev-itpro/performance/performance-developer
AL coding guidelines for extensionshttps://learn.microsoft.com/en-us/dynamics365/business-central/dev-itpro/compliance/apptest-bestpracticesforalcode
Azure Application Insights for BChttps://learn.microsoft.com/en-us/dynamics365/business-central/dev-itpro/administration/telemetry-overview