Wes and I are adding a few more topics to our Salesforce Handbook before we put it to bed and I thought a great topic would be programming best practices. I've never seen a "complete" list of best practices so I thought I would put something together based upon my experiences. I know I've left some out, so if you have any to add, please chime in and we may include them in the book.
- Since Apex is case insensitive you can write it however you'd like. However, to increase readability, follow Java capitalization standards and use two spaces instead of tabs for indentation.
- Use Asychronous Apex (@future annotation) for logic that does not need to be executed synchronous.
- Asychronous Apex should be "bulkified".
- Apex code must provide proper exception handling.
- Prevent SOQL and SOSL injection attacks by using static queries, binding variables or the escapeSingleQuotes method.
- When querying large data sets, use a SOQL "for" loop
- Use SOSL over SOQL where possible - it's much faster.
- Use Apex Limits Methods to avoid hitting governor exceptions.
- No SOQL or SOSL queries inside loops
- No DML statements inside loops
- No Async (@future) methods inside loops
- Do not use hardcoded IDs
- There should only be one trigger for each object.
- Avoid complex logic in triggers. To simplify testing and resuse, triggers should delegate to Apex classes which contain the actual execution logic. See Mike Leach's excellent trigger template for more info.
- Bulkify any "helper" classes and/or methods.
- Trigers should be "bulkified" and be able to process up to 200 records for each call.
- Execute DML statements using collections instead of individual records per DML statement.
- Use Collections in SOQL "WHERE" clauses to retrieve all records back in single query
- Use a consistent naming convention including the object name (e.g., AccountTrigger)
- Do not hardcode picklists in Visualforce pages; include them in the controller instead.
- Mark controller variables as "transient" if they are not needed between server calls. This will make your page load faster as it reduces the size of the View State.
- Use <apex:repeat> to iterate over large collections.
- Use the cache attribute with the <apex:page> component to take advantage CDN caching when appropriate
- Use a consistent naming convention including "Test" and the name of the class being tested (e.g., Test_AccountTrigger)
- Test classes should use the @isTest annotation
- Test methods should craete all data needed for the method and not rely on data currently in the Org.
- Use System.assert liberally to prove that code behaves as expected.
- Test each branch of conditional logic
- Write test methods that both pass and fail for certain conditions and test for boundary conditions.
- Test triggers to process 200 records - make sure your code is "bulkified" for 200 records and doesn't throw the dreaded "Too many SOQL queries: 21" exception.
- When testing for governor limits, use Test.startTest and Test.stopTest and the Limit class instead of hard-coding governor limits.
- Use System.runAs() to execute code as a specific user to test for sharing rules (but not CRUD or FLS permissions)
- Execute tests with the Force.com IDE and not the salesforce.com UI. We've seen misleading code coverage results when running from the salesforce.com UI.
- Run the Force.com Security Source Scanner to test your Org for a number of security and code quality issues (e.g., Cross Site Scripting, Access Control Issues, Frame Spoofing)