In the following sections, we consider four levels of Salesforce application complexity. This is not intended as a comprehensive reference, but rather as a just-granular-enough overview of what kinds of problems you can solve with which sort of Salesforce SaaS or PaaS tooling to help you sketch out your application design.
Level 1: Simple Automation
Here are four commonly used automation tools available on the Salesforce platform that require no coding ability. Most of this logic can be implemented without any code at all (not even Excel-like formulas), or by using point-and-click tools to simplify formula creation.
Tool |
Specifications |
Example |
Formula Field |
Can be added to any SObject. Available in SOQL as a simple field name. |
IF (ISBLANK(FirstName),LastName,LastName & ',' & FirstName) (if no FirstName then show LastName; otherwise show LastName, FirstName)
|
Roll-Up Summary Field |
Can be added to the Master SObject on fields in any Detail SObject. |
<summaryForeignKey>OpportunityLineIt em.OpportunityId</summaryForeignKey> <summaryOperation>count</summaryOperation> (Count of all OpportunityLineItems that are Detail records on this Opportunity Master record) |
Validation Rule |
Can absolutely block or merely warn on failed validation. |
FirstName='Darth' && LastName='Vader' (fail validation if full name is Darth Vader) |
Workflow Rule |
Conditions evaluated on create or update. Can trigger field update, email, task creation, approval process, and/or SOAP message. |
If LastName == ‘Fett’ then: • Update Profession to ‘Bounty Hunter’ • Send email to lando@cloudcity.net • Create a reminder not to warn Han in 10 days |
Level 2: Complex, No-Code Applications
Salesforce offers two additional no-code tools that can implement relatively sophisticated control flows without writing any code. Process Builder is the older technology; think of it like a more powerful version of Workflow Rules. Flows (formerly known as Lightning Flows, an evolution of Visual Flows) are more recent, far more powerful, and should be considered a visual programming language.
Tool |
Specifications |
Process Builder |
More flexible than Workflow Rules, but no longer being developed. |
Flow |
Suitable for complex CRUD, state-based UI flows that can embed LWC. Intended to replace the Process Builder. |
In many cases, it’s obvious when to use Flows vs. Process Builders; and in many cases, it isn’t. This is an effect of Salesforce’s rapid platform development (product/feature overlaps are not always clear) and often turns into a bit of design-and-technical-debt headache, especially in consulting jobs.
Level 3: Clicks Plus Light Code
Process Builder and Flow can do a lot but are often “gateway drugs” to code. The first step in the slippery slope from Salesforce admin to Salesforce developer is often one of the following three:
Tool |
Specifications |
Apex Trigger |
Written in Apex. Normally used to apply simple checks before passing on to classes that implement actual business logic. Often a gateway from admin to developer roles. |
Process Builder Calling Invocable Apex |
Same as no-code Process Builder but passes variables onto Apex methods marked as @InvocableMethod |
Flow Calling Invocable Apex |
Same but for Flows. |
Clicks Not Code: Pros and Cons
Visual programming can be extremely powerful, and Flows are lightyears ahead of any previous point and-click automation tool available OOTB on the Salesforce platform. But every solution involves tradeoffs. Here are some ways to consider pros and cons of building Salesforce apps using “clicks not code”:
Benefits of “Clicks Not Code”
1. Nobody knows what they want.
Requirements are typically even less clear or understood up front than for non-Salesforce apps because Salesforce apps are “sub-apps” built into complex org-wide user workflows.
Often many iterations are needed before users can even tell whether their pain points are being addressed. Declarative solutions make simple changes trivial. Also, they do not require code deployments.
2. No need to handle the boring stuff.
Many automations of business processes are simple and similar enough that the reward of solving the problem in code is small compared to the effort, but the benefit to the business is considerable. The more such problems are solved without code, the less time you waste writing boring, repetitive boilerplate with a little business logic sprinkled in.
3. Poor adoption of version 1.
Even slightly inconvenient user flows are likely to lead to poor adoption of any custom solution. OOTB Salesforce adoption is difficult enough (users often input bad data, don’t keep records up to date, etc.).
Because Salesforce’s no/low-code tools are so powerful, many business process improvements can be facilitated by small changes — so up-front design is unlikely to seem worth the costs. But this means that the first version is highly likely to be wrong, which makes the ability to iterate quickly even more important than usual.
4. The floodgates open.
Once you build a custom app, users may expect many changes quickly “because building SF apps is easy.” Small applications are often not worth writing in code because they do not need a Turing-complete language.
5. All code is (potentially) technical debt.
Code rots like fish, not wine. As a rule, the less code the better (for business purposes at least).
Challenges of Salesforce-Native “Clicks Not Code”
1. Impedance mismatch.
Flows are good for CRUD but not complex data transformations. If your code is naturally suited to a bunch of if-thens, for loops, and simple state machines, then declarative solutions are probably best. If not, then declarative solutions suffer significant impedance mismatch. And even if so, lots of ifs are horrible to reason about, cannot easily be understood at a glance, and realistically cannot be maintained without extensive automated testing (due to high cyclomatic complexity) — which Salesforce does not require of Flows.
2. Poor complexity scaling.
Complex business logic that can technically be implemented in Flows is often hard to read/arrange visually. Flow definitions are not automatically expressed in a visually meaningful way. Flows do offer auto-arrange, but this is a poor, domain-ignorant guess. Icons in flows do not express much visual hierarchy, so a truly complex flow can be much harder to read than identically complex code.
3. Limited UI.
UI elements available declaratively without custom code are limited. Salesforce is always adding more, but many use-cases that Flows are suitable for at the level of control flow (e.g., wizards) require user interaction in each state that is not available via clicks only. So the Flow can turn into nothing but glue-sticking custom code (LWC) together. This may or not add much value.
4. Little structure among different Flows.
Each Flow can call a Subflow, but there is no clear way to distinguish basic, everyday admin tasks from deeper business logic definitions. Imagine a class structure as a linked list: One class may point to another, but no richer algebra is available. Or imagine an application consisting solely of static methods. The result is that centrality of each node is extrinsic to the design, determined entirely by a specific call chain.
5. Primitive debugging.
Errors thrown by Flows are often cryptically messaged or silently swallowed. This does force you into aggressively deliberate error handling (Flows allow you to model error states easily), but if you miss anything, then the resulting logs are comparatively information-sparse.
6. Not intimately linked with unit testing.
Salesforce requires 75% coverage of all custom Apex run in production but imposes no such requirement on point-and-click automations. Unit tests can (and should) be written for all low code logic, but flipping back and forth between declarative visualizations (or metadata XML) and/or custom Apex is an expensive context switch.
Level 3.5: App Builder Ecosystem
Sometimes, a problem is too complex for Flows but maybe not important enough to justify full-code development. Because such situations are common in enterprise contexts — and because the “Salesforce-y” thing to do is to empower admin clicks as much as possible — a robust ecosystem of third-party rapid application development tools has grown up on the Salesforce PaaS. Here are some scenarios that might suit such tools:
Complex or finicky UX demands.
The Salesforce front-end stack comes with powerful CSS frameworks out of the box, but the standard SLDS classes are optimized for a particular (whitespace-heavy, mid-2000s, record-with-related-records) look and feel. If your app requires more complex UX, you may be looking at adding another framework on top of the OOTB imports or writing a lot of UX boilerplate yourself.
Business logic that involves many tables and/or external systems.
A lot of enterprise CRUD is a lot simpler at a high level than a low level. Third normal form is efficient and clean, but 3NF ER diagrams can be cluttered, hard to read, and hard to maintain, especially for admins that are not RDBMS specialists (as Salesforce admins typically aren’t). When your ER and BPM diagrams start looking painful, model-driven tools might make your life easier.
Strong branding requirements.
To a developer, branding can seem like unimportant window-dressing. But companies spend many millions keeping everything — even internal tools — perfectly on-brand. If branding requirements are granular and dynamic, you might spend a lot of time restructuring LWC shadow DOMs and messing with border-radius values.
Level 4: Full-Code Applications
As a developer, you probably want to write code. Before writing code for the Salesforce PaaS, though, consider the following benefits and drawbacks of the platform from a coder’s point of view.
Benefits of Full-Code Salesforce Applications
1. No local environment setup.
The Salesforce PaaS is completely self-contained. No runtime resources are available to you anyway. Salesforce allows you to edit any code in-browser, so you don’t even need a local IDE, text editor, or connection to a Salesforce server.
The in-browser editors do not have any version control, however, so you’ll want a local text editor/IDE for any serious development. But it’s very convenient to be able to add small changes to play with a specific environment without any setup at all, especially for debugging release pipelines.
2. No low-level ops.
You can’t control anything at the VM level, so you don’t have to worry about it. You can’t control much beyond the Apex runtime, so no higher-level infrastructure (container orchestration, load balancing, etc.) is your problem. If Salesforce servers fail, then all the blame lies with Salesforce, and everyone understands that.
3. Trivially easy database changes.
Not only can you add an SObject (similar to a table) or field in a few clicks; but you can also do so by editing the XML files that define SObjects locally, then deploying to the org. Also, because all schema definitions are available locally, they can be deployed automatically (with suitable linting, tests, whatever) in the same package as your application code.
Challenges With Full-Code Salesforce Applications
1. Variable multi-tenant performance.
All Salesforce orgs are multi-tenant, so you will have noisy neighbors. Salesforce imposes strict platform-level limits on resource usage, but this doesn’t solve the problem completely.
2. Platform limits.
To address the noisy neighbors problem (and presumably to limit their own expenses, since Salesforce subscriptions are not granular to compute resource usage), Salesforce halts execution whenever certain resource usage limits (called governor limits) are reached. These exceptions end the transaction instantly and cannot be caught. Some apply per Apex transaction; others apply per time period or at any given moment in time.
a) Per-transaction limits include: hard caps on CPU time, heap size, query count, query rows returned, DML statements issued, stack depth, http callouts, asynchronous method invocations, asynchronous jobs queued, emails sent, absolute execution time, push notification methods called, and events published to the platform event message bus.
b) Non-per-transaction limits include: asynchronous methods invoked per 24-hour, long-running synchronous transactions concurrent across the entire org, overlapping scheduled jobs, queued batch jobs, concurrent batch starts, test class runs per 24 hours, and concurrently open query cursors.
3. Outdated, complex stack.
a) The Apex server-side language and runtime are proprietary and closed-source. However, at a basic level, they are similar enough to Java/JRE and C#/CLR that many developers do not face a major learning curve as they begin to use Apex.
b) The original web UI framework (VisualForce) is proprietary (based on JSP), server-side, and clunky. However, Salesforce now discourages VisualForce development.
c) The first client-side web framework (Aura) is proprietary, verbose, and odd — more declarative than is natural for JavaScript. However, Salesforce now encourages developers to use Lightning Web Components (LWC) for client-side development, which is based on the W3C Web Components standard.
d) The new client-side web framework (Lightning Web Components) is standards-based but still quirky and heavyweight. Even the W3C’s Web Components standard has not proved as popular as modern, non-W3C-standard JS frameworks (like React and Vue) partly for the reason that all generally reusable modules eventually prove more or less intractable: It is hard to strike a balance between power and complexity of configuration.
e) Much of the stack is just old. Apex resembles Java from 10 or 15 years ago. Aura resembles JSP translated to the client. This drawback does not strictly apply to LWC, since Web Components are a relatively new standard, but even the Web Components standard is heavyweight (and insofar “feels old”) compared with modern frameworks like Vue.js.
4. Poor debugging experience.
Server-side debugging is extremely limited and costs a large additional subscription fee. Since no local runtime is available, it is impossible to set Apex breakpoints locally. However, log analysis tools, integrated with IDEs and increasingly powerful in recent years, have made pseudo breakpoint local debugging possible (although for complex transactions this quickly runs into Salesforce’s hard log size limits). But this is still far worse than a modern Java debugging experience, or even than Visual Basic debugging in the 90s.
5. Immature ecosystem.
a) Many unskilled developers. This is partly an inevitable result of the excellent free training materials that Salesforce makes available. These materials, along with the platform’s powerful low-code tools, make the transition from end-user to administrator to developer extremely easy. This is good insofar as it empowers people to ride bicycles of the mind, but bad insofar as the ecosystem-level filter that blocks poor developers from spoiling codebases is extremely permissive.
b) Who knows what automation (code or not) will have what effect in any given org? Salesforce-optimized IDEs are not yet powerful enough to simulate execution graphs that include Salesforce’s declarative automation tools. So when values change unexpectedly, it’s hard to find all possible mutators by poking around your codebase directly.
The org might contain some Flow that might not even be in your source, because some admin clicked and dragged a few times. This can lead to extremely frustrating debugging, especially in orgs that have been around for a while — as if you were debugging an application that random developers, some with no coding experience, had built over a few decades with no plan or design thought.
c) Many hacky, rushed projects.
{{ parent.title || parent.header.title}}
{{ parent.tldr }}
{{ parent.linkDescription }}
{{ parent.urlSource.name }}