If you are a Java developer exploring the Salesforce ecosystem and wondering what Apex is, this post is for you! Apex is the Salesforce Platform’s backend programming language. Apex, along with declarative tools like Flows, allows you to customize business logic. Apex is based on the syntax of Java specifically optimized for the requirements of Salesforce’s multi tenant platform, and offers a number of out-of-the-box features, such as multi-tenancy, security, backward compatibility, and scalability.

In this post, we explore Apex features from the perspective of a Java developer. We’ll look at the steps from source code to runtime, syntax and behavior differences, data access and security, and finally namespaces and packages.

From source code to runtime

Compile on deploy

Unlike in Java, there is no Apex compiler that runs on your machine. Apex only runs on the Salesforce Platform and code is automatically compiled at deploy time. Attempting to deploy code that does not compile aborts the deployment. This process greatly reduces the chance that the Platform hosts “invalid” Apex code.

The Platform exposes a language server that helps you validate your source code. This allows you to develop in a local or remote IDE, such as VS Code, with the Salesforce extensions or Code Builder (Beta).

On top of compilation at deploy time, Apex classes and interfaces are tied to API versions. This guarantees robust backward compatibility as the Platform evolves. You could achieve something similar by recompiling Java source code for different runtime versions, but the power of Apex running in a Salesforce org is that code written a decade ago, as well as recent code, are executed in the same runtime.


Just like for any language, testing is a key part of the development process. However, what’s different with the Salesforce ecosystem is that the Platform requires a minimum of 75% code coverage when deploying to a production environment. If this target is not met, the deployment is canceled.

Apex tests can be run at any time thanks to the Platform APIs or the developer tools. The Platform includes its own test runner and unit testing features in the form of Apex assertions and annotations.

Governor limits

The Salesforce Platform is based on a multi-tenant architecture. When Apex code is executed, there are a number of strict limits that enforce an equal quality of service among all tenants. Limits are a key part of the runtime as the documentation puts it:

[…] the Apex runtime engine strictly enforces limits, so that runaway Apex code or processes don’t monopolize shared resources. If some Apex code exceeds a limit, the associated governor issues a runtime exception that can’t be handled.

There are different types of limits, such as CPU time, heap size, and per-transaction limits. Be sure to read the execution governors and limits section of the Apex Developer Guide for an overview of those limits. Limits are particularly important when working with data. We’ll get back to this in the data access section of this post.

Syntax and behavior differences

While Apex syntax is similar to Java 5, there are a number of syntactical and behavioral differences.

Case insensitive syntax

One of the biggest surprises for developers that are new to Apex can be the fact that the language is case insensitive. Reading Apex code that doesn’t have a consistent style can be confusing for a Java developer. We recommend that you keep using Java code conventions for class, method, and variable naming when working with Apex, even if those conventions rely on case sensitivity.


In Apex, there are only three collection types: List, Set and Map. Unlike in Java, these three types are classes, not interfaces. They are instantiated directly in Apex and there are no equivalents for specialized Java collection implementations, such as ArrayList, LinkedSet, or HashMap.

You can find more information about collections in the Mastering Apex Collections blog post.


Apex methods and classes are final by default. This means that you can only extend classes if you specify:

  • The virtual modifier on the parent class
  • The override modifier on child methods that override a parent method

Check out the Apex Developer Guide to learn more about how to extend a class.

Class and method modifiers

The static modifier has some notable differences between Java and Apex.

  • Static class variables or static methods can’t be accessed through class instances
  • Inner classes behave like a static Java inner class but don’t require the static keyword
  • Static methods and variables can only be declared in a top-level class definition, not in an inner class

In Apex, the default method modifier is private (there’s no equivalent for the “default” Java access modifier) and interface methods have no modifiers (they are always global).

Apex introduces a couple of modifiers that do not exist in Java:

  • The global access modifier for classes and methods. We’ll share more on this modifier in the “Namespaces and Packages” section of this post.
  • The with sharing, without sharing, and inherited sharing class modifiers. We’ll get back to these modifiers in the “Data access and security” section, and you can read more about these security modifiers in the Apex Developer Guide.

Asynchronous processing

While Apex does not expose programmatic access to threads, there are some dedicated patterns and classes that allow you to execute code asynchronously. Trailhead’s Asynchronous Apex module and the Apex Developer Guide provide a great overview and guidance on the different options for asynchronous processing: future methods, batch, queues, and scheduled jobs.

Generics and annotations

Apex supports generic types and annotations for system types, however, you can not declare your own custom generic types and annotations.


Exception classes must extend either the base Exception class or another user-defined exception, and their class name ends with the word “exception.”

Data access and security

Unlike Java code, which is designed to run on multiple application servers and connect to different database types, Apex is tightly coupled with the Salesforce Platform. This specialization allows a great deal of optimization and simplicity when working with data. Apex interacts with data thanks to two integrated querying languages and Data Manipulation Language (DML). Apex also automatically enforces permission checks for data access.

Data querying languages

Apex syntax integrates two query languages that are specific to Salesforce:

  • Salesforce Object Query Language (SOQL) — a language used to retrieve records with SQL-like queries
  • Salesforce Object Search Language (SOSL) — a language used for text-based searches

Thanks to these integrations, you can quickly retrieve data and map it to classes with a few lines of code instead of tens of lines of Java:

Note the use of SObject (see docs) in the second query. SObject is the parent class for all objects that can be stored in the Platform database: standard objects like Account or Contact, as well as any custom user-defined database objects.

Whenever an administrator creates a custom object, the Platform automatically generates a matching class with the relevant fields for this object and recompiles your codebase with no downtime.

Try the Apex Basics & Database module on Trailhead to learn more about how Apex interacts with data.

Data Manipulation Languages (DML)

On top of retrieving data, the platform provides keywords that let you perform DML operations, such as insert, update, or delete, with a minimum amount of code.

DML operations are automatically and seamlessly included in a database transaction, which can roll back in case of error.

Permission checks

Beyond core database functionalities, Apex also provides mechanisms for enforcing the security model defined by Salesforce administrators for control over which users can access or modify objects, records, or fields. Apex applies these security and sharing rules when running queries and DML operations.

Sharing rules are part of the security model that influences record access. These rules are enforced based on class security modifiers (with sharing, without sharing, and inherited sharing) and the current execution context (whether a specific user or the system initiated the execution). However, these access modifiers do not enforce the user’s permissions and Field-Level Security (FLS).

As of the Winter ’23 release, there’s an ongoing Beta for user mode database operations. User mode ensures that FLS and object permissions of the running user are respected, unlike when running in system mode (the default mode for Apex).

Namespaces and packages

Both Apex and Java use the notion of packages, but these mean different things depending on the language. The closest equivalent to Java packages in Apex are namespaces, but their use is different.

Apex classes can be isolated from other classes thanks to namespaces, however special considerations apply:

  • A namespace is registered in a Salesforce org
  • An org may only hold one namespace
  • The name of a namespace must be unique across all Salesforce customers
  • There are no sub-namespaces
  • Namespaces are only available when used with Salesforce packages

We won’t dive into the different Salesforce package types for the sake of brevity, but packages act as a container for deliverables and these include more than Apex code. Packages can include most kinds of metadata, such as object and field definitions, layouts, reports and dashboards, UI components, and more. Some package types (managed packages) are created with a namespace.

The global access modifier allows classes and methods to be accessed outside of their namespace. A public class within a package is not visible to an org where the package is installed.

There are no package or dependency managers for Apex like Maven or Gradle. Packages are installed using their version ID either from the Salesforce APIs, tools like the Salesforce CLI, or the AppExchange (Salesforce’s marketplace).

Closing words

This concludes our overview of Apex. You’ve learned about the specifics of how to develop, test, deploy, and package your Apex source code. You had an overview of the developer tools and the language’s key characteristics. You saw what makes Apex uniquely powerful to address the challenge of a multi-tenant and data-driven cloud environment and how tight Platform coupling allows for optimizations that Java wouldn’t support.

You are now ready to continue your developer journey with Apex. We’ll leave you with some resources to guide you along the way.


About the author

Philippe Ozil is a Principal Developer Advocate at Salesforce where he focuses on the Salesforce Platform. He writes technical content and speaks frequently at conferences. He is a full stack developer and enjoys working on DevOps, robotics, and VR projects. Follow him on Twitter @PhilippeOzil or check his GitHub projects @pozil.

Get the latest Salesforce Developer blog posts and podcast episodes via Slack or RSS.

Add to Slack Subscribe to RSS