General¶
Standards that apply to every language. Each standard is stated once here, by topic, and the language pages link to it; a standard that belongs to one language stays on that language's page. Naming is the exception: the naming standards of every language are here, under one heading for each language.
Accessibility¶
Accessibility standards that apply to every kind of markup: Visualforce, HTML, Lightning Web Components, and Aura. The baseline, WCAG 2.2 Level AA, how it is enforced, and the laws that require it are on the Accessibility principle page. The standards that belong to one kind of markup, such as the tables, forms, and media of HTML and Visualforce, stay on its page.
Text Alternatives¶
A screen reader announces an image by its alternative text, and a button or a link by its name. Without them, the user hears a file name, or only "button" or "link", and cannot tell what the image shows or what the control does. Every image that carries information has a text alternative (WCAG 1.1.1), and every control has a name (WCAG 4.1.2). Both are Level A.
Images¶
- Every image has an alternative text attribute:
alton animgor anapex:image,alternative-texton alightning-carousel-image, andalternativeTexton alightning:carouselImage, the attribute that the base components give an image for WCAG 1.1.1 (Base Components Accessibility). Its text says what the image shows (technique H37). An image with no such attribute blocks the pull request (severity 2). Enforced byImageWithoutAltVfandImageWithoutAltHtmlLwcAura. - A decorative image, one that carries no information, has an empty value, which tells assistive technology to ignore it (technique H67), and only a decorative image should have an empty value. An image with an empty or blank value is reported at severity 4, so that the reviewer may confirm that it is decorative, with the W3C's decision tree (An alt Decision Tree). Enforced by
ImageWithEmptyAltVfandImageWithEmptyAltHtmlLwcAura. - An avatar (
lightning-avatarorlightning:avatar) names the person or the record that it shows in its alternative text. That text becomes thealtof the image (Avatar). Salesforce renders an avatar with no alternative text as a decorative image, so leaving the attribute out is allowed. An avatar whose alternative text is set to an empty value is reported at severity 4, as an emptyaltis. Enforced byImageWithEmptyAltHtmlLwcAura. - A merge expression or a bound property in the attribute (
alt="{!$Label.Logo}",alt={logoLabel},alternativeText="{!v.logoLabel}") counts as text. The rules cannot know its value when the page runs, so the developer makes sure that it is never empty for an image that carries information.
Buttons and Links¶
- Every image button, icon button, icon-only Lightning button, and image-map link has a name that a screen reader can announce, and the name says what the control does: "Close", not "X icon". An empty value gives the control no name, so it is as bad as a missing one. A control with no name, or with an empty one, blocks the pull request (severity 2). Enforced by
ButtonWithoutNameVfandButtonWithoutNameHtmlLwcAura. - A Visualforce page renders as HTML, so an image map in a page follows the same standard. Visualforce has no component for an image map, and a page uses the HTML
mapandareaelements, whichButtonWithoutNameVfreads.
Where each control takes its name from:
| Control | Its name |
|---|---|
input type="image", and apex:commandButton with an image |
alt (technique H36) |
area with an href |
alt (technique H24) |
lightning-button-icon, lightning-button-icon-stateful |
alternative-text (Button Icon) |
lightning:buttonIcon, lightning:buttonIconStateful |
alternativeText |
lightning-button, lightning:button with an icon |
label |
| Any of them | aria-label or aria-labelledby; on an apex:commandButton, html-aria-label or html-aria-labelledby (apex:commandButton) |
What the Rules Do Not Check¶
These elements are not checked by a rule. Reviewers check the ones that carry meaning.
- Icons.
lightning-icon,lightning-dynamic-icon, the icon of alightning-badgeor of alightning-vertical-navigation-item-icon, and their Aura forms, draw their icon hidden from assistive technology, and read out the alternative text only when it has a value. With no attribute and with an empty one, an icon renders the same. Salesforce lets a decorative icon leave the attribute out (Icon). An icon that carries meaning has alternative text that says what it means, such as "Approved" (Using Images and Icons). An icon can be decorative at one screen size and carry meaning at another, so an icon left without alternative text is checked on small screens as well as wide ones. No rule checks this. Reviewers do. - Components that Salesforce names itself.
lightning-button-menu,lightning-helptext, andlightning-barcode-scannerare given a name, such as "Show menu" or "Help", when the attribute is left out. - Spinners. A spinner (
lightning-spinnerorlightning:spinner) is a status message, not an image or a button. The alternative text of a spinner says what is loading. No rule checks this. Reviewers do. - Beta components.
lightning-illustrationandlightning-empty-state. - Elements named another way. An
svg, an element withrole="img", anobject, and acanvasget their name from atitle, anaria-label, or their fallback content, not from analt. - Text from JavaScript. Alternative text that a component takes from JavaScript data, such as the items of a
lightning-pill-containeror an icon column of alightning-datatable, is outside the markup that the rules read. - Visualforce email templates. These rules do not read Visualforce email templates (
.emailfiles): they are Salesforce Classic templates, which can be edited only in Classic, and Lightning email templates take their place in Lightning Experience (Use Visualforce Email Templates with Guest Users).
The rules for HTML, Lightning Web Components, and Aura read Aura markup through Code Analyzer. With PMD alone, Aura markup needs a second run (Appendix F.2: Aura Markup and PMD Alone).
Sources:
- An alt Decision Tree - W3C
- apex:commandButton - Visualforce Developer Guide
- Avatar - Lightning Component Reference
- Base Components Accessibility - Lightning Web Components Developer Guide
- Button Icon - Lightning Component Reference
- H24: Providing text alternatives for the area elements of image maps - W3C
- H36: Using alt attributes on images used as submit buttons - W3C
- H37: Using alt attributes on img elements - W3C
- H67: Using null alt text and no title attribute on img elements for images that assistive… - W3C
- Icon - Lightning Component Reference
- Understanding Success Criterion 1.1.1: Non-text Content - W3C
- Understanding Success Criterion 4.1.2: Name, Role, Value - W3C
- Use Visualforce Email Templates with Guest Users - Salesforce Help
- Using Images and Icons - Lightning Aura Components Developer Guide
Documentation¶
The code says what it does. Documentation says what the code is for and why it is written that way, which the code cannot say. The next developer, a reviewer, and an auditor do not have the context of the person who wrote it, and a tool or an agent that reads the code or its metadata learns from the description as well.
- Every class, method, and property in Apex carries an ApexDoc comment with a description (Apex: ApexDoc). Every metadata component carries a description of what it is for (Objects and Fields: Descriptions).
Comments¶
The standards below apply to every language. Each language page adds what is particular to it: Apex, HTML and Visualforce, JavaScript, and Lightning Web Components and Aura.
- Code is commented where a comment helps the next reader, in every language and in addition to ApexDoc. A comment says why the code is written as it is, and explains what a reader would otherwise have to work out: a step that is not obvious, a workaround and what it works around, the reason for an order of steps, or a limit that shaped the code. No rule checks this. Reviewers do.
- A comment says why, not what. A comment that only repeats the code adds nothing, and it can fall out of step with the code when the code changes. No rule checks this. Reviewers do.
- An exception to a blocking rule is explained in the code. Where a reviewer will have to accept a blocking violation, such as a query in system mode, a comment on the lines above it or in the method's header acknowledges the exception and gives the reason. The reviewer then has the context to accept it without asking the developer for an explanation, and a comment in the code, unlike the description of a pull request, is there for every later review of the same code. No rule checks for the comment. Reviewers do.
- A comment is not where unfinished work is kept.
TODO,TO-DO,FIXME,FIX-ME, orHACKas the first word of a comment line, or anywhere in a comment with a colon after it (// Note: TODO: retry), marks unfinished work, which is finished, or recorded where the team tracks its work, and then removed.TODOandFIXMEare matched in any case, and the other three in capitals only, so that "to-do" and "hack" in an ordinary sentence are not reported. One that remains is reported at severity 5. Enforced byNoUnresolvedTodosApex,NoUnresolvedTodosJavaScript, andNoUnresolvedTodosMarkup. - No violation is suppressed without an approving review from the owning team (No Scanner Suppressions). A suppression in the code blocks the pull request (severity 2) until the violation is fixed or the owning team approves the suppression. Enforced by
NoUnapprovedSuppression,NoUnapprovedEslintSuppression,@lwc/lwc-platform/no-inline-disable, and@salesforce-ux/slds/reduce-annotations, for the suppression comments of the SLDS linter (SLDS 2: Suppression Comments). -
A header comment names a real author, a person or a team, never a default that a tool wrote.
The Salesforce Documenter (SFDoc) extension for VS Code, which is no longer maintained, fills in
ChangeMeIn@UserSettingsUnder.SFDocas the author and as the last user to modify the file, until its user name setting is changed. The default looks like an author and names nobody. That string in any file blocks the pull request (severity 2). Enforced byNoSfdocDefaultAuthor.
Naming¶
The naming standards of each language, in one place. The naming of metadata is on the Metadata Standards page.
Apex¶
- Names follow the standard Apex conventions: classes, interfaces, and enums in Pascal case (
AccountService); methods, fields, parameters, and local variables in camel case (findAccounts); and constants and enum values in upper case with underscores (MAX_RETRIES). A name that breaks the convention blocks the pull request (severity 1). Enforced byClassNamingConventions,MethodNamingConventions,FieldNamingConventions,FormalParameterNamingConventions, andLocalVariableNamingConventions. - An annotation is written in Pascal case, as the Apex Developer Guide writes every annotation (Annotations):
@IsTest, not@isTest, and@TestVisible, not@testVisible. Apex reads an annotation in any case, and Prettier leaves it as it was typed, so the developer writes it. An annotation in another case blocks the pull request (severity 1). Enforced byAnnotationsNamingConventions. - No class, enum, or interface takes the name of a type in the
SystemorSchemanamespace, such asDatabase,Url,Callable, orDisplayType(System Namespace, Schema Namespace). Code that means the built-in type can reach the local one instead, and a reader cannot tell which of the two it means. A top-level type with such a name blocks the pull request (severity 2). Enforced byTypeShadowsBuiltInNamespace. The rule does not read the name of an inner class, so reviewers check it. - A variable's name is not one letter: a local variable, a loop variable, a parameter, a field, a property, and the variable of a
catchblock each have a name of two or more letters that says what it holds (account, nota). Three names are allowed, each in its place, because every Apex developer reads them the same way:iandjas the counter of aforloop, andeas the variable of acatchblock. A one-letter name is reported at severity 3. A test class is not checked by default, and an organisation that wants it checked sets thereportInTestClassesproperty to true. An organisation that allows other one-letter names lists them in theallowedNamesproperty, and they are then allowed for every kind of variable. Enforced byNoSingleLetterVariableName.
Class/Trigger
- A class name ends with a suffix that says what the class is:
TestorTestsfor a test class,Batchfor a Batchable class,Queueablefor a Queueable class,SchedorSchedulablefor a Schedulable class,SchedBatchfor a class that is both Schedulable and Batchable,Exceptionfor a class that extendsException, andConstantsfor a class that only holds constants. The test class suffix is checked by a rule: a test class whose name ends otherwise blocks the pull request (severity 1). Enforced byClassNamingConventions, through itstestClassPatternproperty. No rule checks the other suffixes. Reviewers report a missing one at severity 3, which does not block. - A class that a Lightning component calls ends in
Controller. The@AuraEnabledannotation makes a method available to Lightning web components and Aura components (AuraEnabled Annotation), so the name tells a reviewer that the class can be reached from a browser. A top-level class with an@AuraEnabledmethod and another name is reported at severity 4. Enforced byAuraEnabledControllerNaming. A class that only has@AuraEnabledfields is a data class and is not reported. The custom controller and the extensions of a Visualforce page are named the same way (Controller Names). -
A class is a test class because of its
@IsTestannotation, not its name (Test Classes). Every test class still ends inTestorTests, so that a reader can tell it from a production class by its name alone. -
An interface's name starts with
I(ILogger), and so does the name of an interface nested in a class. An interface named otherwise blocks the pull request (severity 1). Enforced byClassNamingConventions, through itsinterfacePatternandinnerInterfacePatternproperties. - An abstract class's name starts with
Abstract(AbstractIntegrationService), as an interface's name starts withI. The prefix tells a reader that no code creates an instance of the class and that a subclass completes it. A top-level abstract class named otherwise blocks the pull request (severity 1). Enforced byClassNamingConventions, through itsabstractClassPatternproperty. The property reads a top-level class only, so reviewers check an abstract class nested in another. ClassNamingConventionshas seven pattern properties, and the stock rules sample sets all of them. Three keep PMD's default, Pascal case ([A-Z][a-zA-Z0-9_]*):classPattern,innerClassPattern, andenumPattern. Four carry the standards above:testClassPattern([A-Z][a-zA-Z0-9_]*Tests?),interfacePatternandinnerInterfacePattern(I[A-Z][a-zA-Z0-9_]*), andabstractClassPattern(Abstract[A-Z][a-zA-Z0-9_]*).- A trigger's name ends in
Trigger(AccountTrigger), or in the suffix the organisation sets in the rule's property, such assfor a trigger named after the plural of its object (Accounts). An org uses one convention for every trigger, never more. A trigger whose name ends otherwise is reported at severity 3. Enforced byTriggerNameSuffix. The trigger body is one line whatever its name (Triggers). - A trigger action class is named
TA_, the object, an underscore, and what the action does:TA_Account_SetDefaults. The Trigger Actions Framework names its own examples this way (TA_Opportunity_StageInsertRules). A class that serves every object usesSObjectas the object (TA_SObject_PreventDeletionin Preventing Record Deletion). No rule checks this. Reviewers report a name that breaks it at severity 3, which does not block. - A class with the
@RestResourceannotation ends inRestResource, and starts with the object it serves when it serves one:AccountRestResource(RestResource Annotation). The annotation makes the class a REST endpoint, and the name tells a reviewer that callers outside the org can reach it, asControllerdoes for a class that a Lightning component calls. No rule checks this. Reviewers report a name that breaks it at severity 3, which does not block.
Method
- A method's name should start with a verb that says what the method does:
findAccounts,createInvoice,validateAddress,isOverdue,hasAccess, orcanEdit. A name that is only a noun, such asaccounts(), does not say whether the method reads, changes, or checks. No rule checks this. Reviewers do, and it does not block a pull request. - A method that returns a
Booleanshould be named as a question, such asisActiveorhasAccess, notgetActive. A method with no parameters whose name starts withgetand that returns aBooleanis reported at severity 4. Enforced byBooleanGetMethodNameApex.
Test Method
- Underscores in a test method name are allowed and discouraged, and no other method name may contain one. Enforced by
MethodNamingConventions, which allows them in test methods only.
Test methods have long been written with underscores, such as findAccounts_noMatch_returnsEmptyList, so these standards are lenient on purpose, to make adoption easier. An organisation should remove them as it revises its codebase.
Property/Field/Variable
- Property names follow the same camel case convention. A property name that breaks it blocks the pull request (severity 1). Enforced by
PropertyNamingConventions. FieldNamingConventionshas five pattern properties, and the stock rules sample sets each at PMD's default.constantPatternandenumConstantPatternare upper snake case ([A-Z][A-Z0-9_]*), for a constant (static final) and an enum value.finalPattern,instancePattern, andstaticPatternare camel case ([a-z][a-zA-Z0-9]*). So the enum valuePENDING_APPROVALpasses, andApprovedorpendingApprovalblocks the pull request (severity 1).- No Hungarian notation. The name of a variable, field, property, or parameter does not start with a prefix for its type:
familyName, notsFamilyName;member, notoMember;isLegal, notbIsLegal. The prefix makes a name harder to read, and it is unnecessary: an editor such as VS Code shows the type of any variable when the pointer rests on it. The prefixes are also extra characters of Apex, and a very large org can reach the limit on the total amount of Apex code it may hold (Execution Governors and Limits). No rule checks this. Reviewers encourage it, and it does not block a pull request. - A simple accessor is an Apex property (
{ get; set; }). Agetorsetprefix is allowed on a method that fetches or computes a value. No rule can reliably tell an accessor from any other method. - A collection's name should say what it holds. A list is named for its items in the plural (
accounts,relatedContacts). A map is named{value}By{key}(accountsById). A set of record Ids is named for the record in the singular, thenIds(accountIds). No rule checks this. Reviewers do, and it does not block a pull request.
Type/Enum
- A type is written as a class is named, in Pascal case, wherever it is used:
String, notstring, andList, notLIST. The same goes for every other type. Apex reads any case (Formatting), and Prettier leaves a name as it was typed, so the developer writes it. No rule checks this. Reviewers do. - A field or property does not share its name with a method of its class, or with the class itself. Apex compiles both, because methods and variables have their own namespaces within a class (Naming Conventions), and it compares names without regard to case, so a property
Summaryand a methodsummary()have the same name. A reader cannot tell the two apart at a glance. The same goes for a local variable with the name of a class in its file: inLine line = new Line();the variable reads as the class. Each is reported at severity 4. Enforced byFieldNameMatchesMethodNameandFieldNameMatchesClassName.
The platform cannot always tell them apart either. Apex evaluates local variable names before class names, so a local variable with the name of a class hides that class's methods and variables: with a variable named Database in scope, Database.insert(...) no longer compiles (Static and Instance Methods, Variables, and Initialization Code). A variable named after an sObject type hides it the same way, in any case, where the name starts a dotted expression: with String account in scope, Account.SObjectType and the field token Account.Name no longer compile, while Schema.Account.SObjectType and new Account() still do.
- A type suffix on a variable or a class (
accountsToUpdateList,AccountWrapper) is allowed, and never required.
The same conventions by kind of code, each with an example:
| Code type | Naming convention |
|---|---|
| Class | Case: Pascal case (AccountService) |
| Interface | Case: Pascal case. Prefix: I (ILogger) |
| Enum | Case: Pascal case (ShippingStatus) |
| Enum value | Case: upper snake case (IN_TRANSIT) |
| Test class | Suffix: Test or Tests (AccountServiceTest) |
| Batch class | Suffix: Batch (AccountCleanupBatch) |
| Queueable class | Suffix: Queueable (ExternalSyncQueueable) |
| Schedulable class | Suffix: Sched or Schedulable (AccountCleanupSched) |
| Schedulable and Batchable class | Suffix: SchedBatch (AccountCleanupSchedBatch) |
| Constants class | Suffix: Constants (BillingConstants) |
| Abstract class | Prefix: Abstract (AbstractIntegrationService) |
| Exception class | Suffix: Exception (AccountServiceException) |
| Trigger action class | Prefix and pattern: TA_{SObject}_{Action} (TA_Account_SetDefaults) |
| REST resource class | Suffix: RestResource (AccountRestResource) |
| Trigger | Suffix: Trigger, or the one the organisation sets; one convention for the org, never more (AccountTrigger) |
| Method | Case: camel case, starting with a verb (findAccounts) |
| Test method | Case: camel case; underscores allowed and discouraged (findAccounts_noMatch_returnsEmptyList) |
| Property | Case: camel case (accountName) |
| Apex Field/Variable | Case: camel case (retryCount) |
| Constant | Case: upper snake case (MAX_RETRIES) |
| Parameter | Case: camel case (accountIds) |
| Local variable | Case: camel case (openCases) |
| List | Plural noun (relatedContacts) |
| Map | Pattern: {value}By{key} (accountsById) |
| Set of record Ids | Singular noun and Ids (accountIds) |
| Annotation | Case: Pascal case (@IsTest) |
JavaScript¶
TODO
Stub. The naming standards of JavaScript are not written yet.
Visualforce¶
- A Visualforce page or component is named in Pascal case, such as
AccountSummary.
Controller Names¶
The Apex class behind a Visualforce page is named for its role, so that a reader of the class knows that a page is bound to it.
- A custom controller's name ends in
Controller, and a controller extension's name ends inExtensionorExt. They are the classes named in thecontrollerandextensionsattributes of theapex:pagetag (apex:page). A name that ends otherwise is reported at severity 4. Enforced byVisualforceControllerNaming.
A class that a Lightning component calls is named the same way (Naming).
Sources:
- Annotations - Apex Developer Guide
- apex:page - Visualforce Developer Guide
- AuraEnabled Annotation - Apex Developer Guide
- Execution Governors and Limits - Apex Developer Guide
- Naming Conventions - Apex Developer Guide
- RestResource Annotation - Apex Developer Guide
- Schema Namespace - Apex Reference Guide
- Static and Instance Methods, Variables, and Initialization Code - Apex Developer Guide
- System Namespace - Apex Reference Guide
- Trigger Actions Framework
Rules¶
The rules that this page mentions. The Standard column gives each rule's standard in one line, and the Section column links to the section that mentions the rule; "Not yet written" marks a rule of this page that no section mentions yet. Appendix F.6 holds the full cross-reference.
| Rule | Kind | Severity | Standard | Section |
|---|---|---|---|---|
Annotations |
Stock PMD | 1 | Write annotations in Pascal case: @IsTest, not @isTest |
Apex |
Class |
Stock PMD | 1 | Class, interface, and enum names are in Pascal case | Apex |
Field |
Stock PMD | 1 | Field names are in camel case, and constant and enum value names in upper snake case | Apex |
Formal |
Stock PMD | 1 | Parameter names are in camel case | Apex |
Local |
Stock PMD | 1 | Local variable names are in camel case | Apex |
Method |
Stock PMD | 1 | Method names are in camel case; underscores are allowed | Apex |
Property |
Stock PMD | 1 | Property names are in camel case | Apex |
@lwc/ |
Stock Code Analyzer ESLint | 2 | No comment that switches off an ESLint rule for a line | Comments |
@salesforce-ux/ |
Stock Code Analyzer ESLint | 2 | No SLDS linter suppression without an approving review from the owning team | Comments |
Button |
Custom PMD XPath | 2 | Every image button, icon button, and image-map link has a name that a screen reader can announce | Buttons and Links |
Button |
Custom PMD XPath | 2 | Every image button and image-map link in a Visualforce page has a name that a screen reader can announce | Buttons and Links |
Image |
Custom PMD XPath | 2 | Every image has an alternative text attribute | Images |
Image |
Custom PMD XPath | 2 | Every image in a Visualforce page has an alt attribute |
Images |
No |
Custom Code Analyzer Regex | 2 | The author is a person or a team, never a tool default such as ChangeMeIn@UserSettingsUnder.SFDoc |
Comments |
No |
Custom Code Analyzer Regex | 2 | No ESLint suppression without an approving review from the owning team | Comments |
No |
Custom Code Analyzer Regex | 2 | No PMD or Code Analyzer suppression without an approving review from the owning team | Comments |
Type |
Stock PMD | 2 | No class, enum, or interface takes the name of a System or Schema type |
Apex |
No |
Custom PMD XPath | 3 | No one-letter variable names, except i and j as loop counters and e in a catch block |
Apex |
Trigger |
Custom PMD XPath | 3 | A trigger name ends in Trigger, or in the one suffix the organisation sets Notes on this rule. |
Apex |
Aura |
Custom PMD XPath | 4 | A class that a Lightning component calls ends in Controller |
Apex |
Boolean |
Custom PMD XPath | 4 | A Boolean method is named as a question, not with get |
Apex |
Field |
Custom PMD XPath | 4 | A field or property does not take the name of its class, and a local variable does not take the name of a class | Apex |
Field |
Custom PMD XPath | 4 | A field or property does not share its name with a method of its class | Apex |
Image |
Custom PMD XPath | 4 | Only a decorative image has an empty alternative text | Images |
Image |
Custom PMD XPath | 4 | Only a decorative image in a Visualforce page has an empty alt |
Images |
Visualforce |
Custom PMD XPath | 4 | A custom controller's name ends in Controller, and a controller extension's in Extension or Ext Notes on this rule. |
Controller Names |
No |
Custom Code Analyzer Regex | 5 | No TODO, TO-DO, FIXME, FIX-ME, or HACK comment stays in Apex |
Comments |
No |
Custom Code Analyzer Regex | 5 | No TODO, TO-DO, FIXME, FIX-ME, or HACK comment stays in JavaScript |
Comments |
No |
Custom Code Analyzer Regex | 5 | No TODO, TO-DO, FIXME, FIX-ME, or HACK stays in a markup comment |
Comments |