Defensive Apex Programming
-
Upload
salesforce-developers -
Category
Technology
-
view
1.013 -
download
0
Transcript of Defensive Apex Programming
Defensive Apex Programming
Dan Appleman
CTO – Full Circle Insights, Author of Advanced Apex Programming
@danappleman
The Software Application Life-Cycle - Traditional
Requirements
Design
Code
Test/QA/Doc
Maintenance
• Bug fixes
• New OS version/browser
• Platform changes
• Regression testing of
changes by developers
• Critical features
The Software Application Life-Cycle – Salesforce Platform
Requirements
Design
Code
Test/QA/Doc
Maintenance
• Bug fixes
• Platform updates (3x per year)
• Regression testing of changes
by developers
• Critical features
• Metadata changes that
change the behavior of the
code.
Traditional Applications
Platform
Your App
Platform
Apex
Other declarative
metadata changes
Apex Applications
Making a Future Call – The Wrong Way
public void CallFuture1()
{
vulnerableFutureCall();
}
@future
public static void vulnerableFutureCall()
{
// Do something here
}
Making a Future Call – The Wrong Way
public void CallFuture1()
{
vulnerableFutureCall();
}
@future
public static void vulnerableFutureCall()
{
// Do something here
}
• Other app calls this in a future context?
• Batch calls this?
• Other code makes future calls and we hit limit?
• An error occurs during the future call?
(you’ll never know it)
Defensive Future Callspublic void CallFuture2()
{
if(System.isFuture() || system.isBatch()) defensiveFutureCallSync();
else {
if(Limits.getFutureCalls()< Limits.getLimitFutureCalls())
defensiveFutureCallAsync();
else { // ????
}
}
}
@future
public static void defensiveFutureCallAsync()
{
defensiveFutureCallSync();
}
public static void defensiveFutureCallSync()
{
// Do something here
}
• Backs up to sync pattern (if applicable)
• Alternatives include try/catch & logging errors
instead
• Reliable async is a much bigger topic...
Using a Custom Setting – The Wrong Way
Boolean enabled = ConfigSettings__c.getInstance('default').Application_Enabled__c;
Guaranteed null reference exception on:
• Uninitialized orgs/sandboxes (metadata push)
• SeeAllData false tests on methods that don’t initialize setting
• SeeAllData false tests on managed package tests that can’t initialize
setting.
Defensive Custom Settings
private static ConfigSettings__c testConfig;
public static ConfigSettings__c getConfig()
{
if(Test.isRunningTest() && testConfig!=null) return testConfig;
ConfigSettings__c result = ConfigSettings__c.getInstance('default');
if(result==null)
{
result = new ConfigSettings__c(name='default', Application_Enabled__c= false);
}
if(Test.isRunningTest()) testConfig = result;
return result;
}
Then...
Boolean enabled = ConfigurationClass.getConfig().Application_Enabled__c;
• Can’t return null
• Protects from DML
errors on parallel tests
• Simplifies test setup
Updating Records – Watch For Concurrency Errors
update listOfRecords;
or
List<Database.SaveResult> dmlResults =
Database.Update(listOfRecords, false);
for(Integer x = 0; x< ops.size(); x++)
{
Database.SaveResult sr = dmlResults[x];
if(!sr.isSuccess())
{
for(Database.Error err: sr.getErrors())
{
if(err.getStatusCode() == StatusCode.UNABLE_TO_LOCK_ROW)
{
// Concurrency error
}
}
}
}
Variations include
• try/catch blocks
• setSavePoint/rollBack
• all or nothing
• Logging vs retry
Continuous Integration – In Traditional Software Development
Software
repositoryJenkins Build
Developer
Developer
Developer
Continuous Integration – In Salesforce Too!!!!!!!!!
Org
Developer
Developer
Developer
CI app
Managed PackageCI App