P

Pro Salesforce Development

Battle-tested skill for expert, patterns, salesforce, platform. Includes structured workflows, validation checks, and reusable patterns for development.

SkillClipticsdevelopmentv1.0.0MIT
0 views0 copies

Pro Salesforce Development

A comprehensive skill for Salesforce platform development including Lightning Web Components, Apex controllers, SOQL queries, triggers, and integration patterns using the Salesforce DX toolchain.

When to Use This Skill

Choose this skill when:

  • Building Lightning Web Components with wire service and imperative Apex
  • Writing Apex classes, triggers, and batch jobs
  • Designing SOQL queries with relationship traversals and aggregations
  • Implementing Salesforce integrations via REST/SOAP APIs
  • Setting up CI/CD with Salesforce DX and scratch orgs

Consider alternatives when:

  • Building a standalone web app outside Salesforce → use a general web dev skill
  • Need CRM customization without code → use Salesforce declarative tools
  • Working with MuleSoft integration → use a MuleSoft skill
  • Building a Heroku-connected app → use a Heroku skill

Quick Start

# Set up Salesforce DX project sf project generate --name my-project cd my-project sf org login web --set-default-dev-hub sf org create scratch --definition-file config/project-scratch-def.json --alias dev sf project deploy start --source-dir force-app
// force-app/main/default/lwc/accountList/accountList.js import { LightningElement, wire } from 'lwc'; import getAccounts from '@salesforce/apex/AccountController.getAccounts'; export default class AccountList extends LightningElement { @wire(getAccounts, { searchTerm: '$searchTerm' }) accounts; searchTerm = ''; handleSearch(event) { // Debounce search input clearTimeout(this._timer); this._timer = setTimeout(() => { this.searchTerm = event.target.value; }, 300); } get hasAccounts() { return this.accounts?.data?.length > 0; } }

Core Concepts

Development Model

LayerTechnologyPattern
UILightning Web ComponentsReactive with @wire decorators
ControllerApex Classes@AuraEnabled methods with cacheable flag
Data AccessSOQL/SOSLRelationship queries, aggregate functions
AutomationTriggers + FlowsTrigger framework pattern, bulkified
IntegrationREST/Platform EventsNamed credentials, async processing
TestingApex Test Classes75% coverage required for deployment

Apex Controller with Proper Patterns

public with sharing class AccountController { @AuraEnabled(cacheable=true) public static List<Account> getAccounts(String searchTerm) { String term = '%' + String.escapeSingleQuotes(searchTerm) + '%'; return [ SELECT Id, Name, Industry, AnnualRevenue, (SELECT Id, FirstName, LastName FROM Contacts LIMIT 5) FROM Account WHERE Name LIKE :term WITH SECURITY_ENFORCED ORDER BY Name LIMIT 50 ]; } @AuraEnabled public static Account createAccount(Account acc) { // Check CRUD permissions if (!Schema.sObjectType.Account.isCreateable()) { throw new AuraHandledException('Insufficient permissions'); } insert acc; return acc; } }

Trigger Framework Pattern

// Reusable trigger handler pattern public abstract class TriggerHandler { public void run() { if (Trigger.isBefore && Trigger.isInsert) beforeInsert(Trigger.new); if (Trigger.isBefore && Trigger.isUpdate) beforeUpdate(Trigger.new, Trigger.oldMap); if (Trigger.isAfter && Trigger.isInsert) afterInsert(Trigger.new); if (Trigger.isAfter && Trigger.isUpdate) afterUpdate(Trigger.new, Trigger.oldMap); } protected virtual void beforeInsert(List<SObject> newList) {} protected virtual void beforeUpdate(List<SObject> newList, Map<Id,SObject> oldMap) {} protected virtual void afterInsert(List<SObject> newList) {} protected virtual void afterUpdate(List<SObject> newList, Map<Id,SObject> oldMap) {} } // Account-specific handler public class AccountTriggerHandler extends TriggerHandler { protected override void beforeInsert(List<SObject> newList) { for (Account acc : (List<Account>) newList) { if (acc.Industry == null) acc.Industry = 'Other'; } } } // Trigger (one-liner) trigger AccountTrigger on Account (before insert, before update, after insert, after update) { new AccountTriggerHandler().run(); }

Configuration

ParameterTypeDefaultDescription
apiVersionstring'60.0'Salesforce API version for metadata
defaultOrgstring''Default scratch org alias
testLevelstring'RunLocalTests'Test execution level on deploy
securityModelstring'with sharing'Default Apex sharing context
triggerFrameworkstring'handler'Trigger pattern: handler, domain, or inline
lwcStylesheetstring'scoped'LWC CSS: scoped or shared stylesheets

Best Practices

  1. Always use WITH SECURITY_ENFORCED or manual FLS checks in SOQL — Field-Level Security is not automatically enforced in Apex. Without explicit security checks, users can access fields they shouldn't see through your custom components.

  2. Bulkify all trigger and batch logic — Triggers can receive up to 200 records at once. Never put SOQL queries or DML statements inside loops. Collect IDs first, query once, then process the results in a map-based lookup pattern.

  3. Use @AuraEnabled(cacheable=true) for read operations — Cacheable methods enable Lightning Data Service caching and @wire reactive binding. Only omit cacheable for methods that perform DML or need real-time data on every call.

  4. Write test classes that assert behavior, not just coverage — Salesforce requires 75% code coverage, but coverage without assertions catches nothing. Assert expected outcomes, error messages, and edge cases. Use @TestSetup methods to create reusable test data.

  5. Use Named Credentials for external callouts — Never hardcode API endpoints or credentials in Apex code. Named Credentials manage authentication (OAuth, basic auth, certificates) declaratively and support environment-specific configuration.

Common Issues

"Too many SOQL queries: 101" governor limit — SOQL queries inside loops hit the 100-query limit with bulk data. Move queries outside loops, use collections to gather IDs, and perform a single bulk query. Use Limits.getQueries() to monitor usage.

LWC @wire not refreshing after DML — Wire service caches results. After creating or updating records with imperative Apex, call refreshApex(this.wiredResult) to force the wire adapter to refetch. Pass the entire wire result object, not just the data.

Mixed DML operations in test classes — Inserting setup objects (User, Profile) and non-setup objects (Account, Contact) in the same transaction throws an error. Use System.runAs() to isolate setup object DML in a separate execution context within your test methods.

Community

Reviews

Write a review

No reviews yet. Be the first to review this template!

Similar Templates