17

Is there a definitive guide somewhere that outlines the best practices for faster executing code?

I am looking for specifics, like

  1. Adding elements to a Set versus to a Map
  2. Testing for null before adding an element to a Set of IDs versus not testing for null, and then using that Set in a query's WHERE clause.
  3. Is it faster to use a trigger with a query to a related object or a workflow field update, where the workflow accesses the related object's fields via the "__r." syntax?

Things like that.

I could test specific scenarios myself, but there are so many, and there are many I am not even thinking about. I was hoping that this is already captured somewhere online.

Thanks.

EDIT: So general consensus seems to be not to micromanage or worry about which data structure would be faster, because performance gains will be minimal, and making the code readable and maintainable is more important. Cool, thanks.

1
  • 2
    welcome to salesforce.stackexchange Kirill. We try to focus on questions about specific problems, which are not prone to discussion or subjectivity. Currently this question is very broad, would you mind considering scoping down to a specific scenario ? (you could opt to ask multiple questions) Commented Jan 7, 2014 at 20:54

4 Answers 4

26

Great question. In opening this can of worms, I would like to noodle the premise if you don't mind :-) just to learn if we're solving performance in the right quadrant. What motivates your question exactly?

easy problem         ^         hard problem
easy solution        |        easy solution
                     |                     
[start here]         |   [graduate to here]
                     |                     
<--------------------+-------------------->
                     |                     
[dragons be here]    |    [data scientists]
                     |                     
easy problem         |         hard problem
hard solution        |        hard solution

Definitely performance is important, but the Force.com platform is pretty good at keeping you within reasonable boundaries. You don't have to worry about nginx vs iis vs apache serving XYZ requests per second. Float above that stuff. Salesforce throws smarts and hardware at those problems so we don't have to.

As a service layer developer, err on the side of inspecting:

  • performance of callouts (web services, third party hooks hanging off your controllers, or crazy stuff living in JavaScript code facing your users via custom buttons, etc)
  • if you're an ISV developing real product, keep swathes of DML out of tests for your patience/sanity's sake, check large inserts and large deletes aren't anywhere near governor limits,
  • check your use of asynchronous tools / set-and-forget methods (like @future, batches, schedules) to detour any heavy lifting away from execution contexts invoked by user interfaces,

Rather than doing legwork for the sake of the Apex runtime, optimize for you the architect, us the developers, them the future maintainers. The Apex runtime will get faster and smarter, you don't need to do it any favours. Principle of least astonishment and semantics wins over tricks every time.

Governor limits are the thoughtful and useful straitjacket that gives us a gentle slap in the face as course correction if code falls outside those reasonable boundaries.

As a client-side developer, invest your valuable time:

  • taking advantage of speedy (JavaScript Remoting) and reactive (Streaming API) features to offer the snappiness (or perceived snappiness) your users expect, decoupled from Apex performance,

  • check the expires attributes of pages holding JavaScript clients, the cache control attributes of static resources (zips of course, concatenated CSS/JS courtesy a non-overkill build script)

  • profile first, shoot later!

7
  • 1
    Great answer, but I tend to disagree on the 'great question' thing, though. Discuss performance and abstraction is pertinent, but not OP's apparent objective. Commented Jan 7, 2014 at 21:39
  • 1
    @AndréSantosdeMedeiros per meta, when you have a sec, you might comment on the question where OP sees your remarks and can react accordingly to flesh the details out? Commented Jan 7, 2014 at 21:49
  • 4
    I think give the level of the question, the level of this answer in terms of general advice and architecture is perfect! Start from this and work down, never optimise at the lowest level first! Commented Jan 7, 2014 at 22:33
  • 1
    upvote for Answer....perfect
    – user4971
    Commented Jan 8, 2014 at 6:49
  • 1
    Can I favorite an answer??? :-) Commented Jan 9, 2014 at 9:42
16

Most general optimizations don't matter, and you really shouldn't worry about performance terribly, as the other answer said, but I'm going to provide some very specific rules that you should follow regardless:

Use Configuration instead of Code, if at all possible.

This is simply because code uses execution time, and configuration does not (the 10,000 ms limit). There is a higher limit of 10 minutes per transaction that does include validations, etc, but those are much harder to reach. Validation rules, workflow rules, sharing rules, and rollup summaries are standard elements that can be replicated in Apex Code, but shouldn't be without reason.

Use Maps instead of Loops, if at all possible.

A for loop inside a for loop uses multiplicative processing time (specifically, x*y processing time), which is a major performance drain if not considered carefully. For example, if you're in a contact trigger, and you query all accounts, and loop through each account to see if it matches the contact, that's bad. 200 accounts and 200 contacts becomes 40,000 loop cycles instead of 200 cycles by using a map. You can, and probably will, time out on larger transactions without the use of maps.

Query before Branches, if at all possible.

You should never write a loop like this:

for(Account record: [SELECT Id, Name, Date__c FROM Account WHERE Id IN :accountIds]) {
    if(record.Date__c != null) {
        ...

When you could reduce the number of rows queried, loop cycles, and so on with this:

for(Account record: [SELECT Id, Name, Date__c FROM Account WHERE Id IN :accountIds AND Date__c != NULL]) {
    ...

The performance benefits are amazing when you do it this way. Also, query time isn't counted towards the execution time of 10,000 ms.

Query only fields you need.

If you know in advance which fields you need, query just those. Don't use dynamic field lists if you can help it, because it will slow down your query time and execution time, increase memory usage, and generally slow things down-- significantly. It can also cause errors in situations that wouldn't occur without dynamic lists.

Never query inside a loop.

You'll probably read about this elsewhere, so I won't say much on it, but remember that you'll hit your limits with a lot less data if you do this. Using maps correctly usually means you won't be doing this anyways, because you'll know better.

Other than these simple guidelines, most other optimizations are more cosmetic than any serious performance gains, and you'll pick those up as you go along.

1
  • I like this, but I'd like to qualify "configuration before code". A lot of things can be accomplished with Process Builder these days, but without the availability of static variables and other things you get with Apex, complex automation ecosystems can really run amok, and a simple bulk update of 20 Opportunities can take minutes to complete. Commented Jun 5, 2020 at 21:37
8

Ways to speed up your queries:

1) Do not use formula fields in filters - they are recalculated on access.

2) If need to filter on a formula field often, could have SF Support index it. This would only be possible for "deterministic" formulas, whose values don't change all the time depending on today's date for example, and which do not have cross-object lookups.

3) Do not use exclusion conditions (!=, NOT), or comparison to NULL ( != NULL). Those cause SF Query Optimizer to not use indexes on indexed fields. Instead, try to use = or IN.

4) Try to use standard indexed fields in filters, like ID, name, createdDate, lastModifiedDate, etc. Also try to use date ranges in the filters, to reduce the number of available records.

5) Do not use the leading % wildcard in the LIKE criteria. e.g. LIKE '%somestring'. This will also cause query optimizer to not use indexes on indexed fields.

6) Use "with sharing" in classes where it makes sense - this will limit the number of rows searched to the ones available to the user through sharing rules, reducing the total number of rows searched, and reducing the length of the query.

0

Best Practices for Apex Classes The differentiate between Good code and Bad code Graceful Design of the Code

BAD CODE:

public class CustomCalendarUtil {

    public static String getMonth(Integer month){
        String varMonth = ‘’;
        if(month==1){
            varMonth = ‘January’;
        } else if(month==2){
            varMonth = ‘February’;
        }
        ..
        ..
        return varMonth;
    }
}

This is functionally correct code but is a maintenance overhead due To many if else statements.

GOOD CODE:

public class CustomCalendarUtil {

    static Map<Integer, String> monthMap = new Map<Integer, String>();

        private static void init(){
        monthMap.put(1, ‘January’);
        monthMap.put(2, ‘February’);
        monthMap.put(3, ‘March’);
    }

    public String getMonth(Integer month){
        return monthMap.get(month);
    }
}

The Complete Detalis @ http://www.solunus.com/2015/10/advanced-apex-training-apex-best-practices/

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .