The L1 round at Persistent Systems is the first technical screening for a Salesforce Developer role. It is a spoken round, so you explain your reasoning rather than write code on a screen. The 19 questions below come from one real L1 round held on 3 March, recorded question by question as they were asked. The round opens with two behavioural questions, then goes deep on Lightning Web Components, asynchronous Apex, DevOps practice and Service Cloud routing.
The two opening questions
Candidates treat these as a warm up. The panel does not. These two answers set the panel's expectation of your level, and every technical question that follows is pitched against it.
Question 1: Tell me about yourself
Answer in a fixed order so you never ramble: introduction, experience, technologies, project summary. Ninety seconds is the right length. Cover your current role, how long you have worked on Salesforce, the skills you actually use (Apex, LWC, Flows, integrations), and the projects you have delivered.
A working version sounds like this:
"I am a Salesforce Developer with 2.5 years of experience, currently on a Service Cloud implementation for an insurance client. My day to day work is Apex triggers, Lightning Web Components and Flow. I own the case assignment logic and two custom LWC screens used by the claims team. Before Salesforce I was in banking operations, which is why I pick up business requirements quickly."
Notice what is missing. No list of fourteen technologies. Our trainers see this constantly: the candidate recites a resume wider than what they can defend, and the panel picks the weakest item on that list and spends ten minutes there. Say only what you can survive five follow up questions on.
Question 2: Explain your recent project
This is the question that decides the round. The interviewer wants to know what role you played, which Salesforce clouds or technologies were used, what problems you solved, and what you actually built or configured.
Answer using Situation, Approach, Reason, Result. The shape is what stops the answer turning into "actually sir basically in our project we had one requirement and basically...", which is how most candidates open and how most of them lose the panel.
Situation. "The client is a US insurance provider. Agents logged claims by email and the service team re keyed them into Salesforce. Turnaround was four days."
Approach. "We built a claim intake process on Service Cloud. I wrote the Apex trigger on the Claim object that creates and assigns the Case, and a Lightning Web Component showing claim history on the Case record page. It calls Apex imperatively on button click, because the agent wants history only on demand."
Reason. "We chose a trigger over a Record Triggered Flow because the assignment logic needed a callout to the client's policy system and we wanted retry handling in Apex. Imperative Apex rather than a wire, because the data is not needed on load."
Result. "Turnaround came down to under a day. About 40 agents use the component."
Four short blocks, and they answer the next three questions before they are asked. The trainer's rule in class is blunt: if you cannot name the object, the field, the reason you rejected the alternative, and your own commit in that repository, do not put the project on your resume. Persistent panels go five levels deep, and invented experience does not survive level three.
What the interviewer is actually checking in the opening
- Whether your resume and your spoken account of the project agree
- Whether you can name specific objects, fields and components, or only speak in generalities
- Whether you can separate your own contribution from the team's
- Whether you know why your design was picked over the alternative
Lightning Web Components
Six of the 19 questions were LWC, making this the heaviest block in the round.
Question 3: What are LWC decorators?
Decorators are annotations on a property or method that change how the framework treats it. LWC has three, all imported from the lwc module.
@api makes a property or method public, so a parent component can set it or call it.
@track makes changes inside an object or array reactive. Since Spring '19 all fields are reactive by default, so @track is needed only when you mutate a property of an object or an element of an array in place instead of reassigning the whole value.
@wire connects the component to Salesforce data, through a Lightning Data Service adapter or an Apex method.
import { LightningElement, api, track, wire } from 'lwc';
import getContacts from '@salesforce/apex/ContactController.getContacts';
export default class ContactList extends LightningElement {
@api recordId; // set by the parent or the record page
@track filters = { city: '' }; // mutated in place, so tracked
@wire(getContacts, { accountId: '$recordId' }) contacts;
}
A common follow up: why does recordId need @api on a record page component? Because the record page acts as the parent and sets it from outside.
Question 4: Difference between @wire and imperative Apex
Both call the same Apex method. The difference is who triggers the call and what you may do with the result. Note that a cacheable=true method cannot perform DML, which is why saves always go through imperative calls.
| @wire | Imperative | |
|---|---|---|
| When it runs | Automatically, when the component loads and again whenever a reactive parameter changes | Only when your JavaScript calls it |
| Control | Framework controls timing | You control timing |
| Typical use | Loading records on page load | Button click, save, search, anything after a user action |
// @wire, reactive, runs on load and whenever recordId changes
@wire(getContacts, { accountId: '$recordId' })
wiredContacts({ data, error }) {
if (data) { this.contacts = data; }
else if (error) { this.error = error; }
}
// Imperative, runs only on the click
handleSearch() {
getContacts({ accountId: this.recordId })
.then(result => { this.contacts = result; })
.catch(error => { this.error = error; });
}
The rule to state: @wire for loading records automatically, imperative for a button click or anything that writes data.
Question 5: LWC lifecycle hooks
Lifecycle hooks let you run code at fixed points in a component's life.
constructor() runs when the instance is created. The first statement must be super(). No DOM access here, and parent properties are not set yet.
connectedCallback() runs when the component is inserted into the DOM. Fire an imperative Apex call, subscribe to a message channel, or read @api values here, because by now the parent has set them.
renderedCallback() runs after every render, not just the first. Guard it with a boolean flag if the work should happen once, otherwise changing a tracked property inside it loops.
disconnectedCallback() runs when the component is removed from the DOM. Unsubscribe from message channels and clear intervals here.
export default class DemoComponent extends LightningElement {
hasRendered = false;
connectedCallback() {
// safe place for imperative Apex and subscriptions
}
renderedCallback() {
if (this.hasRendered) { return; }
this.hasRendered = true;
// one time DOM work
}
disconnectedCallback() {
// unsubscribe, clear timers
}
}
Interviewers often extend this to parent and child order on first load: parent constructor, parent connectedCallback, child constructor, child connectedCallback, child renderedCallback, then parent renderedCallback. The parent renders last because it waits for its children.
Question 6: Parent to child communication in LWC
The parent passes data down through public properties, declared in the child with @api and set as attributes in the parent template.
// child.js
import { LightningElement, api } from 'lwc';
export default class Child extends LightningElement {
@api contactName;
@api showDetail = false;
@api refresh() { // a public method, callable by the parent
// reload logic
}
}
<!-- parent.html -->
<c-child contact-name={selectedName} show-detail="true"></c-child>
Note the naming. A camelCase property in JavaScript becomes kebab case in the template, so contactName is written as contact-name. Getting this wrong silently passes nothing, and it is a favourite debugging question.
The parent can also call a public method on the child.
// parent.js
handleRefresh() {
this.template.querySelector('c-child').refresh();
}
Question 7: Child to parent communication in LWC
The child raises a custom event and the parent listens for it. The child dispatches, the parent declares a listener on the child tag, the parent handler runs.
// child.js
handleSelect() {
const selected = new CustomEvent('contactselect', {
detail: { contactId: this.contactId }
});
this.dispatchEvent(selected);
}
<!-- parent.html -->
<c-child oncontactselect={handleContactSelect}></c-child>
// parent.js
handleContactSelect(event) {
this.selectedId = event.detail.contactId;
}
Two details worth saying out loud. The event name must be lowercase with no hyphen or camelCase, because HTML attributes are case insensitive. The payload travels in event.detail.
Question 8: Communication between unrelated components
When two components have no parent and child relationship, use Lightning Message Service. One component publishes to a message channel and any other on the page subscribes. The channel is a metadata file of type LightningMessageChannel.
import { publish, MessageContext } from 'lightning/messageService';
import RECORD_CHANNEL from '@salesforce/messageChannel/RecordSelected__c';
export default class Publisher extends LightningElement {
@wire(MessageContext) messageContext;
handleClick() {
publish(this.messageContext, RECORD_CHANNEL, { recordId: this.recordId });
}
}
LMS is the right answer because it also crosses between LWC, Aura and Visualforce on the same page, which the older pub sub pattern cannot do.
What the interviewer is actually checking in the LWC block
- Whether you know when code runs, not just what it is called. Wire versus imperative is a timing question
- Whether you have hit the kebab case and lowercase event name problems, which only happens if you have written components
- Whether you can name a real component you built and what data it showed
- Whether you know
cacheable=truerules out DML
Asynchronous Apex
Question 9: How many records can be processed in Batch Apex?
Batch Apex splits large data volumes into chunks, and each chunk runs execute() with a fresh set of governor limits. That is the whole point of using it.
The default chunk size is 200 records. Pass a smaller scope in Database.executeBatch(new MyBatch(), 50) when each record does heavy work such as a callout. With Database.QueryLocator the job can process up to 50 million records.
global class AccountUpdateBatch implements Database.Batchable<sObject> {
global Database.QueryLocator start(Database.BatchableContext bc) {
return Database.getQueryLocator('SELECT Id, Rating FROM Account');
}
global void execute(Database.BatchableContext bc, List<Account> scope) {
for (Account a : scope) { a.Rating = 'Warm'; }
update scope;
}
global void finish(Database.BatchableContext bc) {
// send email, log the result, or chain the next batch
}
}
start() collects the records, execute() runs once per chunk, finish() runs once at the end.
Question 10: Can we call a batch inside another batch?
Yes, but not from anywhere you like. You cannot call Database.executeBatch() from execute(). You can call it from finish(). That is the batch chaining pattern, and it is how you run job B only after job A completes.
global void finish(Database.BatchableContext bc) {
Database.executeBatch(new ContactUpdateBatch(), 200);
}
Use it when the second job depends on data the first job wrote.
Question 11: Can we call a future method inside another future method?
No. A future method cannot call another future method, and cannot be called from a batch execute() either. This is a platform restriction that stops asynchronous work multiplying without limit. The alternative is Queueable Apex, which supports chaining and has replaced future methods in most new code.
Question 12: Queueable job chaining
Queueable Apex lets one asynchronous job start the next after it finishes. You enqueue the child from inside the parent job's execute() method.
public class FirstJob implements Queueable {
public void execute(QueueableContext context) {
// do the first piece of work
System.enqueueJob(new SecondJob());
}
}
Queueable is more flexible than a future method. It accepts non primitive types such as sObjects as instance members, and it returns a job Id you can monitor in AsyncApexJob. One caution: you can enqueue only one child job from a running queueable job, and in a Developer Edition or Trial org the chain depth is capped at five.
Question 19: Difference between Database.Iterable and QueryLocator
Both supply records to a batch job from start(), but they behave differently.
| Database.QueryLocator | Iterable | |
|---|---|---|
| Source | A SOQL query | Any custom collection or logic |
| Record ceiling | Up to 50 million records | Subject to the normal query row limit of 50,000 |
| When to use | Straight SOQL over standard or custom objects | Data from a callout, a wrapper list, or records built by complex logic |
| Written as | Database.getQueryLocator('SELECT ...') |
A class implementing Iterable<sObject> |
The short version: QueryLocator for SOQL and large volumes, Iterable for custom data sources such as API responses.
What the interviewer is actually checking in the async block
- Which context each job can be started from, the part you only learn by hitting the error
- Whether you can give a real reason you picked Batch over Queueable
- Whether you know the 50 million figure applies to QueryLocator and not Iterable
- Whether you can debug an async failure, such as a batch that finished with zero records processed
DevOps and release management
Question 13: What are CI/CD pipelines?
CI/CD stands for Continuous Integration and Continuous Deployment. A pipeline automates the path from a developer's commit to a deployed org: code integration, automated testing, deployment.
In a Salesforce project the pipeline runs on every pull request. It authenticates to a sandbox using Salesforce CLI, deploys the metadata, runs the Apex tests, and blocks the merge if tests or coverage fail. Releases become repeatable and errors surface before UAT.
Question 14: How do you manage Git code changes?
Git is the version control system underneath the pipeline. The practice is much the same on most Salesforce teams.
- Create a feature branch from develop, named after the story, for example
feature/SF-214-claim-assignment - Commit your metadata changes with a message naming the story
- Raise a pull request into develop, where a reviewer reads the diff and the pipeline runs the tests
- Merge into develop, then promote develop to the release branch for UAT and production
Name the branching model your team used and who approved your pull requests. "Yes I know Git" followed by silence when asked where you used it is how this question is usually failed.
Question 16: DevOps tools used in Salesforce
The tools that come up are Copado, Gearset, AutoRABIT, GitHub Actions and Jenkins. Copado, Gearset and AutoRABIT are Salesforce specific and handle metadata comparison, deployment and rollback. GitHub Actions and Jenkins are general automation servers that drive Salesforce CLI commands.
Name the one your project used and say what you did in it, for example raising a Copado user story and promoting it to QA. Naming five tools you have never opened invites a follow up you cannot answer.
Integration and Service Cloud
Question 15: How can you show a third-party screen in Salesforce?
Three options.
Lightning Web Component with an iframe. Embed the external URL in an iframe. The domain must be added to CSP Trusted Sites in Setup, otherwise the frame is blocked.
Visualforce page. Same idea on the older framework, still common where legacy code exists.
Canvas app. Canvas handles the authentication handshake with OAuth or a signed request, so the external application knows which Salesforce user is viewing it.
The distinction to state: an iframe only displays a page, Canvas passes user context to it.
Question 17: What is Omni-Channel routing?
Omni-Channel is a Service Cloud feature that pushes work to agents automatically instead of letting them pick from a queue. Work items include Cases, Chats, Leads and Tasks.
It rests on three pieces of setup. A Service Channel maps an object to Omni-Channel. A Routing Configuration sets priority, routing model and the capacity each item consumes. A Presence Configuration controls agent status and how much work they can hold.
Question 18: Types of routing in Omni-Channel
Queue-based routing. Work items sit in a queue and Omni-Channel assigns them to available agents who are members of that queue. This is the simpler model and covers most implementations.
Skills-based routing. Each work item carries required skills and Omni-Channel assigns it only to an agent holding those skills, for example language support or product knowledge. Use it when agents are not interchangeable. It costs more to maintain, because every agent's skill set has to stay current.
What the interviewer is actually checking in the DevOps and Service Cloud block
- Whether you have been part of a release, or only heard about one
- Whether you can name your branch, your reviewer and your deployment tool
- Whether you know that Canvas and iframe solve different problems
- Whether you can configure Omni-Channel, or only define it
How to prepare for this round
Only a handful of these nineteen questions ask for a definition you can memorise. The rest ask when something runs, why you chose it, and what you did with it.
So do two things before this panel. Write your project story out in Situation, Approach, Reason, Result and say it aloud until it fits in ninety seconds. Then take every line on your resume and ask what the fifth follow up question would be. Whatever you cannot answer, remove it or learn it.
If you want the project experience these answers assume, our Salesforce training in Pune with placement support is built around live project work and mock interview rounds run the way this one was.