# Best Practices
The following is a collection of methods and considerations that we have used in the past to help deliver a successful SF B2B commerce implementation.
It is based on the best practices proposed by the Salesforce B2B Services team, the PMD community and ourselves. The goal is to find the balance between what works OOTB (for ease of support/future proofing) vs. what needs to be customized (for the best customer experience) and deliver the solution following standard practices and patterns.
# Key Definitions
|Design||Specifications for software artifacts needed to accomplish business requirements.|
|UI/UX||User Interface and user experience. Look and feel, style guides, user journeys, page flows, etc.|
|Coding||Custom build of extension programs to accomplish specific tasks.|
|Performance||What is needed to make sure your storefront is responsive enough.|
|Integrations||What SF B2B Commerce needs to talk to to fullfill business requirements.|
|Release Management (CI/CD)||How to better reduce the ordeals of moving code and settings from dev all the way to production.|
# Design Best Practices
- Keep it DRY: Take your time to understand what are the OOTB capabilities of SF B2B Commerce. Leverage the native features in the package and resort to customizations only if necessary.
- Keep it simple: Avoid creating large classes or code with high complexity and logic branches.
- Designs should be scalable and also well fit into the long-term strategy of the business.
- Disable any of the components not in use (e.g. Featured Products, Wishlists, etc. are common ones). Otherwise, they may still call for backend data which may impact performance
- Consider doing capacity planning and integration planning earlier on. Pay special attention to the guardrails document (see link below)
- Consider using a standard modeling language (e.g. UML) as a way to describe design elements.
- Avoid duplicating code across elements. This could be consolidated into a single class and included on all the pages where it is required.
# UI/UX Best Practices
- Make sure you understand the OOTB Front End elements available, and try to leverage them as much as possible. Understand the impact of replacing them with custom functionality or 3rd party frameworks (E.g. switching to bootstrap 4, lightning, react, etc.).
- Have a Style Guide.
- Make sure to cover any variations to the storefront that may be needed for your audience (screen size, device, translations, usability)
- Avoid designing "high definition" screen mockups until the more "basic" mockups are approved.
- Define Customer Journeys for your personas (e.g. starting from an email, into the storefront, all the way into the integration endpoints). Keep in mind that a storefront may have different personas. And their journeys may be different.
- Be focused on what is important for the customer (e.g. the flow of page > transition animations)
- Have a consistent user experience across the site.
# Coding Best Practices
- Change control documentation in place for code promotion as needed.
- Extension logic is aligned with the Design Review and requirements.
- Consider using the current version of the APIs you call (E.g. using ccrz.ccApi.CURRENT_VERSION instead of a fixed version number)
- Code Cleanliness:
- Avoid leftover stubs or test routines in the code
- Avoid uncalled or unneeded procedures or any unreachable code
- Avoid any blocks of repeated code that could be condensed into a single procedure
- Proper use of inline code comments.
- Remove any redundant or unused variables.
- Consider updating or removing any pending TO-DO comments in your code base.
- Efficient usage of SFDC Storage (important for insert/upsert as each SFDC row is 2K).
- Proper consideration of SFDC features such as Process builder, Work Flows or Templates in lieu of APEX coding or triggers.
- If triggers are used, ensure to only have one per object. This will provide the added flexibility of having control over the sequence of operation of the logic executed in the trigger.
- Ensure your code is functionally deconstructed:
- Any modules excessively complex that should be restructured or split into multiple routines
- Wrote external reusable components or library functions when possible
- SOQL queries should be in Data Access Object Apex classes.
- When possible, use static methods and variables instead of instance.
- Confirm that there are no SOQLs inside of loops.
- Every Apex class should have a corresponding test class. The test class should test the behavior of the class being tested. Avoid using "SeeAllData=true" in tests.
- Generally, SalesForce Code Best Practices need to be followed. This is documented here: https://developer.salesforce.com/page/Apex_Code_Best_Practices
- Follow these tips https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/pages_security_tips_intro.htm to maintain the code safe and secured.
- Consider following the PMD Apex Ruleset, which is fully documented at https://pmd.github.io/latest/pmd_rules_apex.html
- Avoid Global Modifiers if possible: Global classes should be avoided (especially in managed packages) as they can never be deleted or changed in signature. Always check twice if something needs to be global. Many interfaces (e.g. Batch) required global modifiers in the past but don’t require this anymore. Don’t lock yourself in.
- Avoid Logic in Triggers: Instead, delegate the triggers work to a regular class (aka trigger handler class). See more here: https://developer.salesforce.com/page/Trigger_Frameworks_and_Apex_Trigger_Best_Practices
- Method Naming Conventions: Method names should always begin with a lower case character.
- Use a namespace/prefix to help identify your customization.
- Consider replacing hardcode values with cc Pagelabels or config settings instead for greater flexibility.
- Consider moving CSS blocks into the Theme files.
- Avoid using inline styles in the HTML.
- Consider using ccLog instead of System.debug, and make sure you are not sending sensitive data to the logs
- Make sure to have applyHtmlTag, showHeader, sidebar and standardStylesheets in your page headers and set to FALSE to prevent default Visualforce behavior.
- Consider using CCRZ.RemoteInvocation instead of VisualForce’s invokeAction for better upgradeability.
- Avoid calling VF action upon page load as the action becomes vulnerable to CSRF. Note that several pages that comes OOTB with SalesForce Communities may trigger this rule (e.g. AnswersHome, CommunityLogin, Bandwidth Exceeded, etc.). Review them to make sure that they are safe.
- Make sure that the Back End also validates any checks that are occurring on the Front End.
- Do not store PCI information in Salesforce, and avoid calling backend functionality to process credit card info. The only allowed info should be non-PCI (e.g. a credit card token instead of the actual credit card number)
# Performance Best Practices
- Avoid DML, SOQL, SOSL Statements in Loops: Avoid DML, SOQL, SOSL statements inside loops to avoid hitting governor limits. Instead, try to batch up the data into a list and invoke your code once on that list of data outside the loop.
- Reduce the size of the images: better compression and adjust the width & length of the images to only what is needed.
- Use a CDN instead of Salesforce static resources.
- Review how the remote actions are being called from your custom page includes. Maybe there is an OOTB object that can be used instead? (or can be easily extended to fulfill your requirements)
- Disable any of the components not in use (e.g. Featured Products, Wishlists, etc. are common ones). Otherwise, they may still call for backend data which may impact performance.
- Make sure your queries returns only what needed.
- Improve the SOQL performance by adding indexes to the objects and LIMITs to the queries.
- For the callouts: If they are blocking the user, review what they are doing and if it is possible to simplify them, cache them or move them to other parts of the page. Consider using Asynchronous Apex e.g. Future, Scheduled, and/or Batch classes and methods to handle large volumes of data in off peak times.
- Consider upgrading to the latest B2B Commerce version and patch to take advantage of any performance enhancements.
- Enable Page Label cache.
- Enable Platform cache to improve static resource caching, configuration data.
- Confirm that "Dev Mode" storefront configuration is turned off in production.
- Make sure that "Compile All Classes" (under
Setup > Apex Classes) AND/OR "Perform Synchronous Compile on Deploy." (under
Setup > Apex Settings) are checked on. These should help reduce some of the high compilation times seen after initial deployments.
# Integrations Best Practices
- Limit the number of synchronous callouts to other systems. This helps keep the time to service requests from users to a minimum.. Consideration should be given to local storage of data returned by callouts (based on access frequency, data volumes, etc.). E.g:
- Data with a very low rate of change may be stored locally in the database and updated via batch on a suitable frequency (e.g. daily or weekly).
- More transient data could be obtained by a synchronous call, but should be cached with a suitable Time To Live (TTL) (e.g. 1 minute, 1 hour). This minimises the number of synchronous calls required.
- Use @Future for callouts for better perceived performance. They are still transactional callouts, but because they can be executed asynchronously.
- Take advantage of bulk or batch APIs.
- Cache aggressively and only query for the content you need.
- When doing data-feed type of integrations, make the extra effort to have incremental vs. total update mechanisms. Having that will save time in the long term.
- Keep things DRY: If there are previous integration efforts made (even for other systems), consider reusing/updating them instead of creating new ones from scratch.
- Avoid over-designing your interfaces, to keep them clean and easier to maintain.
- Avoid creating new custom objects if existing ones can be used (e.g. adding a custom field in an existing cc object) to minimize the need for customization/performance impact
# Release Management (CI/CD) Best Practices
- Use a version control system for your code.
- Within your version control system, use branches to separate your features or releases as needed.
- Enforce code reviews and approvals before promoting it to higher instances.
- Have a rollback mechanism for both metadata and data in case of defects.
- Have dev, qa, test and production environments for your integration endpoints as well.
- Document any pre and post deployment steps needed, as well as verification steps to confirm the code was successfully promoted.
- Have an alert notification system in place, for any status changes (e.g. successful deployment, errors found, etc.) that may occur.
- Automate, automate, automate. Consider investing time in properly setting a CI/CD, and creating scripts where it makes sense.
- Dont' use Changesets. Instead, consider using SFDX's packages (DCP), or at least deployments based on package.xml files.
- Remember that Salesforce is a combination of code/metadata and also data (e.g. configuration settings, test data). Make sure this consideration is included in your process.
- Don't allow live updates of code. All changes should be in the version control code repository.
- If the team/org is mature enough:
- Include configuration settings and other types of records needed for the correct operation of the storefronts, so they can be easily reproducible/modified in other stages (and reducing the list of pre and post deployment steps)
- Automate static code reviews, code coverage, and other tests to reduce the risk of unexpected errors.
# How to enforce best practices?
- Keep things DRY: have standard best practices available to all your projects, and across your whole team, instead of isolated from other teams.
- Have regular meetings with the implementation team to share about best practices and patterns in use or considering using.
- Create a coding best practice document and make sure the team understands it before starting development. Make it part of the onboarding training for all the developers.
- Define a code style standard as well (e.g. tabs instead of spaces, naming conventions, etc.).
- Get familiar with the Code Review culture: all code should be peer reviewed before approved, and properly tested by a non-dev team (developers shouldn't be doing QA on their own code anyways)
- Get the backup of a heavy weight champion, like a high level manager or stakeholder, and avoid rogue team members (regardless they are "rockstars" or not) to increase adoption of standards.
# Using PMD in VSCode
- Apex Coding Best Practices: https://developer.salesforce.com/page/Apex_Code_Best_Practices
- Apex Security tips: https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/pages_security_tips_intro.htm
- Apex Trigger tips: https://developer.salesforce.com/page/Trigger_Frameworks_and_Apex_Trigger_Best_Practices
- PMD rules for apex: https://pmd.github.io/latest/pmd_rules_apex.html
- PMD for vscode: https://github.com/ChuckJonas/vscode-apex-pmd
- B2B Commerce Guardrails cheatsheet: https://resources.docs.salesforce.com/latest/latest/en-us/sfdc/pdf/b2b_commerce_guardrails_cheatsheet.pdf
- Salesforce Query optimizer FAQ: https://help.salesforce.com/articleView?id=000330734&type=1&mode=1