Salesforce Customization Best Practices

Blog / Salesforce · January 23, 2020 · Updated June 10, 2026 · 9 min read
Salesforce Customization Best Practices

Salesforce customization works best when you follow one rule above all others: configure before you customize. Build with declarative (clicks-not-code) tools first — custom fields, page layouts, record types, validation rules, permission sets, and Flow — and reach for Apex, Lightning Web Components (LWC), or integrations only when the platform genuinely cannot meet the requirement. Code is powerful, but every line you add is something you must test, secure, and maintain forever. The teams that get the most from Salesforce keep their org as declarative as possible, govern what they build, and treat customization as a disciplined engineering practice rather than a pile of ad-hoc tweaks.

This guide is the practitioner's playbook for admins, architects, and technical leads on the 2026 Lightning platform. For the business case for customizing Salesforce, see Salesforce customization to boost your business and customers.

Key takeaways

  • Clicks before code: use declarative tools first; write Apex or LWC only when requirements exceed what configuration can do.
  • Flow-first automation: Workflow Rules and Process Builder are retired — build all new automation in Flow.
  • Respect governor limits: bulkify Apex, never put SOQL or DML inside loops, and keep at least 75% test coverage (aim higher).
  • Govern access with permission sets, not profiles; use naming conventions and never hardcode IDs.
  • Ship through sandboxes and source-driven DevOps (Salesforce CLI, DevOps Center) — never change production directly.
  • Extend with AppExchange and the AI layer (Agentforce, Data Cloud, Einstein) before reinventing them in custom code.

The golden rule: configure before you customize

Salesforce is a low-code platform first. Most business requirements — capturing data, enforcing rules, automating steps, controlling who sees what — can be met with configuration alone. Custom code should be a deliberate decision, not a default, because declarative metadata upgrades automatically with each release while code carries a permanent test, security, and maintenance burden.

Use this decision guide to choose the lightest tool that does the job:

Requirement Use configuration (clicks) Use customization (code)
Capture or display data Custom fields, page layouts, Dynamic Forms, record types LWC or Aura for bespoke UI only
Enforce data quality Validation rules, required fields, duplicate rules Apex triggers for cross-object logic
Automate a process Flow (record-triggered, screen, scheduled) Apex when logic exceeds Flow limits
Roll up or calculate Roll-up summary, formula fields Apex for complex cross-object rollups
Control access Permission sets, sharing rules, FLS Apex managed sharing for edge cases
Integrate a system External Services, MuleSoft, Flow HTTP callouts Apex REST and callouts for complex APIs
Add AI Einstein, Agentforce, Prompt Builder Apex or LWC to invoke models in custom flows

A simple test: if a well-trained admin can build it in a sandbox in an afternoon, it should probably be configuration. If it needs loops, complex transactions, external API orchestration, or pixel-level UI, it is a candidate for code.

Automate Flow-first (Workflow Rules and Process Builder are retired)

Salesforce has consolidated automation onto Flow. Workflow Rules and Process Builder have been retired for new automation — Salesforce no longer lets you create new ones and provides migration tools to move existing automation to Flow. For any new requirement, build in Flow, and migrate legacy Workflow Rule and Process Builder automation when you next touch it.

Within the Flow family, pick the right type and know when to drop to code:

Tool Best for Avoid when
Record-triggered Flow Field updates, related-record actions, notifications on create/update Logic needs complex iteration or thousands of related records
Screen Flow Guided UI for users (wizards, intake, service consoles) You need a fully custom, branded UI — use LWC
Scheduled Flow Batch-style nightly processing of records High volume beyond Flow limits — use Apex Batch
Apex Complex transactions, bulk processing, intricate logic, callouts A declarative tool already does it cleanly
LWC Custom, performant, reusable UI components Standard components or Dynamic Forms suffice

Keep automation per object consolidated and predictable: prefer one record-triggered Flow per object per timing (before-save vs after-save) so execution order is clear and debuggable. Use before-save flows for same-record field updates — they are dramatically faster than after-save updates. For the history of the tool Flow replaced, see Process Builder in Salesforce.

Write code that respects governor limits

Salesforce runs in a multi-tenant environment, so it enforces governor limits — hard caps on SOQL queries, DML statements, CPU time, heap, and more per transaction. The most common cause of production failures is code that works on one record but blows a limit when a batch or integration processes 200 at once. Design every trigger and class to be bulk-safe from day one. For the full breakdown of the caps, see Salesforce governor limits.

Core rules:

  • Never put SOQL or DML inside a loop. Query once into collections, process in memory, then run DML once.
  • Bulkify everything — assume any trigger or method may receive up to 200 records (or thousands via Bulk API or batch).
  • One trigger per object, delegating to a handler class so logic is testable and ordered.
  • Keep at least 75% test coverage (the deployment minimum) but treat coverage as a side effect of meaningful tests — assert real outcomes and test bulk (200-record) scenarios, not just the happy path.
  • Use Database methods and try/catch for partial-success handling and graceful errors.

A minimal bulk-safe trigger pattern looks like this:

trigger AccountTrigger on Account (before update) {
    // Delegate all logic to a handler — no business logic in the trigger body.
    AccountTriggerHandler.handleBeforeUpdate(Trigger.new);
}

public with sharing class AccountTriggerHandler {
    public static void handleBeforeUpdate(List<Account> updated) {
        // Collect IDs first, then run ONE aggregate query — no SOQL in a loop.
        Set<Id> accountIds = new Map<Id, Account>(updated).keySet();
        Map<Id, Integer> openCases = new Map<Id, Integer>();
        for (AggregateResult ar : [
            SELECT AccountId aid, COUNT(Id) total FROM Case
            WHERE AccountId IN :accountIds AND IsClosed = false GROUP BY AccountId
        ]) {
            openCases.put((Id) ar.get('aid'), (Integer) ar.get('total'));
        }
        for (Account a : updated) {
            a.Open_Case_Count__c = openCases.containsKey(a.Id) ? openCases.get(a.Id) : 0;
        }
    }
}

Note the with sharing keyword so the class respects the running user's record access — security should be designed in, not bolted on.

Govern access, naming, and configuration for the long term

Use permission sets, not profiles

Salesforce is moving away from profiles as the primary way to grant access. Keep profiles minimal and assign permissions through permission sets and permission set groups. This scales far better: you grant capabilities additively, audit them easily, and avoid the profile sprawl that makes large orgs unmaintainable.

Never hardcode IDs or values

Record type IDs, queue IDs, and configuration values differ between sandbox and production, so hardcoding them guarantees a broken deployment. Store configuration in Custom Metadata Types (deployable and packageable) or Custom Settings, and put user-facing text in Custom Labels for translation and reuse.

Apply naming conventions and document everything

Adopt consistent API names for fields, objects, flows, and classes, and use the Description field on every component to explain its purpose. Self-documenting metadata prevents duplication and saves the next admin hours. Keep a lightweight design doc for any non-trivial customization.

Prefer record types and page layouts over over-customizing

Use record types, page layouts, and Dynamic Forms to tailor the experience for different teams before resorting to custom screens. Over-customizing the UI in code creates fragility that standard configuration avoids.

Design security in: FLS and sharing

Enforce field-level security (FLS), object permissions, and a clear sharing model from the start. In Apex, use with sharing, WITH USER_MODE, and Security.stripInaccessible() so code honours the same access rules as the UI.

Ship through sandboxes and source-driven DevOps

Never build or edit directly in production. Develop in sandboxes (or scratch orgs), track metadata in version control, and deploy through a repeatable pipeline. Use the Salesforce CLI (sf) and DevOps Center for source-driven change management so every change is reviewed, tested, and traceable. This turns customization into proper software engineering: branches, peer review, automated tests, and clean rollbacks.

A practical promotion path: develop in a Developer or Developer Pro sandbox, validate in a Partial or Full sandbox with realistic data, then deploy to production in a release window. Run your full Apex test suite on every deployment.

Extend the modern platform — do not reinvent it

You are customizing Lightning Experience, not Classic — build with Lightning components and design the experience for Lightning. Before writing anything custom, check whether the platform or ecosystem already solves it:

  • AppExchange first: a vetted managed package is often faster and safer than a bespoke build.
  • Use the AI layer rather than rebuilding it: Agentforce (autonomous agents), Data Cloud (unified, real-time data), Einstein, and Prompt Builder let you add AI to your processes without standing up your own model stack. Extend these with Apex or LWC where you need custom behaviour.

Customization in 2026 is as much about integration and orchestration as net-new code — the goal is a maintainable org that still upgrades cleanly three releases from now.

Common pitfalls to avoid

Many customization problems are predictable. For a deeper look at what goes wrong and how to recover, see difficulties in Salesforce implementation and their solutions.

  • Coding what configuration already does (technical debt for no benefit).
  • SOQL or DML in loops and non-bulkified triggers that fail under load.
  • Multiple competing automations on one object with unpredictable order.
  • Tests that chase coverage numbers instead of asserting behaviour.
  • Hardcoded IDs, granting access via profiles, and undocumented metadata.

If you want an experienced team to audit or build your org to these standards, MicroPyramid has delivered 50+ projects over 12+ years — see our Salesforce consulting and customization services.

Frequently Asked Questions

What is the difference between Salesforce configuration and customization?

Configuration means building with declarative, point-and-click tools — custom fields, page layouts, record types, validation rules, permission sets, and Flow — without writing code. Customization means extending the platform with code: Apex, Lightning Web Components, or integrations. The best practice is to configure first and customize only when a requirement genuinely exceeds what declarative tools can do, because configuration is easier to maintain and upgrades automatically with each release.

Should I use Flow or Apex for automation in 2026?

Use Flow for the vast majority of automation — Workflow Rules and Process Builder have been retired, so Flow is the standard declarative automation tool. Reach for Apex only when the logic needs complex iteration, large-scale bulk processing, intricate transactions, or external API orchestration that Flow cannot handle cleanly. Many orgs run a hybrid: Flow for the workflow, with invocable Apex actions for the heavy lifting.

What are Salesforce governor limits and why do they matter?

Governor limits are per-transaction caps Salesforce enforces because it is a multi-tenant platform — limits on SOQL queries, DML statements, CPU time, and heap, among others. They matter because code that works on a single record can fail when a batch processes 200 or more records at once. Writing bulk-safe code (no SOQL or DML in loops, processing collections instead) is what keeps you within these limits.

How much Apex test coverage does Salesforce require?

Salesforce requires at least 75% Apex test coverage to deploy to production, and every trigger must have some coverage. Treat 75% as a floor, not a goal: write tests that assert real business outcomes and include bulk (200-record) scenarios. Coverage should be a by-product of meaningful tests rather than the objective itself.

Why use permission sets instead of profiles?

Salesforce is shifting access control toward permission sets and permission set groups, keeping profiles minimal. Permission sets grant capabilities additively, are easier to audit and reuse, and avoid the unmaintainable profile sprawl that large orgs accumulate. Assigning a shared permission set to many users is far cleaner than cloning profiles for every role variation.

When should I use AppExchange or Salesforce AI instead of custom code?

Check AppExchange before building anything custom — a vetted managed package is often faster to deploy and better maintained than a bespoke equivalent. Likewise, use the built-in AI layer (Agentforce, Data Cloud, Einstein, Prompt Builder) to add intelligence rather than standing up your own model stack. Write custom Apex or LWC to extend these tools, not to replace what the platform already provides.

Share this article