Not verified hands-on. This article was written from primary documentation rather than reproduced in an org, so it carries no tested-release badge. If you run it and something differs, say so in the comments and it gets tested and corrected.

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.

Three ways to move metadataOrdered by how much history they leave behind, which is the only difference that matters after year one.Change setsNo tooling, no repository, no record of what changed beyond a list of components. Fine for one adminand a handful of changes; unrecoverable as a history.Source-tracked deploymentMetadata in git, deployed by CLI or pipeline. Every change has an author, a review and a diff. Thisis the answer for most teams.Unlocked packagesVersioned units with declared dependencies. Worth it when you ship the same thing to severalorgs, or need to remove a feature cleanly.The deciding questionCan you tell me what changed in production last month, and who approved it? Whicheveroption answers that is the one you need.Teams pick by effort and regret it at audit time. Pick by what the option can tell you six months later.
Three ways to move metadata, ordered by how much history each one leaves behind.

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

  1. Get the metadata into a repository, even if you keep deploying with change sets for a while. History first, mechanism second.
  2. Deploy from the CLI to a sandbox until that is routine. Most of the learning is here.
  3. Add a pull request step, so changes are reviewed before they reach an org. This is where the quality gain actually comes from.
  4. Automate the sandbox deployment, then production. Once it is scripted it is repeatable, and repeatable is what makes releases boring.
  5. 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.