Apex in Salesforce: language, uses, and best practices
Understand what Apex is in Salesforce, when to use Apex or Flow, and how to govern code, integrations, security, and Agentforce in enterprise projects.
Apex in Salesforce: what the programming language is and how to use it in enterprise projects
Apex is Salesforce's proprietary programming language, used to implement business logic that declarative configuration and Flow do not address well. In an enterprise operation, the decision to write Apex is not merely technical: it creates a software asset that must be governed, tested, versioned and maintained for years. This article explains what the Apex language is, when it is the right choice instead of Flow, how to structure code for scale and security, and how to prepare reusable Apex actions for AI agents with Agentforce.
What is Apex in Salesforce?
Apex is an object-oriented, strongly typed programming language that runs server-side on the Salesforce platform. It was designed to work directly with CRM data: objects, records, fields, and events trigger business logic written in Apex, much like database triggers handle data in traditional systems.
This means Apex is not a general-purpose language. It lives within the Salesforce ecosystem, subject to the org data model, the platform runtime, and governance limits that ensure the stability of a multitenant architecture in which thousands of organisations share the same infrastructure. Writing Apex is therefore writing software for the platform: you do not manage servers, but you also do not control the runtime.
The most common comparisons help put the language in context:
- Apex and Java: the syntax resembles Java (classes, methods, strong typing), but Apex is Salesforce-specific. A Java developer adapts quickly to the syntax. What changes is the execution model, data access through SOQL/DML, and governor limits.
- Apex and Flow: Flow is declarative automation, built without code and manageable by business teams. Apex is code: versionable, testable, and capable of expressing complex transactional logic.
- Apex and Visualforce: Visualforce is a presentation-layer technology, while Apex implements business logic. In practice, modern interfaces use Lightning Web Components, while Apex remains in the backend as an application service.
Salesforce's official documentation clearly summarizes the platform's division of responsibilities: Apex for business logic, Visualforce (and now Lightning) for the interface, and APIs for integrations. In an enterprise org, Apex is typically the layer where the commercial operation's most sensitive business rules reside.
What is Apex used for in an enterprise operation?
Apex is used to implement business logic, transactional automation, validations, and data processing beyond what declarative configuration reliably provides. In practical terms, this takes shape in classes (organised units of code), triggers (logic triggered by record events), reusable services, and integrations with external systems through REST and SOAP.
Typical scenarios in which an enterprise operation turns to Apex include:
- Complex business rules: commercial policies with multiple conditions, exceptions and dependencies between objects. The classic example is pricing and discount rules in implementations of Salesforce CPQ, where proposal configuration logic rarely fits into a manageable Flow.
- Transactional automations: processes that need to happen consistently within the same CRM transaction, succeeding or failing as a single unit.
- Integrations with ERP and legacy systems: data consolidation, order synchronisation, callouts to external services and handling of JSON and XML formats.
- High-volume processing: mass recalculations and migrations that require batch processing (Batch Apex) and asynchronous execution.
- Dynamic logic: dynamic SOQL queries, dynamic DML, and metadata access when code behavior depends on the org's configuration.
The principle that should guide this decision, and which we will develop in the next section, is simple: code must solve a need that declarative configuration does not address well. Apex written to solve what a Flow could solve creates maintenance cost with no corresponding benefit. Apex written for rules that only code can express reliably is an investment in scale and predictability.
Apex or Flow: how do you choose the right approach?
Flow is the default choice when logic is simple, manageable, and well served by declarative capabilities. Apex is appropriate when there is transactional complexity, high volume, a need for reuse, controlled integration, or advanced security and governance requirements. The decision should be an architectural criterion, not the preference of the developer at the keyboard that day.
A useful decision matrix for architecture committees considers these criteria:
| Criteria | Prefer Flow / declarative | Prefer Apex |
|---|---|---|
| Logic complexity | Few conditions, linear paths | Multiple conditions, exceptions and dependencies between objects |
| Volume | Individual records or small batches | Large record volumes that require bulkification and Batch |
| Transaction | Independent steps | Success or failure as a single unit (all-or-nothing) |
| Reuse | Process-specific automation | Reusable services for multiple processes |
| Integration | Simple callouts, without complex retries | Timeout, retries, idempotency, reconciliation |
| Team | Team managed by business analysts | Team with developers and a release process |
| Governance | Quick change, low risk | Versioned, reviewed and tested change |
Two points are often overlooked in this decision. First, the profile of the support team: a well-built Flow can be maintained by an administrator, while an Apex class requires a developer in the change cycle, which changes the solution's operating cost. Second, the long-term maintenance cost: critical business logic spread across dozens of Flows that are difficult to test can be more expensive to sustain than a well-structured, version-controlled Apex service.
The practical recommendation for enterprise operations: treat declarative as the default, document exceptions that justify code, and record the decision in an organisational architecture standard. This gives every new piece of Apex a traceable reason and prevents code from becoming invisible debt.
Apex language fundamentals: data, queries and operations
Mastering Apex begins with understanding how the language represents and manipulates CRM data. The fundamentals are few and consistent: primitive types, sObjects (the in-memory representation of CRM records), collections, and three essential operations: SOQL, SOSL and DML.
Data types and collections. Apex offers primitive types such as Integer, Boolean, Decimal, Date and String, as well as enums and custom types in classes. Work with records happens through sObjects (for example, Account, Opportunity, or customised objects such as MyObject__c). The fundamental collections are List (ordered sequence), Set (unique values), and Map (key-value pairs). Mastering Map is what separates bulkified code from code that exceeds limits.
SOQL, SOSL, and DML: the difference in one line each:
- SOQL (Salesforce Object Query Language): queries records from an object using filters, joins, and relationships.
SELECT Id, Name FROM Account WHERE Industry = 'Technology'. - SOSL (Salesforce Object Search Language): text search across multiple objects at once.
FIND 'contrato*' IN ALL FIELDS. - DML (Data Manipulation Language): write operations (
insert,update,upsert,delete,undelete) applied to records or collections.
A short example, in the style every enterprise operation should require: bulk querying and updating with collections.
List<Opportunity> fechadas = [SELECT Id, StageName FROM Opportunity
WHERE CloseDate = THIS_MONTH AND StageName = 'Closed Won'];
for (Opportunity opp : fechadas) {
opp.Description = 'Post-sale review scheduled automatically.';
}
update fechadas;
The golden rule illustrated by this example: never place SOQL or DML inside loops. Every query and write operation consumes per-transaction limits. In a scenario with 200 records (the typical trigger batch size), a SOQL query inside the loop multiplies consumption by 200 and brings down the transaction. Query first, process in collections, then write in batches. This pattern, called bulkification, is the foundation of all enterprise Apex and the subject of the next section.
How to structure Apex for scale and maintainability
Scalable Apex is code that handles record lists from the first line, separates responsibilities into layers, and avoids duplicating business rules. Structuring is not static: it is what allows an org with dozens of objects and hundreds of automations to keep evolving without every change becoming an incident.
Architecture patterns an enterprise operation should adopt:
- Bulkification as the standard: every method that touches records receives and processes collections, never one record at a time. The code review question should always be: “what happens if 200 records arrive?”.
- One trigger per object: the trigger is only the entry point and immediately delegates to a handler class. Multiple triggers on the same object create an unpredictable execution order and make governance more difficult.
- Layer separation: the trigger is the entry point, the handler orchestrates, domain classes hold object rules, services centralise reusable business logic, and the integrations layer isolates callouts from the main flow. Every layer has a reason to exist and its own test.
- Explicit exception handling: exceptions caught intentionally, with structured logging and a deliberate decision between rolling back the transaction or continuing with the failure recorded. No
try/catchgeneric that swallows errors. - Idempotency: automations triggered more than once (by a process, recursive trigger, or integration retry) must not duplicate effects.
- Naming and documentation: org conventions, intent comments and decision records. The code will be read by people who did not write it, possibly years later.
The ultimate objective is to avoid excessive coupling: a business rule exists in one service, is tested, and is called by every process that needs it. When the same rule appears duplicated in a trigger, Flow, and batch, a commercial-policy change that should take hours instead requires a manual hunt across the entire org, with the risk of missing a point.
Governor limits: what the architecture needs to consider
Governor limits are per-transaction limits imposed by the Salesforce platform to control resource consumption in a multitenant architecture and ensure that no organisation compromises the stability of others. They are not a technical detail: they are the main design constraint of any Apex solution. Treating them as an operational risk rather than a curiosity is what separates an enterprise implementation from a prototype.
The limits most relevant to architecture decisions include:
- Queries (SOQL): number of queries per transaction, reinforcing the pattern of querying collections.
- DML operations: number of writes per transaction, reinforcing batch writes.
- CPU time: processing time per transaction, sensitive to heavy loops and poorly distributed logic.
- Heap size: transaction memory, sensitive to unnecessarily retained large collections.
- Callouts: number of calls to external services per transaction, critical for integrations.
- Record volume per transaction: what defines when a process needs to be split into asynchronous batches.
How can risks be identified before production? With tests that use volumes close to real-world use. A test that processes three records proves little. The peak scenario (the end-of-month data load, migration of 50,000 accounts, recalculation of an entire portfolio) is what reveals limit overages. Code review checklists should include objective questions: where is SOQL inside loops? What happens at peak load? Which limit will be exceeded first, and what is the fallback plan?
When the process does not fit into one transaction, the platform offers asynchronous execution models, each with a defined role:
- Queueable Apex: chaining asynchronous work with state, ideal for sequential processes and callouts.
- Batch Apex: processing large volumes in batches with controlled start, execution and finish.
- Future methods: lightweight asynchronous executions and simple callout triggering.
- Scheduled Apex: scheduling recurring executions.
Choosing the model is an architectural decision, recorded with its rationale. And the cross-cutting rule always applies: a limit exceeded in production is an incident. A limit exceeded in a sandbox is an inexpensive fix.
Apex code security and governance
Secure Apex code respects the context for sharing records, checks field- and object-access permissions, operates with the least privilege possible and leaves an auditable trace of what it did. In enterprise operations subject to compliance policies, these requirements are as mandatory as functionality, and are directly linked to data privacy and security in Salesforce.
The points an Apex security review should cover:
- Sharing context:
with sharingapplies CRM sharing rules to class execution;without sharingignores these rules;inherited sharinginherits the caller's context. The choice must be explicit and justified: classes without a declared context are an audit risk, and the secure standard is always to state the intent. - CRUD and FLS: Apex must verify that the user on whose behalf the code runs has object (CRUD) and field (FLS) permission before reading or writing. Ignoring this check can expose data the interface would never show that user.
- Principle of least privilege: automations should not run with administrative profiles merely for convenience. Each process receives the minimum access it needs.
- Sensitive data and credentials: authentication secrets in named credentials, never in code; care with personal data in logs; explicit handling of callout payloads.
- Logs, auditing, and review: structured logging of relevant decisions, mandatory code review for critical changes, and formal criteria for approving security exceptions, always recorded with a deadline and owner.
Alignment with company policies happens when security is treated as a code acceptance criterion, not as a later audit step. In regulated operations, the cost of retrofitting is far greater than the cost of review.
Testing, CI/CD, and deployment between environments
Deploying Apex to production with confidence requires unit and integration tests, version control, an environment-promotion pipeline, and post-deploy monitoring. On the platform, test coverage is a platform requirement for promoting code. Treating coverage as the only indicator, however, is the classic mistake: 100% coverage with empty assertions protects nothing.
A mature quality process for enterprise Apex includes:
- Unit tests with meaningful assertions: verify the expected behavioral outcome, not merely execute the code. Assertions that compare values, states, and side effects.
- Isolated test data: creation of test records within the execution itself, with no dependency on org data, which also keeps the sandbox reliable.
- Negative and volume tests: failure scenarios (integration offline, invalid data, exceptions) and batches close to real use, which reveal governor limits.
- Integration tests: verification of the contract with external systems, including behavior on timeout and error.
From development to production:
- Environments: development in a dedicated sandbox, controlled promotion through a pipeline to quality and production environments, with a clear purpose defined for each environment.
- Version control: all code and metadata in a repository (Git), with mandatory pull-request reviews for changes to critical components.
- CI/CD: an automated pipeline runs tests, validates deployment, and blocks promotion of changes that break existing behaviour.
- Validation and rollback: every change is validated in a quality environment before production, and the rollback strategy is defined before deployment, not during the incident.
- Post-deployment monitoring: monitoring error logs, limit consumption, and process indicators during the first real executions.
Responsibility is distributed: development is accountable for code quality, architecture for design and standards, operations for monitoring, and the business for validating results. Whoever maintains the code, and the role of the Salesforce developer in continuous operation, needs to be on the table from design onward, not only when something breaks.
Apex in integrations and asynchronous processes
Apex is often the layer that integrates the CRM with the rest of the technology landscape, through REST, SOAP, and APIs, with JSON and XML payloads. The central design principle is: external integration must never compromise the CRM transaction. An ERP being unavailable at 9 a.m. on a closing day cannot halt order creation in Salesforce.
Apex integration best practices:
- Callouts outside the main transaction: external calls cannot occur in the middle of an open transaction with locks (the platform sequences DML and callouts for structural reasons). The pattern is to process the CRM transaction and delegate the call to an asynchronous process.
- Managed authentication: named credentials and standardized authentication, with no secrets in code.
- Explicit timeout and retry: define behaviour in the event of slowness and failure, with retry limits so the problem is not amplified.
- Idempotency and reconciliation: the same event processed twice must not duplicate an effect in the external system. Critical processes have a mechanism for reconciling data between systems.
The choice between synchronous and asynchronous processing follows clear criteria: synchronous when the user needs the result immediately and latency is acceptable; asynchronous (Queueable, Batch, Future, Scheduled) when volume is high, external latency is unpredictable, or work can happen later. In data-at-scale scenarios, this connected architecture directly relates to platforms such as Salesforce Data Cloud, where the integration design defines what is gained through data unification. For a complete overview of connection options, the topic is explored in depth in Salesforce API and CRM integrations.
How Apex can extend Agentforce
Agentforce can call Apex code through custom actions and invocable methods (@InvocableMethod), exposing business logic already validated for an AI agent without duplicating rules. This is the bridge between the world of autonomous agents and the automation assets the operation has already built, and it needs to be designed with governance, not merely exposed.
The most important design principle: separate the agent's decision from the controlled execution of Apex logic. The agent decides which action to call and with which inputs; the Apex action validates everything again, exactly as production code does. Trusting the agent's “good intentions” is not a control.
An Apex action prepared for an agent must ensure:
- Input validation: the invocable method treats every agent input as untrusted, validating types, values, limits, and business rules.
- Authorisation: verification of the context and permissions of the user on whose behalf the action runs. The agent is not a licence to ignore CRUD, FLS and sharing.
- Idempotency: the action can be called again by the agent without duplicating effects.
- Limits and errors: the action respects governor limits and returns actionable errors, with messages that allow the agent (or supervising human) to understand the failure.
- Observability: action-execution logs, tracing of agent decisions and mandatory human review in critical processes, such as approving discounts above policy, financial actions and changes to sensitive data.
The strategic benefit of this design is reuse: the eligibility rule that now runs in Flow and in the trigger becomes a single Apex service, also consumed by the agent action. One source of truth for the rule, three consumers. When policy changes, it changes in one place, and the agent remains correct.
Checklist for assessing an enterprise Apex implementation
An Apex implementation is ready for enterprise reality when it passes six questions, in order:
- Does the need truly require code? Was the declarative solution (Flow, validations, formulas) considered and ruled out with documented justification?
- Is the solution bulkified and does it respect governor limits? Methods process collections, there is no SOQL/DML in loops, and the peak scenario has been tested against transaction limits.
- Have security and access context been reviewed? Sharing declared and justified, CRUD/FLS checked, least privilege applied and credentials kept out of code.
- Are there volume, failure, and integration tests? Meaningful assertions, negative scenarios, batches close to real-world use, and verified integration contracts. High coverage is a consequence, not the goal.
- Is deployment reproducible and versioned? Everything in version control, automated pipeline, promotion validated in quality, rollback strategy defined before deployment.
- Will the operation have monitoring, documentation, and a support plan? Error logs monitored, architecture decisions documented, and an owner assigned to keep the code alive after go-live.
If any answer is “no” or “I don't know,” the point is an architecture gap, not a detail to resolve after an incident.
Frequently asked questions about Apex in Salesforce
What is Apex in Salesforce? Apex is the object-oriented, strongly typed programming language that runs on the Salesforce platform server and is used to implement business logic in the CRM. It manipulates platform objects, records and events, and is the code layer for what declarative configuration does not address reliably.
Is Apex the same thing as Java? No. The syntax has similarities that make adaptation easier for people coming from Java, but Apex is specific to the Salesforce platform: it runs in the CRM runtime, manipulates data through SOQL/DML, and is subject to the platform's data model and governor limits. It is not a general-purpose language outside the Salesforce ecosystem.
When should you use Apex instead of Flow? Use Flow for simple, manageable logic well served by declarative capabilities; consider Apex when there is transactional complexity, high volume, a need for reuse, integration with advanced handling, or stricter requirements for security, testing, and governance. The criteria should also consider the profile of the team that will support the solution, not only the preference of the implementer.
What are governor limits in Apex? They are per-transaction limits (SOQL queries, DML operations, CPU, heap, callouts and record volume) created to control resource use in a multitenant architecture and ensure the platform's shared stability. They must be treated as a design constraint from the outset, with peak testing, not as a production surprise.
What is the difference between SOQL, SOSL, and DML?
SOQL queries object records with filters and relationships (SELECT Id FROM Account WHERE ...); SOSL performs text search across multiple objects at once (FIND 'termo'); DML performs write operations (insert, update, upsert, delete) on records or collections. In enterprise Apex, all three always operate on collections, never record by record inside loops.
Is Apex required to customize Salesforce? No. A large share of customization needs is met by declarative configuration: fields, validations, layouts, and Flow automations. Apex is recommended for scenarios that require complex logic, transactional control, high volume, or advanced integrations, and should be a justified decision rather than the default path.
How can you make Apex code secure and scalable?
With bulkification (processing collections without SOQL/DML in loops), adherence to governor limits and an explicit declaration of the sharing context (with sharing and variants), CRUD/FLS verification, the principle of least privilege, exception handling, tests with meaningful assertions, observability, and periodic architecture review. Security and scale are acceptance criteria, not optional steps.
How do you test and deploy Apex code to production? With unit and integration tests (including negative and volume scenarios), sandbox development, repository versioning, pull-request review, a CI/CD pipeline validated in a quality environment, a rollback strategy defined before deployment, and monitoring of logs and limits after promotion. Test coverage is a minimum platform requirement, not the only quality indicator.
Can Agentforce call Apex code?
Yes, through custom actions and invocable methods (@InvocableMethod). The best practice is to treat the action as a controlled layer: input validation, authorisation with permission checks, idempotency, error handling, logs, and human review for critical processes, keeping the agent's decision separate from validated business-logic execution.
Speak with WeeNow
Does your Salesforce operation need Apex code, Flow, or a combined architecture? Talk to a WeeNow specialist to assess the scenario, scalability risks, and the next technical step, from architecture design to production code support.
Continue reading
Related posts
Salesforce CPQ: when to choose native, integrated, or customised?
Compare CPQ architectures in Salesforce, understand the legacy status, and assess integration, Brazilian tax requirements, and total cost over 36 months.
Service Cloud: what it is, how it works and when to use it
Understand Service Cloud, now presented as Agentforce Service: use cases, channels, SLAs, AI, integrations and criteria for evaluating your operation.
CRM integrated with WhatsApp: how to integrate without replacing your system
CRM integrated with WhatsApp means connecting WhatsApp's official API to the CRM you already use, without replacing your system. See the models, risks and how to do it in Salesforce.