SfSkills Domains Docs GitHub

Home / admin / data-model-documentation

data-model-documentation

Use when a BA or admin needs to document the Salesforce data model: creating field inventories, object relationship maps, ER diagrams, or analyzing field usage across objects.

Open the skill folder on GitHub


Data Model Documentation

Use this skill when you need to produce documentation artifacts describing an existing or new Salesforce data model: field inventories, ER diagrams, object relationship maps, or a data dictionary. This skill produces the documentation — it does not design or change the model.


Before Starting

Gather this context before working:


Questions to Ask Before Configuring

Ask before writing a line of the dictionary. Each answer changes which generator you run and what the artefact must carry; an agent that skips them produces a 400-row CSV nobody can act on.

AskWhy it mattersWhat a good answer adds
"What decision will someone make from this document?"Integration mapping, a deletion review, onboarding and an audit need different columns; only the audit needs FLS, only the deletion review needs business_statusThe column set, and permission to omit the rest
"Who owns each object, by name?"The platform models this as businessOwnerUser on the field itself, so the answer is deployable rather than a spreadsheet column that rotsA real owner per object and per sensitive field — the checker errors without one
"Which fields hold regulated or sensitive data?"Drives securityClassification and complianceGroup, and decides whether FLS must be documented at all (a separate Profile/PermissionSet retrieve)The classification per field, and whether the FLS retrieve is in scope
"Which account will run the extract, and with what permissions?"Describe and retrieve are both scoped by the running user; two snapshots taken as different users diff as false driftA single documentation user recorded in generated_from.retrieved_as
"Is this a one-off snapshot or a standing control?"A one-off can be a CSV; a standing control needs the manifest, the snapshot directory and the diff job in source controlA review cadence and a last_reviewed owner, or an explicit decision not to have one
"Which objects are in scope, and do standard objects count?"A <members>*</members> manifest retrieves no standard objects, so "all objects" and "the wildcard" are not the same requestAn explicit object list, standard objects named individually
"What happens to fields marked as dead?"businessStatus: DeprecateCandidate is a documentation state; deleting the field is a different job with a different blast radiusA hand-off to /analyze-field-impact rather than a delete list

What a proper documentation pass adds over exporting the field list: the artefact names an owner for every object, carries a purpose for every field that describe cannot supply, records who and when it was generated so the next diff is interpretable, and has a review date that makes it falsifiable.


Core Concepts

Objects and Fields in Salesforce

Every record in Salesforce is an instance of an sObject. Standard objects (Account, Contact, Opportunity, Case) are provided by Salesforce. Custom objects have API names ending in __c. Fields on objects have an API name, a field type (Text, Number, Date, Lookup, etc.), a label visible to users, and metadata properties including Required, Unique, External ID, and Field-Level Security (FLS).

The Object Reference defines the field types, cardinality rules, and behavior of each relationship field type. Lookup fields create a loosely coupled many-to-one relationship; deleting the parent leaves the child intact (blank lookup). Master-Detail fields create a tightly coupled relationship; deleting the master deletes the child (cascade delete). A junction object for a many-to-many relationship is "a custom junction object with two master-detail relationship fields, each linking to the objects that you want to relate" (Object Reference, Custom Objects — supported in API version 11.0 and later).

Schema Builder

Schema Builder (Setup → Object Manager → Schema Builder) is Salesforce's built-in visual ER diagram tool. It allows you to view objects, fields, and relationships in a drag-and-drop canvas, filter by object, and export a visual representation. It is the fastest way to produce an ER diagram for a small to medium scope (10–30 objects). For large orgs with hundreds of custom objects, Schema Builder becomes slow and the export is limited — use Metadata API retrieval instead.

Two Views of the Schema, and Why They Disagree

The org exposes its schema through two independent surfaces, and a data dictionary needs both.

Runtime view (describe)Source view (retrieve)
Reached bySchema.getGlobalDescribe(), Schema.describeSObjects(), REST /sobjects/<Object>/describeMetadata API retrieve of CustomObject
Gives youtype, length, required, unique, external ID, formula flag, referenceTo, relationshipName, relationshipOrder, help text, picklist valuesdescription, inlineHelpText, businessOwnerUser, businessStatus, securityClassification, complianceGroup, formula text, validation rules
Cannot give youthe field's description — Schema.DescribeFieldResult has no getDescription() methodanything the retrieving user is not entitled to retrieve; non-customisable standard fields
Scoped bythe running user's FLS (isAccessible())the retrieving user's permissions
Wildcard behaviourn/a — enumerate with getGlobalDescribe()<members>*</members> returns all custom objects and no standard ones

The dictionary is the join. Running only describe produces a shape catalogue with an empty purpose column; running only retrieve misses the runtime cardinality that the ER map needs. Both generators, and the join, are in references/worked-examples.md.

Metadata API and SFDX Retrieval for Field Inventory

For complete field inventory across many objects, retrieve the metadata using Metadata API or the sf CLI. The CustomObject metadata type includes every field definition, validation rule, and relationship. Once retrieved as XML or SFDX source format, the field inventory can be parsed programmatically or inspected manually.

# Retrieve all CUSTOM objects via sf CLI. This returns no standard objects — see gotcha 11.
sf project retrieve start --metadata "CustomObject"

After retrieval in source format, each object lives in force-app/main/default/objects/<ObjectName>/ with individual fields/*.field-meta.xml files. Naming a standard object explicitly (CustomObject:Account) returns its customisations plus the standard fields that are customisable; system fields such as CreatedById and LastModifiedDate and autonumber fields are excluded (Metadata API guide, Sample package.xml Manifest Files → Standard Objects). Standard picklists are a separate type: "In API version 38.0 and later, the StandardValueSet type represents standard picklists. Picklists are no longer represented by fields as in earlier versions" — so Account.Industry values come from a StandardValueSet member named Industry, not from the object retrieve.

Field Description Quality

Every custom field has an optional Description property visible only in Setup and in the metadata — not to end users. A complete data dictionary requires every custom field to have a populated Description explaining what the field is used for, what values are valid, and who owns it. An org with blank field descriptions has undocumented schema debt. scripts/check_data_model_documentation.py --manifest-dir <retrieve-root> enumerates it from the retrieved .field-meta.xml files and reports it as WARNs rather than errors, so the debt is listed and routed to owners instead of blocking the delivery.


Common Patterns

Pattern 1: Field Inventory Using Object Manager

When to use: You need a field-by-field inventory for one or a few objects and have live org access.

How it works:

  1. Navigate to Setup → Object Manager → [Object Name] → Fields & Relationships.
  2. The list view shows: Field Label, API Name, Data Type, Controlling Field (for dependent picklists), and whether the field is indexed.
  3. Click each field to view the full Description, Required flag, Unique flag, and FLS settings.
  4. Use the "Fields & Relationships" export via the Metadata API (see Pattern 2) for bulk extraction rather than manually clicking each field.
  5. For FLS documentation, navigate to Setup → Profiles or Permission Sets and review field permissions per field.

Why not export manually: Object Manager UI does not have a CSV export button. Manual documentation from the UI works for < 20 fields. For more, use the Metadata API.

Pattern 2: Bulk Field Inventory via Metadata API Retrieval

When to use: You need a complete field inventory across many objects, or you need to track changes over time.

How it works:

  1. Create or reuse a package.xml that includes CustomObject for the target objects:
    <types>
      <members>Account</members>
      <members>Contact</members>
      <members>MyCustomObject__c</members>
      <name>CustomObject</name>
    </types>
  2. Retrieve: sf project retrieve start --manifest package.xml
  3. Each field is a separate file in objects/<ObjectName>/fields/<FieldName>.field-meta.xml.
  4. Parse the XML to extract: fullName, label, type, required, externalId, description, referenceTo (for Lookups), relationshipName.
  5. Load into a spreadsheet or documentation system.

Output: A flat field inventory with every property. The description field from metadata reveals whether fields are documented.

Pattern 3: Relationship Map Using Schema Builder

When to use: You need a visual ER diagram showing object relationships for a stakeholder presentation or onboarding document.

How it works:

  1. Open Setup → Object Manager → Schema Builder.
  2. Click "Clear All" to deselect all objects, then add only the objects in scope.
  3. Rearrange to group related objects. Master-Detail lines appear bold; Lookup lines appear thin.
  4. Use "Show Elements" to toggle field names on/off.
  5. Take a screenshot or export the diagram.

Limitation: Schema Builder does not distinguish polymorphic lookups (e.g., WhoId on Task which can point to Contact or Lead). Document these manually.


Decision Guidance

SituationRecommended ApproachReason
1–5 objects, live org accessObject Manager UI + Schema BuilderFastest for small scope
10+ objects or repeatable documentationMetadata API retrieval + XML parseProgrammatic, version-trackable
Stakeholder ER diagram neededSchema Builder export or draw.io from relationship mapVisual format; Schema Builder is built-in
Field description quality auditMetadata API retrieval, check <description> tagsUI does not bulk-expose blank descriptions
Documentation needs to track drift over timeSFDX source format in Git + diffMetadata in version control shows schema changes
Whole-org inventory, hundreds of objectsSchema.getGlobalDescribe() to enumerate, then batched describeSObjects()Schema Builder degrades; describeSObjects() caps at 100 objects returned per call
Nightly drift check on a few objectsREST /describe with If-Modified-SinceA 304 Not Modified with no body is the "no drift" answer; cheaper than a full retrieve
Owner and PII classification per fieldMetadata API CustomField — businessOwnerUser, securityClassification, complianceGroupThese are native, deployable metadata; see security/data-classification-labels
ER map that must survive polymorphic lookupsgetReferenceTo() per field, one edge per entrySchema Builder collapses or omits them; getReferenceTo() returns every parent
Sandbox vs production comparisonTwo retrieves under the same user, then a field-level diffPermission differences otherwise read as schema drift; see devops/metadata-diff-between-sandboxes

  1. Answer the questions above and fix the scope. Write the object list explicitly — standard objects

    by name, because a wildcard manifest returns none of them — and name the account the extract will run as. Record both in generated_from in the dictionary.

  2. Take the runtime snapshot. Run the Apex inventory script (references/worked-examples.md §1) or

    the REST equivalent (§2), batching the object list; describeSObjects() returns a maximum of 100 objects per call. This gives shape, not purpose.

  3. Take the source snapshot. Retrieve CustomObject for the same object list with the package.xml in

    §4 and flatten the .field-meta.xml files. This is the only path to description, businessOwnerUser, businessStatus and securityClassification.

  4. Join the two into the dictionary record. Fill templates/data-dictionary.yaml — one entry per

    object with owner, classification, record_volume (SELECT COUNT() FROM <Object>) and last_reviewed; one row per field with type and description. Worked version: §5.

  5. Derive the ER map from the same snapshot, not by hand: emit one edge per getReferenceTo() entry

    with cardinality from isNillable() and isCascadeDelete() (§3). Hand the edge list to architect/salesforce-erd-and-diagramming for notation and layout.

  6. Lint before delivering.

    python3 scripts/check_data_model_documentation.py --file <dictionary.yaml> --manifest-dir <retrieve-root> — errors block delivery (missing owner, unknown classification, field row with no type); WARNs are the documentation-debt list to route to owners, not to suppress.

  7. Make it falsifiable. Commit the manifest, both snapshots and the dictionary, then schedule the

    two-org diff in §6 so the next drift shows up as a diff rather than as a surprise in a project.


Review Checklist

Run through these before delivering data model documentation:


Salesforce-Specific Gotchas

Eleven in full, with grounding, in references/gotchas.md. The ones that most often invalidate a finished document:

GotchaOne-line shape
1. Standard fields absent from a retrieveA CustomObject retrieve returns customisations only; Name, OwnerId, CreatedDate come from the Object Reference
2. FLS lives in Profile/PermissionSet<fieldPermissions> is retrieved separately and cross-referenced; it is not on the field
3. Schema Builder omits polymorphic lookupsTask.WhoId / WhatId render as one line or none
4. No CSV export in Object ManagerUse the retrieve, or Tooling FieldDefinition — see gotcha 10 for its two mandatory constraints
5. The inventory depends on who ran itisAccessible() filters silently; record the extracting user
6. Describe has no getDescription()The purpose column can only come from the Metadata API
7. Compound fields count three, show one, diagram zeroGeolocation counts as three custom fields and is absent from Schema Builder
8. Relationship name ≠ field nameFour separate columns: field, referenceTo, relationshipName, relationshipOrder
9. Namespaces appear or vanish by contextPopulate from getName(), never getLocalName()
10. FieldDefinition is Tooling, not Object ReferenceRequires the EntityDefinition.QualifiedApiName filter; several obvious columns do not exist
11. A wildcard manifest retrieves no standard objectsName every standard object individually

Output Artifacts

ArtifactDescription
Field inventory spreadsheetColumns: Object API Name, Field Label, Field API Name, Type, Required, External ID, Description, Owner/Team
Object relationship mapList or diagram showing: Object A → (Relationship Type) → Object B, plus relationship field API name
ER diagramVisual Schema Builder export or equivalent diagram, annotated with cardinality
Documentation debt reportList of custom fields with blank Description values, grouped by object
Data dictionary recordtemplates/data-dictionary.yaml filled in: owner, classification, record volume, review date per object; type + description per field
Generator scriptsThe Apex / REST / retrieve extractors, committed so the next snapshot is a re-run rather than a re-derivation
Drift diffField-level delta between two same-manifest snapshots, both taken under the same user

Reference Files

FileRead it when
references/worked-examples.mdYou are about to generate anything — the six generators (Apex CSV, REST + flatten, Mermaid ER map, retrieve inventory, dictionary record, two-snapshot diff), filled in
references/gotchas.mdAn inventory, map or diff disagrees with what you expected, or before you trust a describe-only export
references/well-architected.mdScoping the effort, arguing for living documentation over a snapshot, or citing sources
references/llm-anti-patterns.mdReviewing AI-generated data model documentation, or self-checking your own output
templates/data-model-documentation-template.mdProducing the human-facing document — scope, object inventory, relationship map, FLS summary, delivery checklist
templates/data-dictionary.yamlProducing the machine-lintable record the checker validates
scripts/check_data_model_documentation.pyBefore delivering — lints the dictionary (--file) and the source metadata for description debt (--manifest-dir)

Package files