B W Base Coding Standards
B W Base Coding Standards
Contents ii
1 Introduction 1
1.1 Purpose of this Document . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
2 Formatting 2
2.1 Method Naming . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
2.2 Whitespace . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
2.3 Brackets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
2.4 Lists . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
2.5 Variable Declaration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
2.6 Loops . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
2.7 Containers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
2.8 Static Variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
2.9 Comments . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
2.10 Unused Code . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
2.11 SOQL Queries . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
3 Implementation Standards 9
3.1 Implementation Complexity . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
3.2 Dataflow . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
3.3 Classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
3.4 Prohibitions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
3.5 Triggers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
3.6 API Design Guidelines . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
4 Unit Testing 18
4.1 Basics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
4.2 Code Coverage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
4.3 Goals of Unit Tests . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
4.4 Test Assertions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
4.5 Triggers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
Bibliography 21
ii
Chapter 1
Introduction [Intro.Scope]
(1)
The language in this document follows the ISO Directive standards as such:
1
Chapter 2
Formatting [Format]
(2)
This section is designed to provide a consistent style for all Bluewolf developers. If a developer
cannot find an answer to their styling question they should fall back on the Google Java Style
guidelines. However this document is considered authoritative where the two differ.
Example:
void assignTeamMembersToAccounts(List<Account> accounts)
2 Method names shall describe the functionality of the method
Example:
void assignTeamMembersToAccounts(List<Account> accounts)
The method should only assign an account team members to accounts and should have no
other side effects
3 Methods should do their best to indicate if side effects occur
Example:
void upsertAccountTeamMembers()
Example:
static testMethod void whenNoAccountTeamMembers_nothingHappens()
2
2.2 Whitespace [Format.Whitespace]
(2.2)
1 All code shall use 4 spaces for indentation
2 There shall be no whitespace beyond the last non-whitespace character on the line
3 If the line has no-content; then it shall have no whitespace other than the carriage return
4 If the file has mixed white space the developer shall convert the file prior to making any edits
5 The file shall be committed to the repository prior to any further work being done
6 A developer should try to keep all code within 120 columns
This rule arises for a few reasons:
• When reviewing code it’s easier to read if the code does not wrap
• Having long lines makes the code more difficult to manipulate
Example:
Bad:
for(Integer i = 0; i < foo.size(); ++i){
foo[i].bar();
foo[i].foobar();
}
Good:
for(Integer i = 0; i < foo.size(); ++i){
Barfoo fooItem = foo[i];
fooItem.bar();
fooItem.foobar();
}
4 Loops shall not contain:
• DML Statements
• SOQL
• SOSL
• @future methods
• Web Service callouts
• Methods that invoke any of the aforementioned
The aforementioned are all governor limited resources and should be used sparingly. Code
should be written in bulk safe ways, using a governor limited resource prevents this and creates
artificial limits on performance which are unnecessary.
[ Note: This requirement is relaxed if and only if there is no other possible implementation
of the requirement. In general a developer should push back on any requirement that can only
be implemented in such a manner. — end note ]
5 If a developer is required for the purposes of implementation to violate the prior prohibition, the
developer shall write the code in such a way as to fail gracefully as soon as the developer can
verify that the limits will be breached.
This shall mean one of the following:
• Failing as soon as the developer can calculate that limits will be breached if execution is
attempted.
• Failing during execution right before the limit is reached, allowing for partial success.
• Ignoring data that would result in a breach of the limits
• Using Apex Queuable to separate the data out into chunks that will not violate the limits.
A developer should try to ensure that they keep mutability of references to a minimum. This
assists in reasoning about the lifetime of a static and allows a developer to remove unnecessary
null checks.
Example:
List<Account> accountsWithNewOwners =
[SELECT
Name,
OwnerId,
MailingAddress
FROM Account
WHERE Owner_Changed__c = true];
2 A Developer should not include Id in the list of fields queried for unless it is the only field being
queried for.
The Id field is always included with the results of every query, explicitly adding it is unnec-
essary in Apex.
3 A Developer shall not query for records that that will only be used for update.
If the related records do not have data that is necessary to use as part of the update it is
not necessary to query for the record to ensure it is in the database unless the lookup field is a
"Pseudo lookup" (18 character string field). Because the Salesforce database is a true relational
store, lookup fields to deleted records will be nulled out automatically if the related record is
deleted. The practice of querying for Ids for related records to get a valid sObject has been
unnecessary for a long time. Instead create a new object of the type to update, set Id to the
value of the object to update and then update the necessary fields.
Example:
final List<Account> accountsForAddressMirroring = new List<Account>();
for(Contact updatedContact : updatedContacts){
accountsForAddressMirroring.add(
new Account(
Id = updatedContact.AccountId,
MailingStreet = updatedContact.Street,
MailingState = updatedContact.State,
MailingPostalCode = updatedContact.PostalCode
MailingCountry = updatedContact.Country,
MailingCity = updatedContact.City
)
);
}
if(accountsForAddressMirroring.isEmpty()){
return;
}
update accountsForAddressMirroring;
Chapter 3
(3)
9
[ Note: This requirement is relaxed if and only if the class is a utility that needs to inherit
sharing from the calling context. — end note ]
Example:
public with sharing class AccountTeamServices{
// do with sharing stuff Here
This means that any super-type should be replaceable with any of its subtypes without the
calling code needing to alter behavior; on the basis that the visible side effects to the caller
should not change.
1 Classes that implement an interface or extend an abstract class shall follow the Liskov Substitution
Principle.
2 In general a developer shall favor composition over inheritance when extending behaviors.
The IS-A relationship enforced by inheritance is often not desired. Composition however
reflects a HAS-A relationship which is far more common. By using composition instead of
inheritance a developer makes the relationships between parent and child clear.
Example:
static testMethod void coverage(){
foo();
}
This code does nothing and tests nothing. As such it exists purely to drive up the reported
code coverage in the organization. Implementing such code is a disservice to our clients as it
creates the illusion of reliability and a well tested codebase.
trigger
handler
ServiceA ServiceB
Handler:
public with sharing class FooTriggerHandler{
// used for testing triggers when the
// setup DML would interfere with the tests
@testVisible static Boolean bypassTrigger = false;
final List<Foo__c> newRecords;
final Map<Id, Foo__c> oldRecords;
public FooTriggerHandler(
List<Foo__c> newRecords,
Map<Id, Foo__c> oldRecords) {
this.newRecords = newRecords;
this.oldRecords = oldRecords;
}
class Sorter{
Boolean ascendingSort = false;
/**
* @description Controls how the data is sorted as set
* by the SortMode enumeration
*/
public SortMode sortingMode{
get;
set{
ascendingSort = sortingMode == SortingMode.Ascending;
}
}
/**
* @description Gets the names of the contacts in ascending order.
* Pure, has no side effects.
* @return The contacts’ full names in descending order
*/
public List<String> getNamesAscending(){
Sorter sort = new Sorter();
sort.sortingMode = SortMode.Descending;
return sort.doSort(names);
}
Another solution to the boolean trap problem is to split the method into two signatures that
indicate the difference in functionality.
/**
* @description Builds test data records
* @param recordCount The number of records to build
* @param objectType The sObjectType of the records to build
* @param fieldToValue A map of sObjectField to the value or
* value provider to override the default fields provided in
* the RequiredFieldsCache
* @return The list of built objects that are not yet inserted
*/
public static List<SObject> build(
Integer recordCount,
Schema.SObjectType objectType,
Map<Schema.SObjectField, Object> fieldToValue){
// build logic here
}
2 In the case where an existing method with a Boolean Trap (3.6.1.1) must be called the developer
shall annotate the parameter to describe the boolean parameter.
Example:
List<Database.SaveResult> results = createObjects(false /*all or none*/);
Chapter 4
(4)
The following sections are relevant specifically to Apex Testing and are supplementary to any
guide provided in the Developer Guide.
Test.startTest();
insert newFoos;
Test.stopTest();
18
4.3 Goals of Unit Tests [Testing.Results]
(4.3)
1 Tests should be targeted at verifying behaviors dictated by requirements not implementation.
While there are cases where testing implementation details must be done, in general an imple-
mentation should not require this.
Good Example:
System.assertEquals(
null,
foo,
’When an invalid parameter is passed the returned Foobar ’ +
’should be null’);
This assertion message is good because it explains what should trigger the expected behavior
and what the expected behavior is.
Bad Example:
System.assertEquals(
null,
foo,
’The returned Foobar should be null’);
This is a an example of a bad assertion message, it says the returned value should be null
but not why. Thus it fails to document what triggers this behavior. Documenting the why of a
behavior is important because it makes it clear what a developer should expect when interacting
with that API.
4 The size of a returned result list shall be verified prior to asserting on the contents
Example:
final Integer EXPECTED_NEW_ACCOUNTS = 5;
List<Account> createdAccounts =
[SELECT Name, Phone, ShippingStreet FROM Account];
System.assertEquals(
EXPECTED_NEW_ACCOUNTS,
createdAccounts.size(),
’The correct number of accounts should be created ’ +
’when a new customer is added’);
for(Account newAccount : createdAccounts){
// verify details of created objects here
}
4.5 Triggers [Testing.Triggers]
(4.5)
1 All triggers shall be tested using bulk data
This shall mean Limits.getLimitQueries() + 1 records
2 If there are legacy triggers that prevent the aforementioned limit from being used, then the
developer shall ascertain the upper bound on which the system can tolerate. This limit shall be
documented at the point where the bulk record count variable is defined.
Example:
// Current limit due to foo.trigger
static final NUM_TEST_RECORDS = 5;
// static final NUM_TEST_RECORDS = Limits.getLimitQueries() + 1
3 A Trigger Handler 3.5.1 is considered to be tested by the trigger tests and should not require its
own tests
Bibliography
[1] Barbra Liskov. “A behavioral notion of subtyping.” In: ACM Trans. Program. Lang. Syst.
16.6 (), pp. 1811–1841.
21