A candidate who went through Salesforce's MTS loop in early 2025 described the behavioral round like this: "They kept asking me how I'd handle a situation where shipping fast would compromise a customer's trust. Different framing every time. Same question." That tells you almost everything about what Salesforce is actually evaluating in its engineering interviews.
The technical bar is real, two DSA rounds plus a system design round will screen out unprepared candidates. But Salesforce differs from Google or Meta in one specific way: interviewers are evaluating whether you think natively about multi-tenant SaaS constraints, not whether you can solve an abstract graph problem faster than anyone else. The platform serves thousands of organizations on shared infrastructure. Every design question you get will, eventually, come back to that.
This page covers the 2026 Salesforce loop for MTS, SMTS, and LMTS (member of technical staff, senior MTS, lead MTS) roles. The questions and process details come from verified candidate accounts on Glassdoor, interviewing.io, LeetCode Discuss, and Medium, with reports spanning 2024 through early 2026. Where something is team-specific or unconfirmed, I've noted it.
Easy questions
15Floyd's tortoise and hare. A slow pointer moves one node at a time, a fast pointer moves two. If a cycle exists, they eventually land on the same node; if the list terminates cleanly, the fast pointer hits null first. Finding where the cycle starts takes one more step: reset a pointer to the head, leave the other at the meeting point, then advance both one node at a time. They meet again exactly at the cycle's entry node, a property that falls out of the math and is worth memorizing rather than re-deriving live.
Reported as a warm-up question ahead of the harder k-group reversal problem in the same phone screen, more than once. Interviewers use it to confirm you can manipulate pointers cleanly before they load on the reversal problem's extra bookkeeping. Reaching for a HashSet of visited nodes instead of the O(1)-space two-pointer trick is a bad sign this early in the loop.
The Ohana question. The key is that "without being asked" is doing the work here. Interviewers want evidence that you notice when a colleague is struggling and take initiative before a manager or a deadline forces the conversation. Stories about pairing on a hard bug, picking up a task someone was blocked on, or having a difficult honest conversation with a peer who was in over their head all work well.
What doesn't work: "I helped a new hire onboard." Onboarding is expected. Interviewers look for a situation with some stakes: the teammate's contribution to a project was at risk, or the team dynamic was affected, and your action changed the outcome.
Generic answers ("I want to work on CRM at scale") don't land. Salesforce engineers work on one of the most complex multi-tenant SaaS platforms in existence and they know it. Specificity about what actually draws you to the technical problem is the only answer that registers. Einstein AI features and how they're being integrated into the CRM workflow, the MuleSoft integration layer, Tableau's data visualization stack, or the challenge of building Apex as a safe, governor-limited language for untrusted third-party code are all specific enough to be real.
Ask your recruiter early which cloud organization you're interviewing for. Saying "I'm excited about the Data Cloud's approach to zero-copy data federation" in a Sales Cloud round suggests you didn't do the research. These are genuinely different technical domains under the same Salesforce umbrella.
Sustainability is the fifth value on Salesforce's list, and the company frames it two ways: environmental (net zero commitments, renewable energy targets) and organizational, building things that hold up rather than optimizing for whatever ships this sprint. It's the value least likely to get a dedicated 40-minute drill the way Trust does. When it does show up, it's usually reframed as a technical debt question: did you ever choose the slower, more maintainable path over the faster one, and what made you do it.
A workable answer names the actual trade-off; more upfront design time against a deadline, a refactor that delayed a feature by two weeks, choosing a boring, well-understood library over a newer one with better benchmarks. What matters is that you can say why the slower path won, in terms an interviewer can evaluate, not just that you generally value doing things "the right way."
Most candidates report three to seven weeks. The average across SMTS reports specifically is around 34 days. The longest stretch is usually between the virtual onsite and the offer decision: five to ten business days for the hiring manager to calibrate with interviewers and get approval. If you have a referral from inside the company, the timeline often compresses by one to two weeks, partly because referrals tend to get recruiter attention faster and partly because team fit is partially pre-established.
No strict requirement, but Java is strongly preferred and is the default language for backend roles. Most reported coding rounds from MTS and SMTS candidates were conducted in Java. Python is acceptable and sometimes used in data-adjacent roles. For any role with "Platform" or "Apex" in the title, you should be comfortable with Java's type system and concurrency model at minimum, since Apex is syntactically similar to Java. Ask your recruiter what language the team codes in day-to-day: that's the language you should use in the interview.
For general SWE roles (MTS, SMTS), Apex and SOQL knowledge are not required. The interviews test core CS fundamentals, Java or Python, and system design. However, knowing what Salesforce actually builds, multi-tenancy, governor limits, the CRM data model, Apex as a sandboxed execution environment, shows genuine interest and comes up in system design rounds. For Salesforce Developer or Platform Engineer roles specifically, Apex and SOQL are tested directly.
More important than at most companies. Multiple SMTS candidates who reported technical rounds going well also reported that the offer decision came down to the values round. Salesforce interviewers at SMTS and above have reported spending 40 to 50 minutes on a single behavioral question in some loops. The Trust value is not decorative: Salesforce's business depends on enterprise customers trusting them with sensitive data, and they hire for people who've demonstrated that instinct in their own careers. Come with specific, high-stakes stories tied to each of the five values.
V2MOM stands for Vision, Values, Methods, Obstacles, and Measures. It's an internal goal-setting framework that Marc Benioff introduced at Salesforce in 1999 and that every team and individual uses for alignment. In interviews, it tends to come up in the hiring manager round as a framing question: "how do you set goals?" or "how do you align your work with team priorities?" Knowing what V2MOM is and being able to describe how you've used a similar structured approach to goal-setting in your own work is sufficient. You don't need to have used V2MOM specifically.
Generally no. New grad and intern loops focus on coding and behavioral questions. A full system design round is standard for MTS and above. At the new grad level, interviewers may ask lighter object-oriented design questions or discuss high-level architecture trade-offs during the hiring manager round, but not the 60-minute end-to-end design session that SMTS candidates get. If you're a new grad and your recruiter mentions a design round, clarify whether it's OOP design or full system design: these are quite different preparation targets.
Apex is Salesforce's proprietary, strongly typed, object oriented language. It looks a lot like Java on the surface, classes, interfaces, generics-lite collections, try/catch, but it only runs inside the Force.com runtime, which changes what you can and can't do with it.
You don't get a file system, you don't spin up your own threads, and you don't manage memory. Almost everything you'd normally do with a library or a raw socket call has to go through a platform API instead, HTTP callouts through Named Credentials, async work through future methods or Queueable, scheduled jobs through the Schedulable interface. Apex also bakes database access directly into the language, SOQL and DML are keywords, not a library you import, and every line of Apex is metered against governor limits because the same server hardware is running thousands of other customers' code at the same time.
Salesforce is multitenant, one physical server cluster runs code for thousands of orgs at once, so there's no way to let any single transaction hog CPU, memory, or database connections. Governor limits are hard caps the platform enforces per transaction to stop that from happening. A synchronous transaction gets 100 SOQL queries, 150 DML statements, 10,000 DML rows, and 10 seconds of CPU time, for example. Async contexts like batch or queueable get roomier limits, up to 200 queries and 60 seconds of CPU time.
The important part isn't memorizing the numbers, it's what happens when you cross one: the platform throws an uncatchable LimitException and the entire transaction rolls back, including anything that already succeeded earlier in that same transaction. That's why almost every Apex best practice, bulkifying loops, caching query results, avoiding nested loops over related records, exists purely to keep you under these ceilings.
SOQL is read only. There's no INSERT, UPDATE, or DELETE in a SOQL statement, those go through separate DML operations in Apex. You also can't do SELECT *, you have to name every field you want back, and there's no arbitrary JOIN syntax across unrelated tables. Instead, SOQL uses relationship traversal: dot notation walks up to a parent (Account.Name from a Contact query), and a subquery in parentheses walks down to children (SELECT Id, (SELECT LastName FROM Contacts) FROM Account).
There's also a second query language, SOSL, for full text search across multiple object types at once, something SOQL can't do in a single statement. And SOQL results come back as typed sObjects rather than generic rows, so field access is compile time checked against the object's schema.
A trigger doesn't always fire for one record at a time. A data import, an API batch insert, or a mass update from a list view can push up to 200 records into a single trigger invocation. If your trigger runs a SOQL query or a DML statement inside a for loop over trigger.new, that's fine with one record and a LimitException with two hundred.
Bulkifying means writing the trigger to assume it always runs against a full batch: collect the Ids or values you need into a Set first, run one SOQL query outside the loop to pull related records, build your changes in a List or Map in memory, then do one DML statement at the end for the whole batch.
// bad: query inside the loop
for (Opportunity opp : trigger.new) {
Account acc = [SELECT Id, Rating FROM Account WHERE Id = :opp.AccountId];
}
// bulkified
Set<Id> accountIds = new Set<Id>();
for (Opportunity opp : trigger.new) {
accountIds.add(opp.AccountId);
}
Map<Id, Account> accounts = new Map<Id, Account>(
[SELECT Id, Rating FROM Account WHERE Id IN :accountIds]
);Salesforce has been pushing Flow as the default automation tool for a while now, Workflow Rules and Process Builder are both being retired, and Flow can handle most day to day business logic: field updates, related record creation, sending an email, calling a subflow. An admin can build and maintain it without touching code, which matters a lot for a team that doesn't have a dedicated developer.
You reach for Apex when the logic needs something declarative tools can't express cleanly: complex conditional branching across many objects, recursive or iterative processing, synchronous callouts with retry and error handling logic, or performance-sensitive bulk operations where a flow's per-record overhead would be too slow. A common pattern is actually a hybrid, a lightweight invocable Apex method that a Flow calls for the one piece of logic that's genuinely easier to write in code, while the rest of the automation stays declarative.
Medium questions
22Classic two-pointer sliding window. Maintain a set of characters in the current window. Advance the right pointer, add the character to the set. When you encounter a duplicate, advance the left pointer and remove characters until the duplicate is gone. Track the maximum window size throughout.
Reported in multiple OA write-ups from 2024 and 2025. Interviewers extend this to "longest substring with at most K distinct characters," which is the same sliding window pattern with a frequency map instead of a set. Both variants appear in Salesforce's OA pool.
public int lengthOfLongestSubstring(String s) {
Map<Character, Integer> charIndex = new HashMap<>();
int maxLen = 0;
int left = 0;
for (int right = 0; right < s.length(); right++) {
char c = s.charAt(right);
if (charIndex.containsKey(c) && charIndex.get(c) >= left) {
left = charIndex.get(c) + 1;
}
charIndex.put(c, right);
maxLen = Math.max(maxLen, right - left + 1);
}
return maxLen;
}Sort meetings by start time. Use a min-heap to track when the earliest-ending meeting finishes. For each new meeting, if it starts after or when the earliest meeting ends, reuse that room (pop and push the new end time). Otherwise, add a new room. The heap size at the end is your answer.
Reported in several OA discussions. Salesforce frames this as a resource scheduling problem: "given N concurrent API calls with start and end times, what is the minimum number of threads needed?" Same algorithm, different story. The heap-based approach is O(n log n) for sorting plus O(n log n) for heap operations.
DFS with a running path list and a remaining sum. At each node, subtract the node's value from the remaining sum and add the node to the current path. When you reach a leaf node and the remaining sum is zero, you've found a valid path. Copy the path (not the reference) into your result list before returning.
Reported from an MTS coding round. Common follow-up: "modify this to return only the longest path" or "what if node values can be negative?" Negative values mean you can't prune early when the remaining sum goes negative, which changes the complexity from average-O(n) to always-O(n).
Build a directed adjacency list from the prerequisite pairs. Run Kahn's algorithm: collect all nodes with in-degree zero into a queue, process them one at a time, decrement the in-degree of their neighbors, and add any neighbor with in-degree zero to the queue. If the total processed nodes equals the total course count, a valid ordering exists. Otherwise, a cycle is present.
Salesforce OA reports from 2025 mention workflow dependency problems that map exactly to this structure. "Given a list of automation steps where some steps must complete before others can start, return a valid execution order or report that no order exists." Topological sort is the core pattern. Interviewers want to see Kahn's explicitly, not just DFS cycle detection, because Kahn's produces the order directly.
from collections import deque, defaultdict
def find_order(num_courses, prerequisites):
graph = defaultdict(list)
in_degree = [0] * num_courses
for dest, src in prerequisites:
graph[src].append(dest)
in_degree[dest] += 1
queue = deque([i for i in range(num_courses) if in_degree[i] == 0])
order = []
while queue:
node = queue.popleft()
order.append(node)
for neighbor in graph[node]:
in_degree[neighbor] -= 1
if in_degree[neighbor] == 0:
queue.append(neighbor)
return order if len(order) == num_courses else []Union-Find with path compression and union by rank. Initialize each node as its own parent. For each edge, union the two nodes. Count distinct root parents at the end to get the number of components.
Salesforce frames this as an account deduplication problem in reported loops: "given a list of known duplicate pairs between customer accounts, how many distinct customers do you have?" Union-Find is the right answer and interviewers expect you to name it. A naive BFS solution works but interviewers push you toward Union-Find because it handles incremental updates efficiently, relevant when new duplicate signals arrive in a streaming feed.
DFS or BFS flood fill. Scan the grid cell by cell. Every time you hit an unvisited land cell, increment the island count and flood-fill outward in four directions, marking every connected land cell as visited so it isn't counted twice. The flood fill itself is a handful of lines; the entire problem is really about recognizing that a grid is just a graph where each cell has up to four neighbors.
Salesforce sometimes pairs this directly with the Union-Find question above, same underlying question (how many distinct groups exist), two different techniques. A grid maps naturally to DFS/BFS because adjacency is implicit in the coordinates. A list of arbitrary pairs, like the duplicate-account scenario earlier, maps better to Union-Find because there's no grid structure to walk. Knowing which one to reach for, not just how to code either, is what the follow-up actually tests.
public int numIslands(char[][] grid) {
int count = 0;
for (int r = 0; r < grid.length; r++) {
for (int c = 0; c < grid[0].length; c++) {
if (grid[r][c] == '1') {
count++;
sink(grid, r, c);
}
}
}
return count;
}
private void sink(char[][] grid, int r, int c) {
if (r < 0 || c < 0 || r >= grid.length || c >= grid[0].length || grid[r][c] != '1') return;
grid[r][c] = '0';
sink(grid, r + 1, c);
sink(grid, r - 1, c);
sink(grid, r, c + 1);
sink(grid, r, c - 1);
}2D DP table where dp[i][j] is the length of the LCS of the first i characters of s1 and the first j characters of s2. If s1[i-1] == s2[j-1], dp[i][j] = dp[i-1][j-1] + 1. Otherwise, dp[i][j] = max(dp[i-1][j], dp[i][j-1]). Space can be optimized to O(min(m,n)) using two rows.
Reported from Salesforce OA. The Salesforce framing: "given two versions of a configuration file, find the longest sequence of settings common to both." Same DP, but talking through the real-world mapping shows domain thinking that interviewers note.
Sort each string's characters to produce a canonical key ("eat" and "tea" both become "aet"), then group strings in a hash map keyed by that canonical form. Time complexity is O(n * k log k), where k is the average string length. A slightly faster variant uses a character-count array as the key instead of a sorted string, dropping the per-string cost to O(k) and the total to O(n * k), worth mentioning if the interviewer pushes on scale.
Reported across multiple Glassdoor and LeetCode Discuss threads for Salesforce OAs, close to the textbook version with no CRM framing layered on top. That's arguably the point: it's a fast, unambiguous read on whether a candidate reaches for hashing without being prompted.
A min-heap capped at size k. Push every new value, and if the heap grows past k, pop the smallest. Whatever sits at the top of the heap at any moment is the Kth largest value seen so far, in O(log k) per insertion instead of re-sorting the whole stream on every arrival.
Salesforce interviewers like to wrap this in a pipeline-ranking story: surface the Kth biggest open deal in a sales team's pipeline as new opportunities get created, without recomputing a sort over every record on every write. The harder follow-up is deletions, what happens when a deal in your top-k gets closed or deleted, since a plain heap doesn't support arbitrary removal. The honest answer involves a lazy-deletion flag checked on pop, or swapping to an indexed heap, and saying so out loud beats pretending the basic heap handles it cleanly.
A HashMap paired with a doubly linked list. The map gives O(1) lookup by key; the list tracks recency, most recently used at the head, least recently used at the tail. On every get or put, move the touched node to the head. When the cache is full, evict the node at the tail and remove its key from the map. Every operation the interface needs, get, put, eviction, is O(1) because both structures support constant-time insertion, removal, and lookup at the positions that matter.
This exact structure sits underneath more caching layers than most candidates realize, including the client-side cacheable data adapters in Salesforce's own Lightning Web Components framework. The interview version stays language-agnostic. If your loop is specifically for a Salesforce Developer role and you want the Apex and LWC-flavored version of caching, the Salesforce Developer interview questions page covers that layer directly.
public class LRUCache {
private final int capacity;
private final Map<Integer, Node> cache = new HashMap<>();
private final Node head = new Node(0, 0);
private final Node tail = new Node(0, 0);
public LRUCache(int capacity) {
this.capacity = capacity;
head.next = tail;
tail.prev = head;
}
public int get(int key) {
if (!cache.containsKey(key)) return -1;
Node node = cache.get(key);
moveToFront(node);
return node.value;
}
public void put(int key, int value) {
if (cache.containsKey(key)) {
Node node = cache.get(key);
node.value = value;
moveToFront(node);
return;
}
if (cache.size() == capacity) {
Node lru = tail.prev;
remove(lru);
cache.remove(lru.key);
}
Node node = new Node(key, value);
cache.put(key, node);
addToFront(node);
}
private void moveToFront(Node node) {
remove(node);
addToFront(node);
}
private void remove(Node node) {
node.prev.next = node.next;
node.next.prev = node.prev;
}
private void addToFront(Node node) {
node.next = head.next;
node.prev = head;
head.next.prev = node;
head.next = node;
}
private static class Node {
int key, value;
Node prev, next;
Node(int key, int value) {
this.key = key;
this.value = value;
}
}
}A Logger class that wraps message printing with a timestamp and level filter. Thread safety for the output stream (synchronizing on the writer or using a concurrent queue with a single writer thread). Rate limiting per log level: DEBUG logs might be throttled to 100 per second while ERROR logs pass through unconditionally. Use the token bucket from the previous problem per level, with ERROR exempted from rate limiting.
Reported as a follow-up to the rate limiter design question. Interviewers are testing whether you can compose designs, not just solve isolated problems. The ConcurrentHashMap-keyed-by-level approach from the rate limiter drops in directly here.
Three states. Closed is the normal state, calls go through and failures get counted. Once failures cross a threshold, the breaker trips to Open and every call fails immediately without even attempting the request, protecting the caller from wasting time on a service that's already down. After a fixed timeout, the breaker moves to Half-Open and lets exactly one trial call through. Success closes the breaker and resets the failure count; failure sends it straight back to Open.
This one gets paired with the rate limiter question earlier in the loop specifically because the two look similar and solve opposite problems. A rate limiter protects the callee from too many requests. A circuit breaker protects the caller from a callee that's already unhealthy. Candidates who conflate the two, or describe a circuit breaker as "basically a rate limiter," lose points here even if the code they write happens to work.
public class CircuitBreaker {
private enum State { CLOSED, OPEN, HALF_OPEN }
private State state = State.CLOSED;
private int failureCount = 0;
private final int failureThreshold;
private final long resetTimeoutMs;
private long openedAt;
public CircuitBreaker(int failureThreshold, long resetTimeoutMs) {
this.failureThreshold = failureThreshold;
this.resetTimeoutMs = resetTimeoutMs;
}
public synchronized boolean allowRequest() {
if (state == State.OPEN && System.currentTimeMillis() - openedAt > resetTimeoutMs) {
state = State.HALF_OPEN;
}
return state != State.OPEN;
}
public synchronized void recordSuccess() {
failureCount = 0;
state = State.CLOSED;
}
public synchronized void recordFailure() {
failureCount++;
if (state == State.HALF_OPEN || failureCount >= failureThreshold) {
state = State.OPEN;
openedAt = System.currentTimeMillis();
}
}
}In production you'd reach for java.util.concurrent.ArrayBlockingQueue or LinkedBlockingQueue and never hand-roll this. Interviewers know that, and ask you to build it anyway, specifically to see if you actually understand wait() and notify() or have only ever called .put() on a library class. A synchronized put() blocks while the queue is full; a synchronized take() blocks while it's empty. Both call notifyAll() after changing the queue's state so any thread waiting on the opposite condition wakes up and rechecks.
The detail that separates a correct answer from a lucky one: the wait has to sit inside a while loop, not an if statement. A thread can wake up from a spurious wakeup, or wake up to find some other thread already grabbed the slot it was waiting for, and re-checking the condition in a loop is the only way to handle that correctly. Interviewers who've been burned by this in real code ask about it directly, "what happens if wait() returns and the condition isn't actually true yet."
public class BoundedQueue<T> {
private final Queue<T> queue = new LinkedList<>();
private final int capacity;
public BoundedQueue(int capacity) {
this.capacity = capacity;
}
public synchronized void put(T item) throws InterruptedException {
while (queue.size() == capacity) {
wait();
}
queue.add(item);
notifyAll();
}
public synchronized T take() throws InterruptedException {
while (queue.isEmpty()) {
wait();
}
T item = queue.poll();
notifyAll();
return item;
}
}The most-reported behavioral question in Salesforce SMTS loops. Use the STAR structure, but go deep on the concrete stakes: what specifically was the security risk, what was the business pressure to move fast, who pushed back on your position, and what happened after you held the line. The outcome does not need to be a win. Interviewers accept stories where the compromise went badly as long as you can articulate the lesson.
Weak answer: "I always prioritize security." That's a principle, not a story. Strong answer: "In Q3 2024, we had a release with a misconfigured field-level encryption setting affecting a small subset of records. I flagged it 48 hours before ship. The product team pushed to ship with a hotfix plan. I escalated because a hotfix plan is not the same as fixed. We delayed two weeks." Concrete, real, uncomfortable, and credible.
Customer Success as a value is not about being friendly. It's about making engineering decisions with an explicit mental model of downstream customer impact. The question is probing whether you think about users when making technical trade-offs, not just technical correctness.
Strong answers connect a specific technical choice (caching strategy, API rate limit, error message design, timeout value) to a measurable customer outcome (reduced support tickets, improved load time, fewer failed imports). If you've worked on B2B software, you've made decisions like this. The interviewer wants to hear you narrate the connection explicitly rather than leaving it implicit.
Interviewers want two things here: that you can hold a technical position with evidence, and that you can change your mind with evidence. Pure deference ("I raised my concern and then deferred to the senior engineer") reads as low-agency. Stubbornness that ignores new information ("I kept pushing until they agreed with me") raises a different flag.
The best answers describe a specific technical trade-off, the data or reasoning you brought to the conversation, what the outcome was, and an honest assessment of whether, in retrospect, you were right. If you were wrong, say so. That's more credible than a story where your technical instinct was always vindicated.
Salesforce sells to enterprises with zero-tolerance for unexpected changes. The question is probing whether you understand the tension between shipping fast (Innovation) and being predictable (Trust / Customer Success). The expected framework: feature flags and dark launches for gradual rollouts, API versioning to avoid breaking changes, backward compatibility as a non-negotiable constraint, and a clear communication channel (release notes, deprecation schedules) for any change that affects existing behavior.
Reported from SMTS hiring manager rounds. Candidates who only talk about innovation without naming the reliability constraints sound like they've never worked in enterprise software. Candidates who only talk about reliability sound like they'd never ship anything new. Interviewers are looking for the tension acknowledged and resolved, not one value winning over the other.
Salesforce cares about operational maturity. The question tests your debugging methodology, your communication under pressure, and your ability to drive a blameless post-mortem. Strong answers include the specific debugging steps you took (not just "I investigated the logs"), who you pulled in and when, how you communicated status to stakeholders while the incident was live, and what structural change you made to prevent recurrence.
Reported from multiple SMTS and LMTS behavioral rounds. The follow-up is almost always "what monitoring or alerting did you add afterward?" If your answer is "none," that's a problem. If your answer is specific (added a p99 latency alert, added a circuit breaker, added a runbook to the incident doc), that's the right signal.
Equality sits fourth in Salesforce's stated value hierarchy, and it comes up less consistently across loops than Trust does, but it does surface, particularly when a panel includes a more senior interviewer with team-building responsibility. The bar here is the same as the Ohana question: a specific moment, not a stated belief. A colleague being consistently talked over in design reviews, credit for a shipped feature landing on the wrong name in a retro, a promotion cycle that quietly favored people who happened to sit near the manager. Notice it, then act on it.
Weak answer: "I believe everyone should be treated fairly." True, and useless as an interview answer. Strong answer names the specific person or pattern, what you actually did (raised it privately, pushed back in the room, changed a process), and what happened afterward, including if the outcome was only partial. A story that ends "and it fixed everything" reads less credibly than one that ends "and it got better, not perfect."
Not Salesforce-specific. Most SMTS and LMTS loops ask some version of it regardless of company, and Salesforce's panels are no exception. Interviewers want three things: the feedback itself, close to verbatim rather than softened into something flattering, your actual first reaction (defensiveness is a normal answer and more credible than claiming instant gratitude), and something that changed afterward that another person could actually verify happened.
Weak answer: "I always welcome feedback and use it to grow." Strong answer names the specific feedback, admits the part that stung, and describes a concrete behavior change someone else would notice, a different code review habit, asking for input earlier in a project, changing how you run a meeting. If your story doesn't include a moment of discomfort, it's probably too polished to be believed.
The platform loads the existing record from the database (or initializes a blank one for an insert), then overlays the incoming field values on top of it. It runs system validation first, required fields, field length, data type formatting, then any before-save record-triggered flows, then before triggers. After that it runs your custom validation rules and duplicate rules. If a duplicate rule is set to block, the save stops right there before anything touches the database.
Once validation passes, the record is written to the database, but the transaction isn't committed yet. After triggers fire next, followed by assignment rules and auto-response rules if it's a case or lead. Then workflow rules run, and this is the part that trips people up: if a workflow rule does a field update, the record saves again and before/after triggers fire a second time, but only once total no matter how many workflow rules matched. Processes and after-save flows run next, then escalation and entitlement rules, then any roll-up summary recalculation on a parent record, which in turn re-fires that parent's own triggers. Only after all of that does Salesforce commit the whole thing to the database and fire any async work, future methods, queueables, outbound messages, platform events.
By default an Apex class runs in system context and ignores every sharing rule, role hierarchy, and manual share you've set up, it can see and touch any record regardless of who's running it. Marking a class 'with sharing' makes it respect the running user's record-level access, so a query in that class only returns rows the user could actually see in the UI. 'without sharing' is the explicit opposite, it forces system mode even if it's called from a class that declared 'with sharing.'
The gotcha most people miss: if a class has no sharing declaration at all, it doesn't default to system mode, it inherits whatever context it was called from. So an undeclared helper class called from a 'with sharing' controller runs with sharing, and the same class called from a batch job runs without it. That's why Salesforce's security review requires every class to explicitly declare one or the other, silent inheritance is exactly the kind of thing that causes a data leak nobody notices in testing.
A legitimate reason to use 'without sharing' deliberately is a shared service class that has to aggregate data across the whole org for a dashboard or a scheduled job, where record-level visibility genuinely shouldn't apply, or a REST API endpoint where authorization is already handled explicitly at the request layer rather than through the built-in sharing model. Note that neither setting touches field-level security or object permissions, you still need WITH SECURITY_ENFORCED or Security.stripInaccessible for that.
Hard questions
6Use a min-heap seeded with the head node of each list. Repeatedly extract the minimum node, add it to the result, and push its next node onto the heap. This runs in O(N log k) where N is total nodes and k is the number of lists.
Appeared in an MTS onsite coding round in 2024. The divide-and-conquer approach (merge pairs of lists repeatedly) is an acceptable alternative and also runs in O(N log k). Salesforce interviewers sometimes ask you to implement the min-heap version explicitly to confirm you understand heap operations, not just the high-level idea.
public ListNode mergeKLists(ListNode[] lists) {
PriorityQueue<ListNode> heap = new PriorityQueue<>(
(a, b) -> a.val - b.val
);
for (ListNode node : lists) {
if (node != null) heap.offer(node);
}
ListNode dummy = new ListNode(0);
ListNode curr = dummy;
while (!heap.isEmpty()) {
ListNode node = heap.poll();
curr.next = node;
curr = curr.next;
if (node.next != null) heap.offer(node.next);
}
return dummy.next;
}Reverse exactly k nodes at a time, leave remaining nodes untouched if fewer than k remain. The clean approach: check if there are at least k nodes ahead, reverse those k nodes using three pointers (prev, curr, next), then recursively or iteratively process the remainder. Connect the reversed segment back to the main list.
Reported from an MTS DSA round. The interviewer asked for both the recursive and iterative implementations. The iterative version avoids stack overflow on large inputs and is the production-preferred answer. Edge cases to cover: k equals 1 (no change), k equals list length (full reversal), list shorter than k (return as-is).
public ListNode reverseKGroup(ListNode head, int k) {
ListNode check = head;
for (int i = 0; i < k; i++) {
if (check == null) return head;
check = check.next;
}
ListNode prev = null, curr = head;
for (int i = 0; i < k; i++) {
ListNode next = curr.next;
curr.next = prev;
prev = curr;
curr = next;
}
head.next = reverseKGroup(curr, k);
return prev;
}BFS serialization: write node values level by level with null markers for missing children. Deserialization: parse the serialized string, create the root from the first value, then use a queue to track nodes waiting for their children and assign values from the remaining string in order.
Appears in Salesforce OA question pools (reported from 2024 and 2025 candidates). The specific follow-up reported: "how would you handle serialization if the tree can have up to 10 million nodes?" The expected answer covers streaming/chunked serialization rather than holding the full string in memory.
This is a hybrid problem. One approach: iterate through all subsequences of X (exponential, impractical). Better: DP where dp[i][j] is the length of the longest subsequence of X[0..i] that ends at position j in Y. Transition: if X[i] == Y[j], dp[i][j] = dp[i-1][prev_j] + 1 where prev_j is the most recent position before j where X[i-1] was matched.
Reported directly from a Salesforce HackerRank OA (intern and MTS levels). It is harder than it looks and combines subsequence DP with substring constraints. Candidates who recognized the pattern from practice handled it; those who tried to derive it from scratch under time pressure mostly ran out of time.
The O(n) time, O(1) space trick: treat the array itself as a hash table. Walk through it swapping each value v (where 1 <= v <= n) into index v-1, until every value that belongs in range sits at its correct index. A second pass finds the first index where arr[i] != i + 1; that index plus one is the answer. If no mismatch turns up, the answer is n + 1.
This is one of the harder array problems reported from Salesforce's OA pool, and it's a fair one: most candidates can write a solution using an extra HashSet in five minutes, but the in-place, constant-space version takes real practice to get right under time pressure. Framed against Salesforce's platform, think of it as generating the next available record number after a batch of records got deleted, without maintaining a separate sorted free-list structure.
The Bulk API needs to handle asynchronous batch jobs: a client submits a large record set (up to millions of rows), Salesforce processes them in the background, and the client polls for completion. Key design decisions: chunking the input into batches of a configurable max size (Salesforce's actual limit is 10,000 records per batch), per-batch retry logic on transient failures, a job status table with states (Queued, InProgress, Completed, Failed), and backpressure when the queue is full.
Multi-tenant constraint: governor limits per tenant. A single tenant submitting 50 million records must not starve other tenants of processing capacity. Rate limiting per organization ID at the job submission layer is the expected answer. Interviewers also ask how you'd handle partial failures: some batches succeed, some fail, and the client needs a clear way to resubmit only the failed records.
// Simplified job submission API sketch
public class BulkJobService {
public JobStatus submitJob(String orgId, List<Record> records, JobConfig config) {
enforceRateLimit(orgId); // Tenant-level governor
String jobId = UUID.randomUUID().toString();
List<List<Record>> batches = partition(records, config.getBatchSize());
jobStore.createJob(jobId, orgId, batches.size());
for (int i = 0; i < batches.size(); i++) {
jobQueue.enqueue(new BatchTask(jobId, orgId, i, batches.get(i)));
}
return new JobStatus(jobId, State.QUEUED, batches.size());
}
public JobStatus getStatus(String orgId, String jobId) {
validateOwnership(orgId, jobId); // Tenant isolation check
return jobStore.getStatus(jobId);
}
}Real-time scenario questions
9OAuth 2.0 as the authorization framework with JWT bearer tokens. Per-tenant authentication configuration: some organizations use SAML SSO through their own identity provider, others use username/password with Salesforce as the IdP. The auth layer must route to the correct IdP based on the login email domain without leaking tenant configuration to unauthenticated callers (domain enumeration attacks).
Reported directly from an MTS interview. Token design matters here: include OrgId in the JWT payload and validate it on every API call before even evaluating the request. This prevents cross-tenant data access even if a token is somehow shared. Session revocation (logging out all sessions for a user when their account is deactivated) requires a token invalidation mechanism beyond just JWT expiry, a distributed token blocklist or short expiry with refresh tokens.
Shared schema with a Tenant ID (OrgId) column on every table. Accounts, Contacts, and Opportunities each have their own table with OrgId as part of the composite primary key. Foreign keys are (OrgId, RecordId) pairs to ensure cross-tenant record references are structurally impossible. Row-level security (RLS) in the database enforces that every query includes an OrgId predicate, either through a query rewriting layer or through database row-security policies.
Custom fields are the hard part. Salesforce lets organizations add their own fields without schema changes. The reported implementation involves a wide "overflow" table with many generic columns (string1, string2, date1, date2...) or a JSON blob column for custom attributes, with a metadata service mapping field names to storage positions per organization. Interviewers at SMTS and LMTS level specifically ask about this trade-off.
Each tenant gets its own rate limit configuration (requests per second, burst size). Use a token bucket per tenant ID. The bucket has a capacity equal to the burst limit and refills at the rate limit tokens per second. On each request, if the bucket has tokens, decrement one and allow the request. Otherwise, reject it. Thread safety: access to each tenant's bucket needs synchronization, but per-tenant locking (ConcurrentHashMap of locks keyed by tenantId) avoids contention between different tenants.
Reported from an SMTS low-level design round. Follow-ups: "how does this work across multiple application server instances?" (shared in-process state breaks down, need Redis or a similar shared store), and "how do you handle a tenant with rate limit 0?" (immediate rejection, log it).
public class MultiTenantRateLimiter {
private final ConcurrentHashMap<String, TokenBucket> buckets = new ConcurrentHashMap<>();
public boolean allowRequest(String tenantId) {
TokenBucket bucket = buckets.computeIfAbsent(tenantId,
id -> new TokenBucket(getConfig(id).getCapacity(),
getConfig(id).getRefillRate()));
return bucket.tryConsume();
}
private static class TokenBucket {
private final long capacity;
private final double refillRatePerNano;
private double tokens;
private long lastRefillTime;
TokenBucket(long capacity, double ratePerSecond) {
this.capacity = capacity;
this.refillRatePerNano = ratePerSecond / 1_000_000_000.0;
this.tokens = capacity;
this.lastRefillTime = System.nanoTime();
}
synchronized boolean tryConsume() {
refill();
if (tokens >= 1.0) {
tokens -= 1.0;
return true;
}
return false;
}
private void refill() {
long now = System.nanoTime();
double added = (now - lastRefillTime) * refillRatePerNano;
tokens = Math.min(capacity, tokens + added);
lastRefillTime = now;
}
}
}Event-driven architecture. When a Salesforce record changes (account updated, opportunity closed), emit an event to a durable event bus (Kafka or Salesforce's own Platform Events). Workflow rules subscribe to relevant event types, evaluate conditions against the record payload, and enqueue actions (send email, update field, call external API). The action executor processes the queue asynchronously with retry and dead-letter handling.
Design considerations interviewers specifically probe: ordering guarantees (if two updates to the same record happen within milliseconds, which workflow fires first?), idempotency (a retry of an action email should not send two emails), and loop prevention (workflow A updating record B which triggers workflow B updating record A is a real problem Salesforce engineers solve). Reported from SMTS and LMTS onsites in 2024-2025.
WebSockets for persistent connections from active browser sessions. A pub/sub layer where each organization has a topic channel, record changes publish events to the org's topic, and all connected clients for that org receive the event. The challenge at scale: 150,000 organizations might each have dozens of active sessions simultaneously. Connection state management for millions of WebSocket connections requires a dedicated connection server tier (not your application server) with sticky routing by org ID.
Alternative for low-latency use cases: Salesforce's actual Streaming API uses long-polling over Bayeux protocol (CometD) as a fallback for environments where WebSocket is blocked by corporate firewalls. Interviewers may ask you to compare the two approaches.
The classic fan-out decision. Fan-out-on-write pushes a new post or record update into every follower's precomputed feed the moment it happens, which makes reads instant but turns a single post from a highly-followed account into thousands of individual writes. Fan-out-on-read does the opposite: store the raw events and merge them into a feed only when a user actually opens it, cheap on write, more expensive per read. Most production designs, and this is the answer Salesforce interviewers want, split the difference: fan-out-on-write for most users, fan-out-on-read for accounts above a follower threshold, so one busy record doesn't create a write storm.
The multi-tenant twist: feed writes have to be rate-limited per organization, not just per user, or one org's internal announcement to a large distribution list can degrade feed latency for every other tenant sharing the same write queue. Interviewers who've actually worked on this kind of system ask about that constraint directly rather than waiting for you to bring it up.
On every update, diff the before and after image of the record at the field level, then write one append-only audit row per changed field: old value, new value, the user who made the change, and a timestamp. Never mutate an existing audit row; the whole point of an audit trail is that it can't be edited after the fact. Index by record ID and timestamp together, since the dominant query pattern is "show me this specific record's history, most recent first."
Two things interviewers push on that candidates often miss. First, unbounded growth: an audit table logging every field change across millions of records grows faster than almost anything else in the schema, so a real design needs an archiving policy, move rows older than some retention window to cold storage instead of keeping everything hot forever. Second, tenant isolation has to be structural here more than almost anywhere else in the platform, since audit data is exactly the kind of record a regulator might ask a customer to produce, and a query that accidentally scans across organizations would be a serious compliance failure, not just a performance bug.
Maintain a separate inverted index, Elasticsearch or a similar search-optimized store, kept in sync with the primary transactional database through the same change-data-capture or event-bus pipeline that drives workflow automation elsewhere in the platform. Writes to Account, Contact, or Case records emit an event; a downstream indexer consumes it and updates the search index asynchronously, so search stays eventually consistent with the source of truth rather than blocking the write path on indexing work.
The permission problem is the part interviewers actually care about. A search index that returns a record a user isn't allowed to see is a data leak, full stop, so either the index has to carry enough permission metadata to filter at query time, or every search result has to be re-checked against the user's actual field and object-level access before it's returned. Given how expensive a full ACL re-check per result gets at scale, most real designs bake a coarse permission signal (which profiles or permission sets can see this record) directly into the index document, then do a final precise check only on the smaller result set that survives the coarse filter.
The query syntax itself, when to write SOQL versus SOSL and how the two actually differ, is a separate skill from designing the retrieval system underneath it. The Salesforce Developer interview questions page covers that distinction directly if your loop expects you to write the query rather than design the service behind it.
A formula field in Salesforce evaluates an expression against a record's field values. A simplified version: support basic arithmetic, string concatenation, IF() expressions, and field references like {!Account.AnnualRevenue}. The implementation has two stages: tokenization (lexer that splits the formula string into numbers, operators, strings, field references, and function calls) and parsing (recursive descent parser building an AST), followed by evaluation (tree traversal substituting field values).
Reported from an SMTS onsite in late 2024. The interviewer specifically asked how you'd handle null field references (return null, propagate up, or substitute a default based on type?), division by zero, and type mismatches in arithmetic. Salesforce's actual formula engine has a strict null propagation rule: any null in arithmetic makes the whole expression null. That's the expected answer here.
Across mock interview sessions on LastRoundAI, the failure pattern that shows up most often in Salesforce prep is not the DSA. It is the system design follow-up: "how would you modify this if multiple tenants were using it simultaneously?" Candidates who prepped for generic system design do fine on the first 80% of the question and then stall when multi-tenancy comes up.
Salesforce's infrastructure serves thousands of companies on shared hardware. Their engineers think about tenant isolation as a first-class constraint, not an afterthought. If your system design answer doesn't mention how you'd isolate one tenant's data from another's, or how you'd prevent one high-volume tenant from degrading performance for others, you haven't finished the question.
The second pattern: candidates undersell trust-related behavioral stories because they sound soft. Salesforce's #1 value is Trust, in their own stated hierarchy, it comes before Customer Success and Innovation. Behavioral rounds here are not warm-up conversations. An interviewer spending 40 minutes on a single "tell me about a time you prioritized security over speed" is not unusual at SMTS level. Have a real story with real stakes.

