The quick version: change sets are fine for a small org with occasional changes, source-tracked deployment from a repository is right for everyone else, and unlocked packages are worth it only when you have something genuinely modular to version. The mistake is not picking the wrong one — it is having no repository, whichever deployment mechanism you use.
The three options, by what they cost
Change sets. Built in the interface, deployed between connected orgs. No tooling, no repository, and no record of what changed beyond the set itself. They are slow to assemble, easy to get wrong by omission, and impossible to review. For an org with one admin and a change a month, they work.
Metadata deployed from source. Your metadata lives in a repository, and the CLI deploys it. Every change is a commit with an author and a diff, deployments are scriptable, and a pull request is a review. This is where most orgs should be, and the barrier is habit rather than difficulty.
Unlocked packages. Metadata grouped into a versioned package installed into orgs. Real modularity, real dependency management, and real overhead: package versions, ancestry, and a build process to maintain. Worth it when you have several teams or several orgs, and an expensive way to deploy a validation rule.
The question that actually decides it
Not team size. Ask instead: can you tell me what changed in production last month, and who approved it?
If the answer requires opening the Setup Audit Trail and guessing, the problem is not the deployment mechanism, it is that there is no source of truth. Moving to source control fixes that whether or not you ever adopt packages, and it is the single change that makes every later decision easier.
Change sets cannot answer that question by design. That is the real argument against them, and it has nothing to do with how modern they look.
What deploys badly, whichever route you take
Some metadata does not behave, and knowing which saves a release:
- Profiles. They deploy as a merge of whatever your file contains, which means a profile deployed from a sandbox can quietly remove permissions that exist only in production. Deploy permission sets and keep profiles minimal — one more reason for the permission set model.
- Anything referencing data. Record types, queues, dashboards with a running user. The metadata deploys and the reference does not resolve, because the record does not exist in the target.
- Field deletions. Destructive changes need an explicit destructive manifest, and they are irreversible with data attached.
- Flows. They deploy as new inactive versions unless you say otherwise, and an org can end up with an active version nobody deployed.
Tests, and the number everyone gets wrong
A production deployment runs tests. The requirement is 75% org-wide coverage, not 75% per class, and every trigger must have some coverage.
What that means in practice: a deployment can fail because of a class nobody touched, whose coverage dropped when somebody deleted a test elsewhere. Run the tests in a sandbox that mirrors production before the window opens, not during it.
And when a deployment fails on coverage, the fix is a test that asserts something, not a test that runs the code. The difference is invisible to the coverage number and obvious to the next person who breaks that class.
A sequence that works for most teams
- Get the metadata into a repository, even if you keep deploying with change sets for a while. History first, mechanism second.
- Deploy from the CLI to a sandbox until that is routine. Most of the learning is here.
- Add a pull request step, so changes are reviewed before they reach an org. This is where the quality gain actually comes from.
- Automate the sandbox deployment, then production. Once it is scripted it is repeatable, and repeatable is what makes releases boring.
- Consider packages only when you have modules (a shared library, a component reused across orgs), and not before.
Most of the benefit arrives at step three. Teams that jump to step five without the repository end up with versioned packages and no idea what changed.
Frequently asked
Can I mix approaches? Yes, and many orgs do. CLI deployments for developers, change sets for an admin who works in the interface. It works as long as both end up in the repository, and it stops working the moment one does not.
What about change set size limits? They exist and are rarely the binding constraint. The binding constraint is that assembling one by hand is slow and error-prone at any size.
Does source tracking work against production? Source tracking is a scratch org and sandbox feature. Against production you deploy and verify, which is one more reason the pipeline should be boring by the time it gets there.
How do we handle two people editing the same flow? Badly, whatever the tooling. Flows are XML that does not merge cleanly. The practical answer is coordination: one owner per flow per release.
