The ultimate Apex developer guide

The world’s most popular CRM platform, Salesforce, is built on Apex. Or better said, Apex is the proprietary programming language of Salesforce and accounts for almost the whole platform alone.

The ultimate Apex developer guide
Table of contents

Apex allows Salesforce developers to customize and create out-of-the-box solutions. A Salesforce developer is an individual who excels in the development of standard and custom Salesforce solutions using Apex programming.

Apex allows developers to execute process Flow and transaction control statements while witnessing API calls.

In addition, it enables Salesforce customers to add custom functionalities to their reliable applications.

This article will examine Apex and the perfect guide to becoming an Apex developer.

What is Apex?

Apex is an object-oriented programming language and a proprietary language developed by Salesforce. It is a strongly typed language. Moreover, it is effortless for strongly typed languages to predict the nature of the behavior of the written code.

Apex language allows you to write custom code examples and robust solutions executed on the platform. You can quickly build SaaS applications on top of Salesforce with the help of Apex.

It enables the Salesforce developer to add custom business logic to several events, including button on-clicks, current and related record updates, and triggering visual Force and Lightning pages.

Features of Apex

Automatic upgrade

Apex is Salesforce's rich language, and it is automatically upgraded with every new release or update of the Salesforce platform.

Java-like programming language

It is popularly known as the child of Java and has Java-like syntax. It uses simple programming syntax for loops, conditions, block statements, notation, etc. If anyone is accustomed to Java, learning Apex is simple.

Strongly-typed language

As we’ve mentioned, Apex is a strongly typed language. That means you are required to define the data type of every new variable and follow the language performance restrictions.

Database integration

Apex is integrated with the Salesforce database. Hence, it can access and move objects and records without connecting explicitly with the database. You can perform DML operations like “Insert,” “Delete,” and “Update.”

Moreover, it allows you to process multiple records considering governor limits.

Apex supports SOQL (Salesforce Object Query Language) and SOSL (Salesforce Object Search Language) to handle queries and fetch certain records.


A case-insensitive language allows developers to slack off with the capitalization of names. Case-insensitive languages treat ‘integer’ and ‘Integer’ the same, for example.

Multi-tenant environment

In a multi-tenant environment, multiple people share the same platform or application with the same database architecture trade-offs.

Apex, too, runs in a multi-tenant environment. It has a single instance on the platform but simultaneously serves multiple people and needs.

Built-in test environment

Testing Apex classes and triggers becomes accessible in the in-built environment for running test cases. You can simply create a test class, run a test, and see the complete code coverage for your class. The indicator shows the lines that have been covered.

When to use Apex?

Salesforce is a combination of point-and-click automation tools and programming with Apex. Although you can do anything with Apex, using it at every chance is not advisable. If there are tasks that are easily performed with tools like Flow and Process Builders, it is better to use them instead.

Apex can customize the standard Salesforce scalable applications, and with this wide range of functionalities and rich features, it is crucial to know when to use Apex.

Let’s take a look at some essential requirements and common scenarios where choosing Apex is a must:

When you have to build some complex logic that isn’t achievable with Workflow Rules, Process Builders, or Flow.

  • When you need to create a custom record or update logic on a trigger event caused by some injection to any record or an external situation.
  • When you have to send dynamic emails, post to a chatter feed, and send custom bell notifications.
  • When creating Salesforce web services integrated with external systems, Apex can easily integrate external services using API calls. It can also perform complex validations that aren’t achievable by the standard validation rule formula on a review board.

Apex development environments

Salesforce offers multiple environments to its users. These environments support Apex product development, but there are some things that you need to keep in mind while working with Apex.

You can write and run Apex code in a sandbox organization or developer environment. Still, it is not advised to develop Apex in a production organization, as it is an environment with live-working users.

Developing Apex code in production can disrupt the database user experience or even break some functionality.

Apex flows of action

Developer action

The Salesforce application server performs a few steps whenever a developer creates and saves an Apex code on the platform. It first compiles the Apex code into instructions understood by the Apex runtime. It then interprets the code and saves the instructions as the compiled Apex.

End-user action

When an end-user performs an Apex-invoking action or triggers Apex execution from a button, the application server gets those compiled instructions sourced from the platform metadata. It then sends those instructions through Apex-runtime, which performs the compilation.

However, the end-user doesn’t observe any difference in the execution time as it happens in the same runtime as a standard action.

Get started with Apex

Now that you know Apex’s basic definition, advanced features, and use cases, it’s time to start with it. This section revolves around the entire development process for Apex. The process consists of four steps:

  • Get a developer edition organization.
  • Learn Apex fundamentals from Trailhead.
  • Write and test your Apex code. Deploy to higher environments for product details.

Create a developer edition sandbox

As previously discussed, you can’t code Apex in a production environment. You need a developer organization or a sandbox with basic knowledge.

To create a sandbox organization, you need to:

  • Click on “Setup,” and search for “Sandboxes” in the “Quick Find” box, then select “Sandboxes.
  • Click “New Sandbox.”
  • Give a name (less than ten characters) and description to your sandbox.
  • Select the sandbox type. If you can’t find a sandbox option, contact Salesforce to order sandboxes for your organization.
  • Now select the partial or total copy sandbox. Click “Create.”

Learn Apex

Now you have a sandbox to develop and test Apex code. But first, you have to learn Apex, the basic syntax, triggers, integrations, asynchronous programming tools like Apex, etc. Apex is a very vast language with a wide range of use cases.

The first step of learning Apex is understanding its data types, operators, constructors, etc.

Now, let’s understand the development environment to create, test, and debug Apex classes.

Developer environments to write and test Apex


There are several environments to carry on with Salesforce Apex development. However, there are two most widely used developer field environments. These are the Developer Console and VS (Visual Studio) Code with installed Salesforce CLI extensions (a powerful command line interface that simplifies development and builds automation).

The Developer Console is an IDE (Integrated Development Environment) hosted by Salesforce. It enables you to write, debug, and test Salesforce apps in your organization.

It acts as a one-stop solution for all your Salesforce development needs, including generating debug logs, browsing packages, and identifying and resolving errors.

Similarly, you can also use VS Code to develop Salesforce applications. The VS Code is a free code editor that supports developing and debugging modern web, cloud, and customer applications. Microsoft has developed VS Code with advanced features like syntax highlighting, intelligent code completion, snippets, embedding version control, etc.

It also hosts the Salesforce extension pack, which provides features to work with Salesforce, Apex, Lightning, Aura web components, Visualforce, etc. The developer survey of Stack Overflow 2021 ranked VS Code as the most popular developer tool.

Write with the developer console

Let’s try writing a simple Apex class in the Developer Console:

Log into your sandbox and click on theGear” icon.

  • Click on “Developer Console.”
  • When the console opens up, click on “File,” “New,” and “Apex Class.” If you want to write a trigger, click on“File,” “New,” and “Apex Trigger.”
  • In the case of an Apex class, provide a name for your class and click “Ok.” For the trigger, give a name to your trigger, select the sObject, and click “Ok.”
  • Now, you’ll have an Apex class with a class definition.
  • Add a method to your class like this:
public Account createNewAcc (String name) {
Account acc = new Account();
Acc. Name = name;
return acc;

You now have a simple Apex class that inserts a new account record every time it is ‘called.’ Your class, at last, should look like this:

public Class CreateAccount {
public Account createNewAcc (String name) {
Account acc = new Account();
Acc. Name = name;
return acc;

The class name is “CreateAccount,” and the method is “createNewAcc.” It takes one parameter, a string assigned to the variable name. The “createNewAcc” method takes the account name and creates a new account record with the given name.

The more you learn and practice Apex, the better you’ll understand its use cases and capabilities.

Moreover, learning Apex is incomplete without triggers, SOQL, SOSL, and asynchronous Apex.

Apex triggers

An Apex trigger is a stored Apex procedure executed whenever a change is introduced to Salesforce records. The changes include record creation, updates, deletes, upserts, undeletes, and merges. For example, you can have a trigger:

  • Before a record is inserted into the Salesforce database.
  • After a record is updated or deleted from the database.
  • Even when a record is undeleted and restored into the database.
Triggers are generally preferred whenever Salesforce point-and-click tools can’t achieve the required functionality. By default, triggers are active when created and are fired automatically when a specified event occurs.

Types of triggers

There are two types of triggers, and they are invoked in two cases – before and after the record changes are committed to the database. Understanding when to use ‘before’ or ‘after’ as a trigger is fundamental as it can be tricky sometimes.

  • ‘Before triggers’ are fired before the change is committed to the database. ‘Before triggers’ are generally used to update or validate records before the changes are saved to the database.
  • ‘After triggers’ are fired after the change is committed to the database. ‘After triggers’ are used when you access Salesforce set values. Moreover, these are used to make changes to related or unrelated records after the changes of the triggered record are committed. However, you can’t change the triggered record itself because it becomes read-only when the record is in ‘after trigger’ execution.

Syntax and trigger events

An Apex trigger looks like this:

trigger TriggerName on ObjectName (trigger_events){
// code-block

Trigger events specify the case when the trigger is supposed to fire. There are seven trigger events in Salesforce:

  • Before insert: Fired before a record is inserted into the database.
  • Before update: Fired before a record change is saved into the database.
  • Before delete: Fired before a record is deleted from the database.
  • After insert: Fired after a record is inserted into the database.
  • After update: Fired after a record change is saved into the database.
  • After delete: Fired after a record is deleted from the database.
  • After undelete: Fired after a record is restored from the recycle bin.

Here are two examples of a trigger:

This first trigger fires before a new account record is inserted into the database and prints the given message in the log:

Trigger firstTrigger on Account(before insert) {
System.debug(‘I am before insert.’);

This second trigger fires after a new account record is inserted into the database and prints the given message in the log:

Trigger secondTrigger on Account(after insert) {
System.debug(‘I am after insert.’);
We know that triggers can cause changes in other records. But what if there are active triggers on the changed records? It will cause those triggers to fire too. Hence, the Apex runtime engine limits the number of operations performed simultaneously to prevent infinite repetition. 

Trigger context variables

Trigger context variables enable the developers to access the runtime contexts of triggers. Salesforce contains these variables in the “System.Trigger” class. Here is a list of trigger context variables:

  • “Trigger.isExecuting”: Returns ‘true’ when the current Apex context is a trigger.
  • “Trigger.isInsert”: Returns ‘true’ if the trigger context is due to an insert operation.
  • “Trigger.isUpdate”: Returns ‘true’ if the trigger is fired because of an update operation.
  • “Trigger.isDelete”: Returns ‘true’ if the trigger is fired because of the delete operation.
  • “Trigger.isBefore”: Returns ‘true’ if the trigger is fired before changes are committed to the database.
  • “Trigger.isAfter”: Returns ‘true’ if the trigger is fired after changes are committed to the database.
  • “Trigger.isUndelete”: Returns ‘true’ if the trigger is fired after a record is restored to the database.
  • “”: Returns a list of new or updated values of sObject records. It is available for insert, update, and undelete triggers.
  • “Trigger.newMap”: Returns a map of IDs of the new sObject versions. It is available before an update, after an insert, and after an update triggers.
  • “Trigger.old”: Returns a list of old values of sObject records. It is available only for ‘update’ and ‘delete’ triggers.
  • “Trigger.oldMap”: Returns a map of IDs of the old sObject versions. It is available only for ‘update’ and ‘delete’ triggers.
  • “Trigger.size”: Returns a total number of records involved in the trigger.

Some points to consider while working with context variables:

  • You can’t use “” and “trigger.old” in any Apex DML statement.
  • You can use “” in ‘before triggers’ to change the field values of an object. However, in ‘after triggers,’ “” is not yet saved and throws a runtime exception.
  • “” can’t be deleted, and “trigger.old” is read-only.

Trigger bulkification

Bulkifying triggers are considered a best practice as it binds the triggers within the governor limits, consuming fewer server resources. Trigger bulkification refers to writing Apex triggers in a bulk pattern.

Characteristics of bulkification:

  • Performing operations on all of the trigger records.
  • DML and SOQL operations are executed on a collection of sObjects rather than a single sObject or record.
  • Isolation of distinct records by using sets.
  • Use a map to save records from being easily accessed without using SOQL.

Trigger best practices

  • Always use a helper class (Apex class) with a trigger. Write all the trigger logic in the helper class. It makes long and complex codes maintainable in the long term.
  • Helper class should include context-specific handler methods.
  • Use ‘static’ variables and create a “RecursiveTriggerHandler” class to counter trigger repetition.
  • Limit one trigger per object.

Working with data in Apex

Salesforce lets you play around with organization data and its integrated database using Apex. Moreover, most of your work in Apex is related to sObjects and their records. Hence, learning SOQL, SOSL, and DML operations with Apex becomes important.

What is SOQL?

SOQL is a case-insensitive query language that retrieves Salesforce data from the database. SOQL should be used when you know the object where the data resides.

Use SOQL when you want to:

  • Query data from an object or multiple related database objects.
  • Count several specific records.
  • Retrieve a number, date, or checkbox field data from an object.
  • Sort query results.

This is what a typical SOQL query looks like:

SELECT list_of_fields [subquery]
FROM object_name
[WHERE condition_expression]
[GROUP BY list_of_fields]
[HAVING condition_expression]
[ORDER BY list_of_fields {ASC|DESC} [NULLS {FIRST|LAST}] ]
[LIMIT count_of_rows_to_return]
[OFFSET count_of_rows_to_ignore]

Examples of SOQL queries

An example would be:

Select name from account // standard object
Select name, Student_name from student__c // custom object
Select name from student__c where course__c = ‘Salesforce’; // where clause
SOQL is similar to SQL. You use SQL to retrieve data from database tables. While in SOQL, you query data from Salesforce objects.

However, SOQL has its features and criteria:

  • You can’t use the asterisk in SOQL to query all data as it requires specific fields to be mentioned while retrieving data.
  • SOQL doesn’t support data modification statements like “Insert” or “Update.”
  • SOQL join statements differ from traditional SQL join statements. SQL can join any two databases and fields. But SOQL can only join two related Salesforce objects.

SOQL variable binding

Salesforce has done a fantastic job introducing Apex variable binding. This enables you to use Apex variables in your SOQL query and filter records against it. For example:

String strName = '';
List positionList = [SELECT Name
FROM Company__c
WHERE Name =: strName]; 

SOQL keywords and aggregate functions

Similar to SQL, SOQL supports aggregate functions like “Sum(),” “Max(),” “Min(),’” ”Avg(),” etc. Aggregate functions perform calculative operations on a set of values and return a single value. These are often used by the ‘group by’ clause and ignore null values retrieved in the query.

SOQL also supports keywords like ‘in,’ ‘and/or,’ ‘like,’ ‘not,’ ‘order by,’ etc. These keywords are reserved words used to perform reliable operations while retrieving the data. 

Dynamic SOQL

Dynamic SOQL queries are the queries formed at Apex runtime. These queries are generally in the form of SOQL strings and are used to generate dynamic queries that depend on the user’s input.

You can use “Database.query()” to work with dynamic SOQL:

Public static void main(String str)
String s1 = ‘select name from’ + str;
List<sObject> sList = Database.query(s1);
for(sObject s: sList)

What is SOSL?

SOSL is an optimized way of searching and querying records in Salesforce. Unlike SOQL, you don’t need to know the object or field where the data resides.

A SOSL query returns a list of sObjects as it can be performed on multiple objects simultaneously.

You can use SOSL when you want to:

  • Query multiple objects or fields that may or may not be related.
  • Search data for a specific term, but you don’t know the object or field it resides in.
  • Retrieve data in different languages.
  • Query data for a specific division with the help of the division feature.

SOSL Syntax

Unlike SOQL and SQL, the SOSL query starts with the ‘find’ keyword. SOSL also supports wildcards that SOQL does not support. A wildcard is a character like an asterisk used to represent characters while searching the data. An example is:

List<List<sObject>> searchList = [FIND 'SFDC' IN ALL FIELDS
RETURNING Account(Name),

Asynchronous Apex

Methods for asynchronous Apex execute processes later in time. They run the tasks in the background without having the user wait for the execution. These processes are generally used for callouts beyond governor limits or with an external system.

Asynchronous Apex can do wonders when combined with JavaScript for external callouts.

These processes benefit the users as they are efficient, scalable, and have a higher execution limit. There are four different ways to implement Asynchronous Apex:

  • Future methods are termed the “set it and forget it” method. They are typically used for long-running operations and are set to execute in the future.
  • Batch Apex processes operations work with thousands or even millions of records. Typically, such a number will hit the governor limit, but batch Apex forms groups these records and processes them individually.
  • Queueable Apex is similar to the ‘future’ method. However, it enables the monitoring and addition of jobs in the queue. It acts as an enhanced way of executing asynchronous operations. Scheduled Apex are methods set to run at a specific point in time. These run at regular intervals that the developer specifies.

Asynchronous Apex methods are used to perform complex business needs that generally surpass the defined governor limits of Salesforce.

Apex testing

Salesforce offers an excellent testing feature to get coverage for your Apex code. Apex testing is a process that involves creating classes with data and running them in Salesforce to test your code behavior and coverage.

Ideally, you should have a minimum of 75% test coverage of your Apex class. Any class with less than 75% coverage shouldn’t be deployed to production.

Apex testing requires you to test the following measurements:

  • Positive behavior: Testing of Apex to verify if the code works as needed when correct inputs are given.
  • Negative behavior: Testing the behavior of your code when an unexpected or wrong input is given.
  • Restricted user: Testing the user's access level who invokes the Apex method.

What are unit tests and how to write them?

The Apex test class methods are referred to as unit tests and are used to test whether a particular code block works as expected. These methods don’t take any argument or commit changes to the database.

Unit test methods are flagged using thetestMethod’ keyword or ‘isTest’ annotation at the beginning of the method definition. Test methods are only to be defined within test classes, which are annotated with the ‘isTest’ keyword tool.  

Ideally, each test method should have a different scenario that tests different behaviors of your Apex code. Let’s understand it better with an example:

Private class myClass {
Static void testMethod myTest {
// code block
Private class myClass {
static void myTest() {
// code block

Here is an example test class that contains two test methods:

Private class MyTestClass {
// Methods for testing
@isTest static void test1() {
// Implement test code
@isTest static void test2() {
// Implement test code

System-defined test methods

There are two system-defined unit test methods:

  • ‘startTest’: This method marks the entry point where the testing starts.
  • ‘stopTest’: This method is declared after ‘startTest’ and marks the exit point of the actual testing code.

Test data and its considerations

  • Test data is strictly used for Apex testing and is not committed to the existing database. This data is created at the test method level and can only be used within the method.
  • Test data doesn’t affect any actual data on the Salesforce database and doesn't need to be cleaned after the test execution. It is automatically disposed of.
  • You can test classes in any Salesforce environment as they create their test data irrespective of the organization. Hence, the test classes can be termed as “Organization Agnostic.”
  • Usually, Salesforce data is not visible to the test classes. So, they create their test data. However, this changes with the help of the ‘isTest(SeeAllData=true)’ annotation. This annotation opens up the organization data for the test class.
Apex testing is an integral and unavoidable part of Salesforce Apex development. It is more than just test data, coverage, and a few annotations.

Apex debugging and deployment

Apex comes with excellent debugging support. You can debug Apex code with the help of the Developer Console and debug logs. You can also incorporate standard exception statements or create custom exceptions. Apex also sends email notifications with unhandled exception details to the developer.

Now that you’ve learned more about Apex, its development, testing, and debugging, you must also know how to deploy your code to production. 

As we’ve already discussed, Apex code is written in lower environments, like developer organizations or sandboxes (not in production), because the development may disrupt other live functionalities of the production organization.

To deploy Apex to upper environments, you can use:

  • Change sets.
  • VS Code Salesforce extensions.
  • Simple Object Access Protocol (SOAP) API.
  • Third-party metadata API tools or tooling APIs.
  • Ant migration tools.

You have cracked the code

Apex allows Salesforce developers to code back-end databases and client-side interfaces to create business-required SaaS applications. It also comes with an API that developers use to access organization data, standard web interfaces, and various tools.

Apex is the proprietary programming knowledge language of Salesforce, and it can be used to develop almost any out-of-the-box functionality in Salesforce.

It is an easy-to-use, data-focused, rigorous programming language supporting multi-tenant environment structures. Apex proves to be the strongest weapon of a Salesforce developer.