Sign In Start Free Trial
Account

Add to playlist

Create a Playlist

Modal Close icon
You need to login to use this feature.
  • Book Overview & Buying Mastering Apex Programming
  • Table Of Contents Toc
  • Feedback & Rating feedback
Mastering Apex Programming

Mastering Apex Programming - Second Edition

By : Paul Battisson
4 (1)
close
close
Mastering Apex Programming

Mastering Apex Programming

4 (1)
By: Paul Battisson

Overview of this book

Applications built on the Salesforce platform are now a key part of many organizations' IT systems, with more complex and integrated solutions being delivered every day. As a Salesforce developer working with Apex, it is important to understand the range and variety of tools at your disposal, how and when to use them, and what the best practices are. This revised second edition includes a complete restructuring and five new chapters filled with detailed content on the latest Salesforce innovations including integrating with DataWeave in Apex, and utilizing Flow and Apex together to build scalable applications with Administrators. This Salesforce book starts with a discussion around common mistakes, debugging, exception handling, and testing. The second section focuses on the different asynchronous Apex programming options to help you build more scalable applications, before the third section focuses on integrations, including working with platform events and developing custom Apex REST web services. Finally, the book finishes with a section dedicated to profiling and improving the performance of your Apex including architecture. With code examples used to facilitate discussion throughout, by the end of the book you will be able to develop robust and scalable applications in Apex with confidence.
Table of Contents (28 chapters)
close
close
1
Section 1: Triggers, Testing, and Security
8
Section 2: Asynchronous Apex
13
Section 3: Integrations
21
Section 4: Apex Performance

Hardcoding

The final common mistake I want to discuss here is that of hardcoding within Apex—particularly, hardcoding any type of unique identifier such as an ID or a name. For IDs, this mistake is probably quite obvious for most developers as it is well established that between different environments, including sandbox and production environments, IDs can—and should—differ. If you are creating a sandbox from a production environment, then at the time of creation, the IDs are synchronized for any data that is copied down to the sandbox environment. Following this, IDs do not remain synchronized between the two and are generated when a record is created within that environment.

Despite this, many developers, particularly those working within a single environment such as consultants or in-house developers, will hardcode certain IDs if needed. For example, consider the following code:

for(Account acc : Trigger.new) {
    if(acc.OwnerId = 'SOME_USER_ID') {
        break;
    }
    //do something otherwise
}

This code is designed to skip updates on Account records within our trigger context owned by a particular user, most commonly an application programming interface (API) user or an integration user. This pattern enables a developer to filter these records out so that if an update is coming via an integration, actions are skipped and the integration can update records unimpeded.

Should this user ID change, then we will get an error or issue here, so it is wise to remove this hardcoded value. Given that the ID for the user should be copied from production to any sandboxes, you may ask why this is needed. Firstly, there is no guarantee that the user will not be changed for the integration, going forward. Secondly, for development purposes, when initially writing this code, the user will not likely exist in the production org, and so, in your first deployment, you will have to tweak the code to be environment specific. Thirdly, this also limits your ability to test the code effectively, going forward. We will see how shortly.

As an update to this code, some may recommend making the following change (note that this is precisely the instance where we would extract this query to a utility class; however, we are inlining the query here for ease of display and reading):

for(Account acc : Trigger.new) {
    User apiUser = [SELECT Id FROM User WHERE Username = '[email protected]'];
    if(acc.OwnerId == apiuser.Id) {
        break;
    }
    //do something otherwise
}

This code improves upon our previous code in that we are no longer hardcoding the user record ID, although we are still hardcoding the name. Again, should this change over time, then we would still have an issue, such as when we are working in a sandbox environment and the sandbox name is appended to the username. In this instance, the code would not be executable, including in a test context, without updating the record to be correct. This could be done manually every time a new sandbox is created or through the code in the test. This means that our test is now bound to this user, which is not a good practice for tests, should the user change again.

In this instance, we should remove the name string to a custom setting for flexibility and improved testability. If we were to define a custom setting that held the value (for example, Integration_Settings__c), then we could easily retrieve the custom setting and the username for a query at runtime without the query, as follows:

for(Account acc : Trigger.new) {
    Integration_Settings__c setting = Integration_Settings__c.
      getInstance();
    List<User> apiUsers = [SELECT Id FROM User WHERE Username = 
      :setting.Api_Username__c LIMIT 1];
    if(acc.OwnerId == apiUsers[0]?.Id) {
        break;
    }
    //do something otherwise
}

Such a pattern allows us many benefits, as follows:

  • Firstly, we can now apply different settings across the org and profiles, should we so desire. This can be increasingly useful in orgs where multiple business units (BUs) operate.
  • Secondly, for testing, we can create a user within our test data and assign their username to the setting for the test within Apex. This allows our tests to run independently of the actual org configuration and makes processes such as continuous integration (CI) simpler.
  • We have utilized our safe navigation operator and retrieved a list of users (limiting to a size of one) to ensure that if no such user is found, we avoid any other exceptions such as our NullPointerException instance, as discussed previously.

There is, however, another fundamental fix we should make on our code—have you spotted it?

We should not be doing a query in a loop! Congratulations to the eagle-eyed readers who have been paying attention throughout and spotted the issue already. It is important when looking at some code that we do not overly focus on a single issue and fail to spot another one that may be far more troublesome. Our final improved code would, then, look like this:

Integration_Settings__c setting = Integration_Settings__c.
  getInstance();
List<User> apiUsers = [SELECT Id FROM User WHERE Username = :setting.Api_Username__c LIMIT 1];
for(Account acc : Trigger.new) {
    if(acc.OwnerId == apiUsers[0]?.Id) {
        break;
    }
    //do something otherwise
}

Finally, we could extract both of these lines (the retrieval of the custom setting and the user query) to a utility class to abstract away, for the entire transaction to use as needed.

Visually different images
CONTINUE READING
83
Tech Concepts
36
Programming languages
73
Tech Tools
Icon Unlimited access to the largest independent learning library in tech of over 8,000 expert-authored tech books and videos.
Icon Innovative learning tools, including AI book assistants, code context explainers, and text-to-speech.
Icon 50+ new titles added per month and exclusive early access to books as they are being written.
Mastering Apex Programming
notes
bookmark Notes and Bookmarks search Search in title playlist Add to playlist font-size Font size

Change the font size

margin-width Margin width

Change margin width

day-mode Day/Sepia/Night Modes

Change background colour

Close icon Search
Country selected

Close icon Your notes and bookmarks

Confirmation

Modal Close icon
claim successful

Buy this book with your credits?

Modal Close icon
Are you sure you want to buy this book with one of your credits?
Close
YES, BUY

Submit Your Feedback

Modal Close icon
Modal Close icon
Modal Close icon