Skip to content

Why Teams Still Refresh Salesforce Sandboxes, and What to Refresh For When Git Is the Source of Truth

Refreshing a sandbox to fix drift hides a problem. When git is the source of truth, what still needs a refresh, and what can SFDMU and CI jobs replace?

TL;DR

  • Most sandbox refreshes happen for one of two reasons: production has drifted from the sandbox, or the team needs realistic data.
  • If your git repository is the source of truth, refreshing to fix drift hides the problem. The repo should already describe what production looks like. If it does not, the leak is in your process, not your sandbox.
  • Users, permission set assignments, queue membership and reference data can all be scripted, with SFDMU or a CI job. Better still, define test users as personas in the repo, so no real person's details are copied anywhere.
  • Never commit production records to git. Mask them on the way out and store snapshots as short-lived, encrypted build artifacts.
  • What still earns a refresh: a Full sandbox for testing at production scale, a few settings no API can reach, and managed packages you have not scripted yet.

What You'll Learn

  • The good and the habitual reasons teams refresh sandboxes
  • What a refresh quietly costs
  • How to replace refreshes with scripts for users, access and data
  • A simple rule for which sandboxes to refresh, and when

The Problem

A Salesforce sandbox is a copy of your production org used for building and testing. A refresh throws the sandbox away and copies production over it again. Salesforce limits how often you can do it: once a day for Developer and Developer Pro sandboxes, every 5 days for Partial Copy, and only every 29 days for a Full sandbox.

Ask a team why they refresh and you usually hear "the sandbox doesn't match production any more". Admins changed something in production directly. A managed package upgraded itself. Salesforce shipped one of its three yearly releases. Testing left the data in a mess. A refresh makes all of that go away in one click, which is exactly why it is popular.

But many of the same teams also say "git is our source of truth". Those two statements do not sit comfortably together. If the repository describes the org, why copy the org?

Common Questions This Article Answers:

  • Why do Salesforce teams keep refreshing sandboxes?
  • If git is the source of truth, do we still need refreshes?
  • Can users, permission sets and data be managed with SFDMU or CI instead?

Quick Answer

You should not need a refresh to get configuration right. Deploy the repo to the sandbox instead, and if production has changes the repo does not, bring them into git and stop them happening outside the pipeline.

You may still refresh to get data, and to reset things no API can reach. Script the rest: personas for test users, SFDMU or CI jobs for access and reference data, masked seed data for records, and scripted package installs. Keep one Full sandbox, refreshed on a release schedule with masking, for testing at production scale.

The good reasons to refresh

Realistic data. User acceptance testing, reports, sharing rules and many bugs only behave properly with production-like records: real volumes, real account hierarchies, real ownership. Partial Copy and Full sandboxes exist for exactly this, and that data goes stale.

A clean slate. Months of half-built features, abandoned test users and broken automation make a sandbox unreliable. Sometimes starting again is the honest fix.

Release timing. Teams time refreshes so a sandbox lands on Salesforce's preview of the next release, or deliberately stays on the current one.

The habits

"Refresh before every release." This rule comes from a time when the sandbox was the source of truth. With changes in git, you can deploy the release branch into the sandbox instead of copying production over it.

Refreshing instead of finding out what drifted. It feels safer, and it hides the real problem: something is changing production outside your pipeline. Next month it will drift again.

What a refresh costs

The click is free. What follows is not:

  • Lost work. Anything built in the sandbox and not saved to git is gone.
  • Broken connections. Integration endpoints, named credentials, scheduled jobs and email settings point at production values or need resetting.
  • Changed emails. Salesforce appends .invalid to every user's email in a new sandbox, so it cannot email real people. That also stops users logging in with their email address at test.salesforce.com, which is now the default login prompt, as described in the login-with-email post.
  • Waiting. A team blocked on a Full sandbox refresh can lose up to a month, because of the 29-day limit.
  • Copied personal data. Partial and Full refreshes copy real customer records into an environment that is usually less protected than production. Without masking, that is a privacy problem, and a serious one for health or financial data.

When git is the source of truth

The repo-first rule is simple: the repository describes the org, and environments are built from it. Refreshing then has a narrow job. Here is what a refresh used to give you, and what replaces it.

What a refresh gave you Repo-first replacement
Configuration matching production Deploy the branch from git. Detect drift by comparing production with the repo on a schedule
Users and their access Personas in the repo, created by a script
Permission set and queue membership SFDMU or a CI job, mapping by name
Reference data (products, price books, settings) Committed as data files with external IDs
Realistic records Masked seed data, stored outside git
Managed packages sf package install with pinned versions, plus scripted settings
Production scale Still a Full sandbox

Users and access: personas, not copies

For development and testing you rarely need real people. You need "a support agent", "a partner user", "a manager who can see the team's records", "an admin without View All Data". Write those down in the repo: the profile, permission sets, role and group membership for each. Then a CI job creates them in every new sandbox.

That gives you three things at once: no personal data in git, the same test users in every environment, and a living test of your access model. If a persona can suddenly see something it should not, a test will tell you.

Copy real users only where real people sign in, usually the acceptance-testing sandbox, and ideally through single sign-on with a provisioning script.

Scripting users and access with SFDMU or a CI job

These are ordinary records, so a data tool can move them:

  • User
  • PermissionSetAssignment, which also covers permission set groups
  • PermissionSetLicenseAssign
  • GroupMember, for queues and public groups
  • UserPackageLicense, for managed-package seats

SFDMU, the SFDX Data Move Utility, handles this well because it maps relationships by name rather than by ID: Profile.Name, PermissionSet.Name, the assignee's username. Four things catch people out:

  1. Usernames are unique across all of Salesforce, so you cannot copy a production username into a sandbox. Transform it, for example by adding .uat. If testers sign in through single sign-on, keep the Federation ID aligned.
  2. Creating and changing users sends email. Welcome emails and email-change verification will fire unless you control them. Salesforce is also changing how email verification exemptions work, which affects bulk user updates.
  3. Permission set assignments can be inserted and deleted, but not updated. A sync job has to work out the difference, then delete and insert, rather than upsert.
  4. Licences have to exist in the target org. Sandboxes normally mirror production's licences, but a missing managed-package licence can fail quietly in a bulk load.

Data: mask it, and keep it out of git

Pulling data from production in CI is fine, with one firm rule: do not commit production records to git. Even "only configuration" data tends to carry names, emails or other details, and git keeps everything forever. Removing something from the current files does not remove it from history.

Instead:

  • Mask on the way out. SFDMU can replace field values with generated ones as it copies: set updateWithMockData on the object and list the fields under mockFields (SFDMU data anonymisation). Salesforce's own Data Mask is the alternative for masking a whole sandbox after a refresh.
  • Store masked snapshots as encrypted build artifacts or in a storage bucket, with an expiry date. Not in the repository.
  • Commit only genuine configuration data, such as products, price books, custom setting values and lookup tables, with external IDs so it loads into any org.

Automate what happens after a refresh

You will still refresh sometimes, so make it routine. Salesforce runs an Apex class implementing the SandboxPostCopy interface after a sandbox is created or refreshed. Use it, plus a CI job, for the repeatable parts: resetting named credentials and integration endpoints, fixing email addresses for the people who need them, re-scheduling jobs, creating personas, and loading reference data. A refresh that takes a day of manual fixing will be avoided until it is overdue. One that fixes itself becomes boring, which is the goal.

What still needs a refresh

Even a disciplined team keeps a few reasons:

  • Production scale. Loading millions of records through the API is slow and uses up API limits. A Full sandbox is still the cheapest way to test performance and data volume against production's real shape.
  • Things no API can reach. A few org settings and features are not fully supported by the Metadata API. Salesforce's Metadata Coverage Report lists them for each feature.
  • Hard-coded record IDs. A refresh keeps record IDs the same as production; scripted data does not. The real fix is to stop hard-coding IDs.
  • Managed packages you have not scripted. Until package versions and their settings are scripted, a refresh is the quick way to match production.

A simple rule for each sandbox

Sandbox Refresh it? Why
Developer, Developer Pro, scratch orgs No. Build or deploy from the repo Disposable. The repo is the truth
Integration or QA Rarely, as a reset Deploy the branch; refresh only when it has rotted
Acceptance testing (often Partial Copy) On a release schedule, masked Realistic data for business testers
Staging or performance (Full) Before major releases, masked Production-scale volume and shape

And one test you can run today: list your last five refreshes and why each happened. If they were for data or a reset, that is healthy. If they were "the sandbox didn't match production", your repository is not yet the source of truth. Fix the drift and the gaps in your scripts, and those refreshes stop.

Frequently Asked Questions

Q: If we deploy from git, do we ever need to refresh a Developer sandbox?

A: Rarely. Deploy the branch, run your persona and reference-data scripts, and work. Refresh only if the sandbox has become unusable, and treat even that as a sign something is being changed outside git.

Q: Can SFDMU really handle users and permission sets?

A: Yes. They are ordinary records. Map by names rather than IDs, transform usernames so they stay unique, control the emails that user changes send, and remember that permission set assignments are inserted and deleted, not updated.

Q: Is it safe to keep test data in the repository?

A: Only data you made up, or genuine configuration such as products and settings. Never production records, even masked ones you are unsure about. Git history is permanent.

Q: Do we still need Data Mask if we use SFDMU masking?

A: If you refresh Partial or Full sandboxes, something has to mask the data that the refresh copies, and that is Data Mask's job. SFDMU masking covers the data you move yourself.

Q: How do we know production has drifted from the repo?

A: Retrieve production's metadata on a schedule and compare it with the main branch. Anything that differs either belongs in git or should not have changed. Both answers are useful.

Key Takeaways

  • Refreshing to fix drift hides the drift. If git is the source of truth, deploy from it and find out why production moved.
  • Script access with personas, not copies of real people. It is safer, repeatable, and tests your access model.
  • Mask data on the way out, and keep it out of git.
  • Keep refreshes for data and scale, on a schedule, with masking, and automate everything after them.

What's Next?

Recommended Reading:

Action Items:

  1. List your last five sandbox refreshes and the reason for each.
  2. Write down your test personas: profile, permission sets, role and groups for each.
  3. Add a scheduled job that compares production metadata with your main branch, and look at the first report.

How often does your team refresh, and why? Leave a comment below.

Resources & References

Responses

Checking your session.

Loading responses.