Bulk Update Salesforce Records Without Data Loader
How to bulk update Salesforce records without Data Loader — when a CSV round-trip is right, and when to build a job that runs itself.
Somebody on your team has done this before. Export the records to CSV. Open the file. Sort, filter, find-and-replace the state abbreviations or the phone formats or the owner IDs of people who left last spring. Save. Re-import. Watch the success file, open the error file, fix the seventeen rows that failed, run it again. If you want to bulk update Salesforce records without Data Loader, the reason usually isn’t that the tool is bad — it’s that you have done this exact afternoon three times now, and you will do it again next quarter.
This is a walkthrough of the other path: turning a recurring cleanup into a job that runs inside the org, on rules you wrote down once. It’s a build, not a workaround, and it takes about an afternoon the first time.
What the CSV round-trip actually costs
The cost isn’t the tool. Data Loader is genuinely capable — Salesforce’s own page describes “support for large files with up to 5 million records with Bulk API and 150 million records with Bulk API 2.0,” and it will insert, update, delete, or export whatever you point it at.
The cost is that the rule lives in a human. The logic that decides which records get touched — accounts in these states, with no activity since this date, excluding the ones flagged do-not-touch — exists as a sequence of spreadsheet filters that one person knows how to reproduce. It is not written down anywhere the org can see. When that person is out, or leaves, the cleanup stops happening, and nobody notices until the data is bad enough to break a report.
The second cost is that the export is a snapshot. Between the moment you pull the CSV and the moment you push it back, reps have been working. Records changed. You are writing yesterday’s values over today’s, and the only thing standing between that and a bad afternoon is how carefully somebody eyeballed a spreadsheet.
When a CSV round-trip is still the right answer
Be honest about this, because the answer isn’t always “build something.” Data Loader is the right tool when:
- It’s a one-time migration. You’re loading history from an old system, or a list you bought, or the output of a merge. It happens once and it’s done.
- The data comes from outside the org. If the source of truth is a file somebody hands you, something has to read a file.
- A human genuinely needs to review each row before it lands. Some cleanups are judgment calls, not rules.
The line is repetition. A task you do once is a task. A task you do every month is a job that hasn’t been built yet — and every month you run it by hand, you’re paying the full price again and accepting a fresh chance to paste the wrong column.
Step 1: Write the rule down before you write anything else
Start in plain language, not in Salesforce. One sentence for the population, one for the change, one for the exceptions:
Update the billing state to the two-letter code on every account where billing state is spelled out in full. Skip accounts flagged do-not-modify. Don’t touch anything else on the record.
That sentence is the actual deliverable. Everything after it is mechanics. If you can’t write it — if the exceptions keep growing as you type — that’s a signal the cleanup is a judgment call and belongs with a person, at least this round.
Write the verification sentence too, because you will want it later: afterward, zero accounts should have a billing state longer than two characters, except the do-not-modify ones.
Step 2: Find the real population with a query first
Before changing a single record, count them. A read-only query tells you whether your rule matches what you think it matches:
SELECT COUNT(Id) FROM Account
WHERE BillingState != null AND Do_Not_Modify__c = false
Then look at the actual values — SELECT BillingState, COUNT(Id) FROM Account GROUP BY BillingState — and you’ll usually find the thing that would have wrecked the CSV run. Six spellings of “California.” Trailing spaces. Records where the state is in the city field. The count is almost never what you assumed, and the gap between your assumption and the count is exactly the part you’d have discovered halfway through a spreadsheet.
If reading query results isn’t second nature, that’s fine — understanding what a query is actually returning matters much more here than being able to write one from scratch, because a wrong query doesn’t throw an error. It returns a confident wrong number.
Step 3: Build it as a batch job, not a one-shot script
Here’s the part that makes this a real solution instead of a faster version of the same afternoon: the change should be a batch job in the org.
Salesforce’s own guidance is explicit about why. Per Trailhead’s asynchronous Apex unit, “Batch Apex is used to run large jobs (think thousands or millions of records!) that would exceed normal processing limits.” With a QueryLocator, “the governor limit for the total number of records retrieved by SOQL queries is bypassed and you can query up to 50 million records.” The default batch size is 200 records, and — the part that matters most — “every transaction starts with a new set of governor limits.”
Translated: a batch job chews through the population in chunks, each chunk with a clean slate, and it does the work in the org against live data rather than against a snapshot you exported an hour ago. The structure is three pieces:
- start — the query that defines the population. This is Step 1’s first sentence, in SOQL.
- execute — what to do with each chunk of records. This is Step 1’s second sentence.
- finish — what happens when it’s done. Send yourself a summary. This is where you find out it ran.
The exceptions from your rule live in the start query’s WHERE clause, not in the execute block — filter them out of the population instead of skipping them mid-flight, so the job’s own record count tells you the truth about what it touched.
Step 4: Prove it on a sandbox before it goes near production
Run it in a sandbox with a copy of real data, and check three things:
- The count. Did it process the number of records your Step 2 query predicted? If the job touched 12,000 records and your count said 400, the rule is wrong, and you just learned that for free.
- The verification query. Run Step 1’s verification sentence as a query. It should now return zero rows.
- The blast radius. Query a handful of the modified records in full and look at every field. The failure mode you’re hunting is the one where the update was correct and something else changed too.
This is also where the tests come in. Salesforce requires test coverage before Apex reaches production, which is not bureaucratic overhead here — a test for a cleanup job is a small set of records that should be changed, a small set that shouldn’t, and an assertion that the second set came out untouched. That assertion is the do-not-modify flag, made mechanical.
Step 5: Run it, then read the result back
Deploy, run against production, and immediately re-run the verification query. Not “check the record you happened to be looking at” — run the query that describes the outcome you specified in Step 1. Zero rows means done.
If it went wrong, the recovery question is the one you should have answered before running: what’s the path back? For a field-level cleanup that’s usually a snapshot of the affected records taken before the run, or the field history if the field is tracked. A cleanup with no rollback path isn’t ready to run, however clean the sandbox test looked.
Step 6: Decide whether it should run itself
Now the question the CSV version never got to ask: should this run on a schedule?
For a lot of cleanups, the answer is yes, and it changes the character of the problem entirely. Batch jobs can be scheduled, so “normalize the state values” stops being a quarterly afternoon and becomes something that runs every Sunday night and mails you a count. The data stops drifting because nothing accumulates for three months before someone notices.
For others — anything with judgment in it — the answer is no, and that’s a fine answer. But you’ve still gained: the job is written down, it runs in minutes when you ask it to, and the rule is visible to anyone who opens the class instead of living in one person’s spreadsheet muscle memory.
Why this hasn’t been built already
Nothing above is hard. It’s maybe four hours of work for someone who writes Apex — a query, a batch class, a test class, a deploy.
Four hours is precisely the problem. Four-hour jobs never clear a development queue, because the queue is sized for projects and this isn’t one. Nobody engages a contractor for an afternoon, and an in-house admin who could write it has a release to get out. So the cleanup goes back to the CSV round-trip, which has the singular advantage of not requiring anyone’s permission. That’s the real reason your org has a list of these — the routing that ignores real territories, the roll-up you can’t get from a lookup, the cleanup nobody schedules. They’re all four-hour jobs and they’re all still open, for the same reason.
What changes when the four hours are available
This is what Sentinel is for. Your AI connects to your org directly, so a cleanup request becomes a working session instead of a ticket: it reads the schema, runs the counting query from Step 2, writes the batch class and its tests, and deploys sandbox-first before anything touches production. You describe the rule in the plain-language sentence you already wrote — normalize billing state to two-letter codes, skip do-not-modify, change nothing else — and review what comes back.
Compared with what a Salesforce developer costs for work that never justifies a contract, the math on small jobs is the whole point. Pricing is $2,500 one-time onboarding on your first Sentinel, plus $500/month per Sentinel.
The reason that’s reasonable rather than reckless is accountability, not restriction. Every action is logged, snapshots are taken before deploys, and Salesforce deploys are sandbox-first with tests required. To be explicit: Sentinel does not prevent your AI from writing a batch job that updates the wrong 12,000 records. It makes the change visible in the log and the previous state recoverable — which, for a bulk update, is the only guarantee that has ever actually mattered. Steps 2, 4, and 5 above are still your job, and they’re the ones that catch a wrong rule.
If you haven’t connected an org to an AI before, that setup is a one-time thing, and it’s the same connection every one of these small jobs runs through afterward. From there, most of what used to require a developer is a conversation.
Start with the cleanup you’ve already done twice
Don’t pick the biggest mess. Pick the one you know by heart — the one where you could describe the filters from memory because you’ve rebuilt them in a spreadsheet more than once. That’s the job with the rule already written; you just haven’t written it down anywhere the org can read.
Do that one first, watch it run, and then look at the rest of the list differently.
Set up your Sentinel — onboarding included.
KEEP READING
Salesforce Automation Without Hiring an Apex Developer
Salesforce automation without a developer is possible when your AI writes the Apex — sandbox-first, tested, and logged. Here's where Flow ends and code begins.
Build a Salesforce Custom Object With AI (Step by Step)
Build a Salesforce custom object with AI — no admin, no Apex. A step-by-step look at what gets created, how it deploys, and how to keep it safe.
Ready to see what AI can do for your business?
Start a Conversation