Rules Index¶
Referenceable rules across this guideline. Each rule has a short, stable ID —
DGT-<AREA>-<NUMBER> — assigned ascending per area and never reused: if a rule is removed,
its ID is retired, not recycled, so references from project documentation stay unambiguous. See
Rule Notation for the scheme.
Cite a rule by its ID (e.g. "deviates from DGT-SRV-060"); the links below jump straight to
the rule where it is stated. Each row also has its own anchor
(e.g. reference/rules.md#dgt-srv-060), so you can point a colleague at one specific rule
directly in this list — they land on it and see the surrounding rules for context.
Foundation (FND)¶
| Rule | Statement | Defined in |
|---|---|---|
DGT-FND-010 |
If the customer has a defined standard, that standard takes precedence over this guideline. | Scope & Principles |
DGT-FND-020 |
Deviations from this guideline are permitted but must be documented and justified in the project's architectural documentation. | Scope & Principles |
DGT-FND-030 |
Every project maintains an ADR log in its main repository (docs/decisions/, one Markdown file per decision). |
Architecture Decision Records |
DGT-FND-040 |
Fundamental decisions for or against a guideline rule require an ADR in MADR format. | Architecture Decision Records |
DGT-FND-050 |
A deviation ADR references the affected rule ID machine-readably; the ADR lifecycle is maintained. | Architecture Decision Records |
DGT-FND-060 |
ADRs pass through PR review and are part of the Definition of Done's documentation item. | Architecture Decision Records |
Architecture (ARC)¶
| Rule | Statement | Defined in |
|---|---|---|
DGT-ARC-010 |
Organize Dataverse solutions by component type (core/server/client/apps/flows), with a strict, documented dependency direction. | Solution Architecture |
DGT-ARC-020 |
A component lives in exactly one solution; tables are added segmented, never "Add all assets". | Solution Architecture |
DGT-ARC-030 |
Development environments hold the unmanaged solution; every other environment always receives it managed. | Solution Architecture |
DGT-ARC-040 |
Use the DIGITALL publisher/dgt_ prefix for productized components; a customer-specific publisher/prefix for customer projects. |
Publisher & Prefix |
DGT-ARC-050 |
Once a prefix is chosen for a project, it does not change. | Publisher & Prefix |
DGT-ARC-060 |
Follow Microsoft's customization guidance by default; deviate only with a documented, concrete reason. | Publisher & Prefix |
DGT-ARC-070 |
Prefer an XrmToolBox plugin over installing a special-purpose third-party solution. | Publisher & Prefix |
DGT-ARC-080 |
Four target environments (dev → test → UAT → prod) is the common baseline; three is the minimum. | Environment Strategy |
DGT-ARC-090 |
Enable Managed Environments on every Power Platform Pipelines target environment from initial setup. | Environment Strategy |
DGT-ARC-100 |
One development environment per developer once the team is larger than one or two people. | Environment Strategy |
DGT-ARC-110 |
Decide the deployment approach (Pipelines / Azure DevOps / GitHub Actions / hybrid) once, early in the project. | Deployment Approach |
Customizing (CUS)¶
| Rule | Statement | Defined in |
|---|---|---|
DGT-CUS-010 |
All schema names are snake_case, lowercase, [publisher]_[name][_type-suffix]. |
Naming Conventions |
DGT-CUS-020 |
Set IsCustomizable = false for security roles and rollup/calculated fields. |
Naming Conventions |
DGT-CUS-030 |
Check whether a standard table already fits before adding a custom one. | Tables, Columns & Relationships |
DGT-CUS-040 |
Decide ownership, table type, and primary column deliberately — they are fixed at table creation. | Tables, Columns & Relationships |
DGT-CUS-050 |
High-volume telemetry-style data goes into an elastic table with a TTL column, never a standard table. | Tables, Columns & Relationships |
DGT-CUS-060 |
Pick the narrowest correct column data type. | Tables, Columns & Relationships |
DGT-CUS-070 |
Use global choices instead of local ones for any value list used on more than one column. | Tables, Columns & Relationships |
DGT-CUS-080 |
Don't store what you can derive — prefer rollup/calculated/formula columns over a plugin-written plain column. | Tables, Columns & Relationships |
DGT-CUS-090 |
Define an alternate key for any column an external system uses to address a record. | Tables, Columns & Relationships |
DGT-CUS-100 |
Default relationships to referential cascade; use parental only where the child has no meaning without the parent. | Tables, Columns & Relationships |
DGT-CUS-110 |
One main form per persona, not one form with everything hidden per role. | Forms, Views & BPF |
DGT-CUS-120 |
Logic belongs in code once non-trivial; register only OnLoad and wire the rest in TypeScript. |
Forms, Views & BPF |
DGT-CUS-130 |
Keep view columns lean — every column is a query column. | Forms, Views & BPF |
DGT-CUS-140 |
A BPF models a cross-table process as few, meaningful ordered stages. | Forms, Views & BPF |
DGT-CUS-150 |
Build for the Unified Interface with the modern designers; classic-only features are off limits. | Forms, Views & BPF |
DGT-CUS-160 |
Build commands in the modern Command Bar Designer, not by hand-editing ribbon XML. | Command Bar |
DGT-CUS-170 |
Don't hide or override standard command-bar buttons globally without a specific, documented reason. | Command Bar |
DGT-CUS-180 |
Brand per app using the modern model-driven app theme, kept inside the solution. | Themes & Icons |
DGT-CUS-190 |
Use SVG web resources for table and command-bar icons. | Themes & Icons |
DGT-CUS-200 |
Assign unattended/production flow connections via an application user / service principal. | Env Variables & Connection References |
DGT-CUS-210 |
In development, create the environment variable definition only (no value) and deploy that. | Env Variables & Connection References |
DGT-CUS-220 |
Apply least privilege — grant only what a persona needs, at the smallest working ownership depth. | Security Model |
DGT-CUS-230 |
Don't edit out-of-the-box security roles — copy and adjust. | Security Model |
DGT-CUS-240 |
No standing System Administrator for application/service accounts. | Security Model |
DGT-CUS-250 |
Test with a real security role, not as System Administrator. | Security Model |
DGT-CUS-260 |
Never implement security through UI visibility — use column security and privileges. | Security Model |
DGT-CUS-270 |
Security roles are solution components and get deployed — never rebuilt manually in test/prod. | Security Model |
Server-side (SRV)¶
| Rule | Statement | Defined in |
|---|---|---|
DGT-SRV-010 |
Plugin and Custom API assemblies target net462. |
Project Setup |
DGT-SRV-020 |
Use the SDK-style project format, not the legacy non-SDK .csproj. |
Project Setup |
DGT-SRV-030 |
Reference Microsoft.CrmSdk.CoreAssemblies with PrivateAssets="all" — not Dataverse.Client. |
Project Setup |
DGT-SRV-040 |
One plugin/Custom API/workflow group per assembly, versioned independently. | Project Setup |
DGT-SRV-050 |
Plugin packages are not signed (exceptions: classic single-assembly, managed identity). | Project Setup |
DGT-SRV-060 |
Plugin/Custom API projects use the plugin package (dependent-assembly .nupkg) format; ILMerge is not allowed. |
Plugin Packages |
DGT-SRV-070 |
A plugin package/assembly belongs to exactly one solution. | Plugin Packages |
DGT-SRV-080 |
Inherit Executor and return a meaningful ExecutionResult (Ok/Failure/Skipped), not always Ok. |
DIGITALL Assembly |
DGT-SRV-090 |
Prefer SecuredOrganizationService/ElevatedOrganizationService over calling OrganizationService(...) directly. |
DIGITALL Assembly |
DGT-SRV-100 |
Register steps declaratively via [PluginRegistration] — code is the source of truth. |
Registration Attributes |
DGT-SRV-110 |
Use generated early-bound models (dgtp codegeneration); generated code is not committed. |
Early-Bound Models |
DGT-SRV-120 |
Prefer Custom APIs over classic (un)bound Actions for new server-side operations. | Custom API & Data Providers |
DGT-SRV-130 |
Keep plugins stateless — no mutable static state. |
Patterns & Pitfalls |
DGT-SRV-140 |
No ExecuteMultipleRequest/ExecuteTransactionRequest inside plugins. |
Patterns & Pitfalls |
DGT-SRV-150 |
No custom threading (Parallel.*, Task.Run) inside plugins. |
Patterns & Pitfalls |
DGT-SRV-160 |
Synchronous steps on Retrieve/RetrieveMultiple only in justified exceptions. |
Patterns & Pitfalls |
DGT-SRV-170 |
Don't use context.Depth as loop control — use filtering attributes and precise registration. |
Patterns & Pitfalls |
DGT-SRV-180 |
Every query states an explicit ColumnSet — never AllColumns. |
Patterns & Pitfalls |
DGT-SRV-190 |
Use InvalidPluginExecutionException (Status = Succeeded) only for user-facing validation messages. |
Patterns & Pitfalls |
DGT-SRV-200 |
Use FilterAttributes on the registration rather than re-checking inside Execute(). |
Patterns & Pitfalls |
DGT-SRV-210 |
Keep SuppressNullableSupport: true consistent across every project sharing a generated model. |
Patterns & Pitfalls |
Client-side (CLI)¶
| Rule | Statement | Defined in |
|---|---|---|
DGT-CLI-010 |
Form and command-bar scripts are written in TypeScript and bundled with webpack (the webresources-template). |
TypeScript Web Resources |
DGT-CLI-020 |
Only *.form.ts / *.ribbon.ts become bundles; shared code lives in plain imported modules. |
TypeScript Web Resources |
DGT-CLI-030 |
Register only the form's OnLoad event in the designer; wire all other handlers in code. |
TypeScript Web Resources |
DGT-CLI-040 |
Generate typed models with dgtp codegeneration and type the form context against them. |
TypeScript Web Resources |
DGT-CLI-050 |
No deprecated client APIs: no Xrm.Page, no parent.Xrm, no platform scripts, no OData v2. |
TypeScript Web Resources |
DGT-CLI-060 |
Biome owns formatting and linting for web resources; don't hand-tune style. | TypeScript Web Resources |
DGT-CLI-070 |
Use pac for PCF scaffolding rather than copying an older project's structure by hand. |
PCF Controls |
Cloud & Integration (INT)¶
| Rule | Statement | Defined in |
|---|---|---|
DGT-INT-010 |
Don't do slow/external work synchronously in a plugin (2-minute sandbox limit) — slow or bursty work runs async. | Cloud & Integration |
DGT-INT-020 |
Use a single, lazily-created, shared HttpClient — never one per invocation. |
Cloud & Integration |
DGT-INT-030 |
External calls from plugins set an explicit timeout and KeepAlive = false. |
Cloud & Integration |
DGT-INT-040 |
Every integration handles HTTP 429 / Retry-After (service protection limits); use ServiceClient or equivalent backoff. |
Cloud & Integration |
DGT-INT-050 |
Never use the Dataverse Search API for bulk queries. | Cloud & Integration |
DGT-INT-060 |
Push Dataverse events to Azure asynchronously via service-endpoint registration, not synchronous POSTs from plugins. | Cloud & Integration |
Power Automate Flows (FLW)¶
| Rule | Statement | Defined in |
|---|---|---|
DGT-FLW-010 |
Create flows solution-aware, never as standalone "My flows" artifacts. | Power Automate Flows |
DGT-FLW-020 |
No production flow without error handling (scope-based try/catch and failure notification). | Power Automate Flows |
DGT-FLW-030 |
Filter in the trigger (trigger conditions, column/row filters), not with a first-action condition. | Power Automate Flows |
DGT-FLW-040 |
Treat concurrency and pagination settings as code — variables aren't thread-safe under concurrency; raise pagination limits explicitly. | Power Automate Flows |
Testing & Quality (TST)¶
| Rule | Statement | Defined in |
|---|---|---|
DGT-TST-010 |
Use Digitall.Dataverse.Testing (in-memory) for plugin/Custom API unit tests, not a live environment. |
Server-side Unit Testing |
DGT-TST-020 |
Run the Power Apps Solution Checker against every packed solution as a build-pipeline quality gate. | Solution Checker |
DGT-TST-030 |
Unit-test TypeScript web resource logic with Jest, mocking Xrm via xrm-mock. |
Client-side Testing |
DGT-TST-040 |
A user story must meet the Definition of Ready before entering a sprint. | Definition of Ready / Done |
DGT-TST-050 |
A user story must meet the Definition of Done, including passing tests and updated documentation, to be closed. | Definition of Ready / Done |
DGT-TST-060 |
Activate each release wave's Early Access in a sandbox and run a regression before the wave reaches production. | Performance & Release Waves |
ALM & Deployment (ALM)¶
| Rule | Statement | Defined in |
|---|---|---|
DGT-ALM-010 |
Regularly commit the unpacked Dataverse solution to source control, regardless of deployment approach. | Source Control |
DGT-ALM-020 |
Enforce a single line-ending convention across the repository via .gitattributes. |
Source Control |
DGT-ALM-030 |
No secrets, environment-specific values, or build artifacts in the repository. | Source Control |
DGT-ALM-040 |
Bump the solution version via dgtp maintenance solution-version, not by hand-editing solution.xml. |
Versioning |
DGT-ALM-050 |
The plugin package/assembly version must increase on every build that gets pushed. | Versioning |
DGT-ALM-060 |
Follow the canonical build-pipeline step order, independent of the CI orchestrator. | Build Pipeline |
DGT-ALM-070 |
Use token-based (service principal) dgtp profiles for CI — never personal credentials. |
Build Pipeline |
DGT-ALM-080 |
Import managed solutions as Upgrade (or stage for upgrade), not Update. | Pre- & Post-Deployment Tasks |
DGT-ALM-090 |
Provide connection references and environment-variable values via a deployment settings file per target. | Pre- & Post-Deployment Tasks |
DGT-ALM-100 |
Power Platform Pipelines production deployments run delegated (service principal) and require approval. | Power Platform Pipelines |
DGT-ALM-110 |
Use YAML pipelines checked into the repository, not classic editor-defined pipelines. | Azure DevOps |
DGT-ALM-120 |
Use the matching dgtp export/dgtp import command pair for non-solution-aware config objects. |
Config & Reference Data Migration |
Operations (OPS)¶
| Rule | Statement | Defined in |
|---|---|---|
DGT-OPS-010 |
Enable Application Insights integration for every production environment. | Monitoring & Telemetry |
DGT-OPS-020 |
Every production environment has the monitoring baseline (plugin failures/time, flow failures, checker trend, deployment history). | Monitoring & Telemetry |
DGT-OPS-030 |
Define DLP policies at the environment-group or tenant level appropriate to the customer's risk posture. | Governance & DLP |
DGT-OPS-040 |
Don't customize outside development environments — rely on Managed Environments to enforce this. | Governance & DLP |
DGT-OPS-050 |
Scope production access to what's needed for support/troubleshooting, not standing System Administrator. | Governance & DLP |