Every Dataverse codebase we inherit has two or three naming styles wrestling for dominance. Half the tables use a three-letter prefix a previous consultant picked. The other half kept the new_ default because a maker created them quickly and nobody changed it. Option set values are sometimes Active, sometimes active, sometimes 1. Relationship schema names are whatever Dataverse auto-generated when the developer clicked Save.
The rename cost after a system goes live is enormous. Every managed solution update that touches a renamed component risks layering conflicts. Every plugin, flow, or Web API call that references the old schema name breaks silently. Every report or view has to be audited.
The convention below is the one we enforce from day one on every new Power Platform engagement. It is boring on purpose, and boring is the whole point.
Publisher prefix
Pick a three- to six-letter prefix unique to the client, and use it on every custom artifact without exception.
- Tables: acme_customer, acme_order_line
- Columns: acme_loyalty_tier, acme_total_amount
- Choices (option sets): acme_customer_segment
- Relationships: acme_order_customer
- Plugin steps, actions, custom APIs: same prefix
Avoid generic prefixes like xyz_ from a template repo you cloned, or new_ which is the Dataverse default when a maker forgets. The prefix ends up in the URL of every record you share, the name of every file in exported solutions, and the column header of every report. It is effectively permanent.
Pick the prefix before creating the first table. Changing it later means cloning every component with the new name and migrating data - multiple person-weeks of work we have done twice and never want to do again.
Schema name vs display name
Every table and column has two names in Dataverse:
- Schema name: the internal, permanent, API-visible identifier. Immutable after creation.
- Display name: what appears in the UI. Fully editable at any time.
Treat them as two separate problems.
Schema names are for developers and APIs. Use PascalCase for tables (acme_CustomerAgreement) and snake_case is also acceptable but be consistent. Never include spaces, accents, or symbols. Prefer the singular form (acme_customer, not acme_customers) - Dataverse adds the plural for you.
Display names are for users. Can have spaces, can be translated, can change whenever the business wants. The business users never see schema names, so do not let business wording pressure schema naming.
Common mistake: letting a business analyst name a column "Purchase Order #" and the schema name becomes acme_purchaseorder. Six months later, when the business adds a new concept called "Purchase Order" (a full table), the short column name blocks it. Fix before it happens: schema name describes the technical concept (acme_po_reference_number); display name says whatever makes sense to users.
Columns
- Lookups: name them for the relationship they represent, not the target table. A lookup on acme_order pointing to account should be acme_customer (what it means in this context), not acme_account.
- Booleans (Yes/No columns): phrase them as a statement, not a question. acme_is_active is clear; acme_active is ambiguous (active what?); acme_is_the_customer_active is too long.
- Dates: suffix with the time type - acme_effective_on (date only), acme_created_at (datetime), acme_shipped_at.
- Numbers/currency: suffix with the unit meaning - acme_amount_usd, acme_weight_kg, acme_count.
Choices (option sets)
Two rules that save pain later:
- Option values are integers you pick, not defaults you accept. Dataverse assigns sequential values if you let it. Set them explicitly to stable, non-colliding numbers like 100000000, 100000001, etc. This lets you add new values between existing ones without renumbering.
- Labels are business-facing, internal value is developer-facing. Do not depend on the label in code - a user can rename "Active" to "Live" and break every condition you wrote against the label. Always match on the integer value.
Relationships
Dataverse auto-generates relationship schema names unless you override them. The auto-generated names are a mess (acme_customer_acme_order_acme_customer). Always override.
Our convention: {prefix}_{parent}_{child} for 1-to-many, and {prefix}_{entity_a}_{entity_b} for N-to-N. The schema name should read like a sentence describing the connection, not concatenated entity names.
Solutions and components
Solution unique names: {prefix}_{bounded_context} - acme_customer_onboarding, acme_order_management. One solution per bounded context (not per table).
Plugin assembly names: {Prefix}.Plugins.{Context} - Acme.Plugins.Customer.
Custom API / action unique names: {prefix}_{Verb}{Noun} - acme_ActivateAgreement, acme_CalculateRebate.
When the convention costs you
There are two scenarios where enforcing this feels excessive.
When integrating with a packaged solution that has its own prefix. If you install a Dynamics 365 managed solution that uses prefix msdyn_, resist the urge to match their prefix for your extensions - that creates layering ambiguity later. Keep your acme_ prefix on your extensions and reference their components via their original names.
When writing throwaway internal flows. A maker spinning up a quick Power Automate flow for an internal Excel-replacement tool does not need to agonize over schema names. If the flow is scoped to a single table nobody else touches, default names are fine. The convention applies to anything shipped as part of a managed solution to UAT or Prod.
The review we run
At the end of every sprint that added Dataverse components, we run a thirty-minute review:
- New tables have the correct prefix and singular schema name
- New columns have unit-suffixed names where applicable
- Any new choices have explicit integer values, not auto-assigned
- Any new relationships have overridden, readable schema names
- Display names have been sanity-checked against what a new user would expect
Most sprints, it finds one or two items. Fixing them at sprint end is a ten-minute touch-up. Fixing them after the solution is in Prod is a weekend of migration work. That ratio is why the review keeps earning its slot.
If your current Dataverse codebase is already a mess of inconsistent names, the fix is not a big-bang rename - the fix is to stop adding to the mess, apply the convention to new components, and rename old components only when you are already touching them for another reason.