---
name: salesforce-org-conventions
description: >
  Apply the organisation's own Salesforce conventions — the naming rules and mandated patterns this org enforces on top of generic platform practice — across custom objects, fields, validation rules, page layouts, Lightning pages, Flows, custom labels, Apex classes, triggers and tests, and Visualforce. Use this whenever the user creates or names any Salesforce metadata: a custom object or field, a junction object, a record type, a validation rule, a page or compact layout, a Lightning page, a Flow, a custom label, an Apex class, trigger, handler or test class, or a Visualforce page; writes a trigger that must sit on `TriggerTemplateV2`; needs a record type Id through `Utils.getRecordTypeId`; stores callout credentials in `Credential__c`; wires a bypass through `Ignore_Validation_Rules__c`; suffixes a class `Ctrl`, `Controller` or `Rest`; or simply asks "what should I call this". Also apply it when porting older metadata that predates these rules.
metadata:
  technology: Salesforce
  type: development
---

# Salesforce Org Conventions
> **Targets:** this organisation's Salesforce standards · **Verified:** 2026-09 against the team standards document

These are house rules, not platform rules. Where they differ from generic Salesforce advice they win, because reviewers enforce them and the existing codebase already follows them. Two habits carry most of the value: let the platform generate API names from well-chosen labels, and put every piece of logic behind the framework and helpers the org already owns rather than a new one. British English spelling throughout, in labels and API names alike.

## 1. Decide first

| Question | Default | Change when |
|---|---|---|
| Trigger framework? | **`TriggerTemplateV2`**, one trigger per object, all 7 events declared | Never for new work — an object already on another framework is migrated, not forked |
| Where does a record type Id come from? | `Utils.getRecordTypeId('Case:Digital_Support')` held in a `static final` | Never a hard-coded Id and never an inline describe call |
| Where do callout credentials live? | `Credential__c` or a Named Credential | Named Credential whenever the callout can use one — see §3 for the `Credential__c` conditions |
| Do I need a new field? | No — use a standard field, then an existing custom field | Only when neither carries the meaning; then prefer a picklist over free text |
| API name? | Whatever the platform generates from the label | Never hand-written camel case; only the `x` prefix rule in §4 overrides this |
| Sharing declaration? | `with sharing` | `without sharing` with a comment saying why. Every class states one explicitly |
| Async work? | The org's `Async` class, so context is handled for you | Direct `System.enqueueJob` only where `Async` genuinely cannot express the job |
| Can this automation be switched off? | Yes — validation rules and flows honour `User.Ignore_Validation_Rules__c`; each trigger handler has its own bypass | Never ship automation with no way to disable it |

## 2. Apex files, classes and members

- **Names.** Classes start uppercase and `UseCamelCase`; variables and methods start lowercase and use `camelCase`; `static final` constants are `ALL_CAPS_SNAKE_CASE`. No non-ASCII characters in identifiers. Limit acronyms and abbreviations — a name should describe its purpose and still be readable and memorable.
- **Suffixes carry meaning.** Visualforce and LWC controllers end in `Ctrl` or `Controller` (`DemandPlan` → `DemandPlanCtrl`). Classes exposing REST endpoints end in `Rest`. Test classes are the class under test plus `Test` (`AccountTrigger` → `AccountTriggerTest`).
- **Every file opens with the change log** — date, name or initials, description of the change, and the user story or bug number. Template in `references/apex-file-conventions.md`.
- **DML goes through `Database` methods**, so partial success is a decision rather than an accident; inspect the results you get back.
- No SOQL or DML inside a loop, no hard-coded Ids, and no `future` or other async call from inside a loop.

## 3. Triggers and credentials

- One trigger per object, named `{ObjectName}Trigger`, holding no logic — it hands off to `{ObjectName}TriggerHandler` through `TriggerTemplateV2`.
- **Declare all 7 events** — `before insert, before update, before delete, after insert, after update, after delete, after undelete` — even where a handler ignores some. Trigger Hook relies on the full set being present.
- **Bypass logic lives in the trigger, per handler class**, so one handler can be switched off without disabling the object.
- **Never pass `Trigger.new` wholesale into a handler method.** Filter to the records actually in scope for that piece of logic and pass those. It keeps the handler honest about what it operates on and keeps bulk behaviour predictable.
- Callout credentials belong in `Credential__c` or a Named Credential, never in code, a static resource or a hard-coded string. A `Credential__c` record is ordinary data: store the secret in an encrypted field, restrict field-level security to the integration user, and keep the records out of change sets and packages.

Skeleton, bypass wiring and the `Utils` / `Async` call patterns: `references/apex-file-conventions.md`.

## 4. Objects and fields

- Objects are **singular**, unique, start with an uppercase letter, and carry no underscores in the label. Junction objects are named for the two objects they join with the **primary parent first** — the one that drives sharing — as in `Problem_Part__c`.
- Every object has **at least one record type**, and security comes from permission sets and profiles.
- Field labels are short; detail belongs in help text and the description. Date fields end in `Date`; Date/Time labels end in `Date/Time` with the API name ending `Date_Time`; checkboxes are **not** prefixed `Is`; lookups name their target object; a label starting with a number gets an API name starting with `x`.
- **Add FLS to the four admin profiles** — System Administrator, System Specialist, System Specialist Lite, System Integration — whenever a field is created. A field nobody in admin can see is a support ticket waiting to happen.
- Turn on History Tracking only for fields that genuinely matter, and never paste picklist values from Word or Excel: they carry invisible characters that break comparisons later.

Full tables, including the spelling and acronym rules: `references/object-and-field-naming.md`.

## 5. Automation, layouts and Visualforce

- **Validation rules** reference `User.Ignore_Validation_Rules__c`, hard-code no Ids, and place the error at the field rather than the top of the page.
- **Flows** are named *Object · Record Type Label (if any) · Context · Event*, as in `Problem Asset_MQ Standard_Before_Upsert`, and carry a feature toggle the way validation rules do.
- **Approval processes** hard-code no Ids, and field updates never use *Re-evaluate Workflow Rules after Field Change*.
- **Custom labels** are not prefixed for common terms such as `Save`, and no two labels share a value.
- **Layouts** never contain the word "Layout" in the name. Lightning pages are named `{sobject API Name} Org Default`, with the Details tab first, Related last, and anything object- or process-specific in between.
- **Visualforce** links pages with `{!$Page.PageName}` rather than `/apex/PageName`, surfaces errors through `<apex:pageMessages />`, marks non-persisted data `transient` to shrink view state, and wraps dynamic SOQL input in `String.escapeSingleQuotes()`.

Layout, list view, compact layout, lookup filter and Visualforce detail: `references/layouts-and-ui.md`.

## 6. Tests

Test classes are named after the class they cover and end in `Test`. Target **90 % or better** coverage — the platform's 75 % is a deployment floor, not the bar here. Never `SeeAllData=true`. Split behaviour across multiple test methods, build shared data in `@TestSetup`, reset limits with `Test.startTest()` / `Test.stopTest()`, and cover the negative cases as well as the happy path. Test idioms and mocking belong to `apex-testing`.

## 7. Deliverable format

```
## What I created
<metadata type and API name, with the label it was generated from>

## Conventions applied
<the specific rules that shaped each name, so a reviewer can check them>

## Follow-ups
<FLS on the four admin profiles, record type, help text, bypass wiring — whatever is still outstanding>
```

## Checklist

- [ ] Every API name was generated from its label; none hand-written in camel case
- [ ] Object names singular; junction objects named primary parent first
- [ ] Date, Date/Time, checkbox, lookup and numeric-leading field rules applied
- [ ] FLS granted to the four admin profiles; description and help text filled in
- [ ] Trigger is one line on `TriggerTemplateV2`, declares all 7 events, bypasses per handler
- [ ] No `Trigger.new` passed into a handler unfiltered
- [ ] Record type Ids come from `Utils.getRecordTypeId` into a `static final`
- [ ] Every class declares `with sharing` or `without sharing`, and opens with the change log
- [ ] Validation rules and flows honour `User.Ignore_Validation_Rules__c`
- [ ] Credentials in `Credential__c` (encrypted, FLS-restricted) or a Named Credential
- [ ] Tests named `{ClassUnderTest}Test`, at 90 %+, with negative cases

## Anti-patterns

- **Hand-written camel case API name** (`MyNewField__c` from "My New Field") → let the platform generate it from the label
- **Plural object name, or underscores in the label** → singular, spaces in the label
- **Checkbox called `Is_Active__c`** → `Active__c`; the type already says it is a boolean
- **Acronyms in a label or API name** → spell it out; the abbreviation is obvious only to whoever coined it
- **Inline `getRecordTypeInfosByDeveloperName()` describe call** → `Utils.getRecordTypeId(...)` in a `static final`
- **Logic in the trigger body, or a second trigger on the object** → one trigger, `TriggerTemplateV2`, handler holds the logic
- **`Trigger.new` handed straight to a handler method** → filter to the records in scope and pass those
- **Picklist values pasted from Word or Excel** → retype them; pasted values carry invisible characters
- **"Layout" in a layout name, or a Lightning page named ad hoc** → drop the word; `{sobject API Name} Org Default`

## Go deeper

- `references/object-and-field-naming.md` — the complete object, field, picklist and FLS tables; open it when creating or renaming metadata
- `references/apex-file-conventions.md` — change-log template, the `TriggerTemplateV2` skeleton with all 7 events and bypass wiring, and the `Utils`, `Async` and `Credential__c` call patterns
- `references/layouts-and-ui.md` — page, search, compact and list layouts, lookup filters, Lightning page structure, custom labels and the Visualforce rules
- Sibling skills: `apex-development` for Apex idioms, `apex-testing` for tests, `salesforce-data-model` for modelling decisions, `salesforce-automation` for Flow design, `salesforce-code-review` for reviews
