Helping Admins Makes Us Better
Developers
Jeff May
Independent Consultant
@JeffMTI
Jeff May
Independent Consultant
Traditional Development vs Salesforce
Traditional Development
Salesforce Development
1. All layers of the application
1. External Data Layer and Presentation Layer
2. Total control over Business Processes
2. Inserted into external Business Processes
3. Once installed, the application runs
3. Application needs Admin configuration
User Relationships
Executives
Managers
Admins
Developers
Users
The Truth about being a Salesforce Developer
No one is impressed with your programming skills
No one worries about whether it was easy or hard to do
No one cares which Salesforce features you used
Everyone measures your success by whether they get the functionality they need
Design Decision: Which feature(s) to use
Formula Fields
Process & Flow
Trigger / Apex
Every record has the
correct value
Self-documenting
Complex algorithms
Easy to update / test
Easy conditional execution
First in the Execution Order
Reduce SOQL calls
Access other Configuration
features
No Admin Control
Formula Fields
Address:
BillingCity + ', ' + BillingState + ' ' + BillingCountry
Sales Tax:
CASE(BillingState, 'MA', 0.05, 'RI', 0.07, 0.0)
Related Record Info:
Account.Owner.IsActive
Process & Flow
Lightning Processes
Flows
IF / THEN / ELSE
Branching code
Related Record Field Updates
Loops
Quick Actions
Access to any records
Flows
Multiple object create / update
Trigger: set all Contact Owners to Account Owner
trigger AccountTrigger on Account (after update) {
Map<Id, Id> acctIdToOwnerIdMap = new Map<Id, Id>();
for (Account a : trigger.new){
// if this Account's Owner changed, change all the Contact owners to that same Owner
if (a.OwnerId != trigger.oldMap.get(a.Id).OwnerId ){
acctIdToOwnerIdMap.put(a.Id, a.OwnerId);
}
}
// if any Accounts have changed owners, we need to find/update all Contacts
if (acctIdToOwnerIdMap.size() > 0){
List<Contact> contactsToUpdate = new List<Contact>();
for (Contact c : [select Id, AccountId, OwnerId from Contact where AccountId in :acctIdToOwnerIdMap.keySet()]){
if (c.OwnerId != acctIdToOwnerIdMap.get(c.AccountId)){
c.OwnerId = acctIdToOwnerIdMap.get(c.AccountId);
contactsToUpdate.add(c);
}
}
if (contactsToUpdate.size() > 0){ update contactsToUpdate;}
}
}
Process: set all Contact Owners to Account Owner
Goal: Create an Order from an Opportunity
Opportunity
Order
Line Item
Line Item
Line Item
Line Item
Line Item
Line Item
PriceBookEntry
PriceBook
Product
Flow: create Orders from an Opportunity
Development Practices For Happy
Admins
1. Formula Fields
2. User Fields
3. Custom Settings
Formula Fields – Control Business Logic
trigger OpportunityTrigger on Opportunity (before update) {
Map<Id, Opportunity> oppMapForAutoCloseProcessing = new Map<Id, Opportunity>();
for (Opportunity o : trigger.new) {
if (!o.isClosed && (o.RecordTypeId != null) && (o.CampaignId != '70150000000SjkF')) {
oppMapForAutoCloseProcessing.put(o.Id, o);
}
}
// Process the Opps
}
Formula Fields – Define the Field
Formula Fields – Control Business Logic
trigger OpportunityTrigger on Opportunity (before update) {
Map<Id, Opportunity> oppMapForAutoCloseProcessing = new Map<Id, Opportunity>();
for (Opportunity o : trigger.new) {
if (o.Opp_Needs_Closing__c) {
oppMapForAutoCloseProcessing.put(o.Id, o);
}
}
// Process the Opps
}
Formula Fields – Save SOQL Calls
trigger ContactTrigger on Contact (after update) {
for (Contact c : trigger.new){
if (c.AccountId != null){
acctIdsToGet.add(c.AccountId);
}
}
for (Account a : [select Id, OwnerId from Account where Id in :acctIdsToGet]){
acctIdToOwnerIdMap.put(a.Id, a.OwnerId);
ownerIdsToGet.add(a.OwnerId);
}
Map<Id, User> ownerIdToUser = new Map<id, User>([select Id from User where Id in :ownerIdsToGet
and IsActive = true]);
for (Contact c : trigger.new){
// Do Work
}
// Do Work
}
Formula Fields – Define the Field
Formula Fields – Save SOQL Calls
trigger ContactTrigger on Contact (after update) {
for (Contact c : trigger.new){
if (c.AccountId != null){
acctIdsToGet.add(c.AccountId);
}
}
for (Account a : [select Id, OwnerId from Account where Id in :acctIdsToGet
and Owner_is_Active__c = true]){
acctIdToOwnerIdMap.put(a.Id, a.OwnerId);
}
for (Contact c : trigger.new){
// Do Work
}
// Do Work
}
User Fields
trigger OpportunityTrigger on Opportunity (before delete) {
if (trigger.isDelete){
try{
Map<Id, User> userlist = new Map<Id, User>([select id, Name from User
where username in ('[email protected]',
'[email protected]')]);
for (Opportunity opp : trigger.old){
if (opp.Type == 'Renewal'){
if (!userlist.containskey(UserInfo.getUserId())){
opp.addError('Only Special Users can delete this Opportunity');
}
}
}
} catch (Exception e){
}
}
}
Configure the User Field
Review a User Field
User Fields in the Code
trigger OpportunityTrigger on Opportunity (before delete) {
if (trigger.isDelete){
try{
Map<Id, User> userlist = new Map<Id, User>([select id, Name from User
where Can_Delete_Opps__c = true)]);
for (Opportunity opp : trigger.old){
if (opp.Type == 'Renewal'){
if (!userlist.containskey(UserInfo.getUserId())){
opp.addError('Only Special Users can delete this Opportunity');
}
}
}
} catch (Exception e){
}
}
}
Custom Settings
What they are
Why you want them
Like Custom Objects
You deploy code when YOU want
Apply to All users, or Role-specific
Can control logging and business logic
Admin-controlled
Do not count against SOQL limits
Custom Settings - Defining
Custom Settings - Configuring
Custom Settings – Control Deployed Code
trigger OpportunityTrigger on Opportunity (before delete) {
if (trigger.isDelete){
try{
Map<Id, User> userlist = new Map<Id, User>([select id, Name from User
where Can_Delete_Opps__c = true)]);
for (Opportunity opp : trigger.old){
if (opp.Type == 'Renewal'){
if (!userlist.containskey(UserInfo.getUserId())){
opp.addError('Only Special Users can delete this Opportunity');
}
}
}
} catch (Exception e){
}
}
}
Custom Settings - Control Execution
trigger OpportunityTrigger on Opportunity (before delete) {
Trigger_Control__c tc = Trigger_Control__c.getInstance('OpportunityTrigger');
if (!Test.isRunningTest() || (tc != null && tc.Disabled__c)) { return;}
if (trigger.isDelete){
try{
Map<Id, User> userlist = new Map<Id, User>([select id, Name from User
where Can_Delete_Opps__c = true)]);
for (Opportunity opp : trigger.old){
if (opp.Type == 'Renewal'){
if (!userlist.containskey(UserInfo.getUserId())){
opp.addError('Only Special Users can delete this Opportunity');
}
}
}
} catch (Exception e){
}
}
}
Custom Settings - Control Business Logic
trigger OpportunityTrigger on Opportunity (before delete) {
Trigger_Control__c tc = Trigger_Control__c.getInstance('OpportunityTrigger');
if (Test.isRunningTest() || (tc != null & tc.Disabled__c)) { return;}
if (trigger.isDelete){
try{
Map<Id, User> userlist = new Map<Id, User>([select id, Name from User
where Can_Delete_Opps__c = true)]);
for (Opportunity opp : trigger.old){
if (opp.Type == 'Renewal'){
if (!userlist.containskey(UserInfo.getUserId())){
opp.addError('Only Special Users can delete this Opportunity');
}
}
}
} catch (Exception e){
}
}
}
Custom Settings - Configuring
Custom Settings - Control Business Logic
trigger OpportunityTrigger on Opportunity (before delete) {
Trigger_Control__c tc = Trigger_Control__c.getInstance('OpportunityTrigger');
if (Test.isRunningTest() || (tc != null & tc.Disabled__c)) { return;}
Opp_Processing__c op = Opp_Processing__c.getInstance(‘Default');
if (trigger.isDelete){
try{
Map<Id, User> userlist = new Map<Id, User>([select id, Name from User
where Can_Delete_Opps__c = true)]);
for (Opportunity opp : trigger.old){
if (opp.Type == op.Type_Value_for_Renewal__c){
if (!userlist.containskey(UserInfo.getUserId())){
opp.addError('Only Special Users can delete this Opportunity');
}
}
}
} catch (Exception e){
}
}
}
Custom Settings – Admin View
Custom Settings - Control Integrations
trigger CaseTrigger on Case (after insert) {
Integration_Control__c ic;
if ([SELECT Id, IsSandbox FROM Organization LIMIT 1].IsSandbox){
ic = Integration_Control__c.getInstance('Testing');
} else {
ic = Integration_Control__c.getInstance('Production');
}
// Do Work
}
Getting Started as a Salesforce Developer
What you need
What it Costs
1) A Salesforce org to work in
2) Training on Developer Tools
3) Training on Salesforce features
4) API Documentation
5) Sample code and ‘snippets’
6) Assistance and mentoring
0
Developer Community
https://developer.salesforce.com
Trailhead
https://developer.salesforce.com/trailhead
To Recap
Your Relationship with your Admins is crucial to your success
Use the right tool for each project
Use Formula Fields to add Flexibility and Reduce SOQL
Use Custom Data fields and Custom Settings to give Admins control of your code
Use the Free Developer Community Resources to become Great!
© Copyright 2026 Paperzz