Salesforce unlocked packages stall at scale for three predictable reasons: teams package the easy metadata first and leave the hard dependencies for later, org-wide settings resist modularization entirely, and the CI/CD tooling around packages rarely matches the ambition of the packaging strategy itself. Most orgs get a clean pilot working with two or three packages, then hit a wall when they try to cover the full metadata footprint. The pilot succeeds because it was designed to succeed. Production reality is messier.
I've seen this pattern enough times to call it a stage rather than a failure. Teams don't abandon unlocked packages because the technology is broken. They stall because nobody planned for what happens after month three.
What Unlocked Packages Actually Solve
Unlocked packages give you version-controlled, independently deployable units of metadata without the licensing overhead of managed packages. That's the pitch, and it's a fair one. Instead of one sprawling unpackaged metadata blob, you get discrete packages with their own version numbers, dependency graphs, and deployment history.
For a team running five or six Salesforce developers against a single org, this matters. Unpackaged metadata deployments tend to move as a single unit, which means a small change to one flow can force a full regression pass across features that had nothing to do with it. Packages narrow that blast radius, at least on paper.
The early wins are real. Teams report faster code review cycles because pull requests map to package boundaries instead of a diff against an entire metadata directory. Deployment scripts get shorter. New developers onboard faster because the package structure tells them where things live. None of that is marketing spin. It's just also not the whole story.
The Dependency Wall Teams Hit First
Every unlocked package rollout runs into the same wall: dependencies between packages that weren't obvious when the package boundaries were drawn. A custom object in package A gets referenced by a validation rule in package B. A flow in package C calls an Apex class in package A. None of this shows up as a problem until someone tries to deploy package B to a fresh scratch org and the deployment fails because package A isn't installed yet, or is installed at the wrong version.
Salesforce does support dependency declarations between unlocked packages, but declaring a dependency and managing it well are different skills. Teams that don't invest in a proper package dependency map end up with installation order becoming tribal knowledge, held by whoever set up the pipeline originally. When that person leaves, the sequencing breaks.
The fix isn't complicated, but it's tedious: document the dependency graph explicitly, version it alongside the packages themselves, and test installation order in CI on every build rather than trusting memory. Teams that skip this step are the ones who quietly go back to unpackaged metadata within a year.
Metadata That Refuses to Packagize
Not everything in a Salesforce org fits cleanly into a package. Org-wide settings, sharing rules, certain automation tied to organization-wide defaults, and anything touching Setup-level configuration that Salesforce treats as singleton metadata simply won't split across package boundaries the way custom objects and Apex classes will.
This is where a lot of packaging strategies quietly fall apart. A team draws a clean architecture diagram showing five packages by business domain, then discovers that sharing rules, certain custom settings, and some flow metadata have to live in an unpackaged layer regardless of how the diagram looks. That unpackaged layer doesn't go away. It becomes a permanent second deployment path that has to be coordinated with every package release.
| Metadata type | Packages cleanly? | Common workaround |
|---|---|---|
| Custom objects and fields | Yes | Assign to owning package by domain |
| Apex classes and triggers | Yes | Split by dependency, watch for cross-package calls |
| Sharing rules | Mostly no | Keep in unpackaged config layer |
| Org-wide defaults | No | Manage as environment config, not package content |
| Flows with cross-object references | Partially | Flatten dependencies before assigning to a package |
The teams that handle this well stop pretending the unpackaged layer is temporary. They treat it as a first-class part of the deployment pipeline, with its own tests and its own release gate, instead of a leftover pile that gets pushed manually whenever someone remembers.
Org-Wide Settings and the Governance Gap
Packages solve a code and metadata problem. They don't solve a governance problem, and that distinction is where a lot of the frustration comes from. A package can be reviewed, versioned, and tested. Org-wide settings like currency configuration, business hours, or certain security controls get changed directly in Setup by an admin who has never opened the packaging pipeline and has no reason to.
This creates a split-brain org: half the metadata is under strict version control, and half is governed by whoever last had System Administrator access and a Tuesday afternoon. When something breaks after a release, the first instinct is to check the package deployment logs. Often the actual cause is a manual Setup change that happened outside any tracked pipeline entirely.
Closing this gap takes more than better packaging discipline. It takes a masking and monitoring layer that can flag configuration drift regardless of whether the change came through a package or through the UI. This is exactly the blind spot our MaskEzee and DeployEzee stack is built to cover: package-based deployments handle the code, and drift detection catches the manual changes that packages were never going to see in the first place.
A Practical Path: Package Incrementally
The rollouts that actually stick don't try to convert an entire org to unlocked packages in one project. They pick one bounded domain, usually something like a single Lightning app or a discrete business process with limited cross-object dependencies, and package that first. Order matters here.
- Start with metadata that has few outbound dependencies, so the first package doesn't immediately need three others to install.
- Build the dependency map before writing the package.xml, not after the first failed deployment.
- Run every package install against a fresh scratch org in CI, not just against a long-lived sandbox that already has the dependencies baked in.
- Keep an explicit unpackaged layer with its own owner and its own test coverage, rather than treating it as scrap metadata.
- Set a review point at 90 days to check whether package boundaries still make sense once real usage patterns show up.
That last point gets skipped constantly, and it's the one that saves the most rework. Package boundaries drawn in a planning meeting rarely survive contact with six months of feature development. A quarterly review, even a short one, catches boundary drift before it turns into a rewrite.
Where the Pipeline Has to Catch Up
None of this works if the deployment pipeline treats packages as an afterthought bolted onto a change-set-era process. Sequencing package installs, resolving version conflicts, and rolling back a single package without touching five others requires tooling that understands package dependency graphs natively, not a script that runs `sfdx package install` in a loop and hopes for the best.
This is the gap DeployEzee is built to close. Instead of treating package installation order as a manual runbook, the dependency graph gets encoded once and enforced on every deployment, across every environment from scratch org through to production. Rollbacks target the specific package version that broke, not the entire release. For teams stuck between the promise of unlocked packages and the reality of a fragile install script, that's usually the piece that was missing all along.
Packaging strategy and pipeline tooling have to move together. Teams that get the architecture right but run it through a brittle deployment process end up blaming the packaging model for problems the pipeline actually caused. Get both pieces solid, and the modularity unlocked packages promise on paper starts showing up in the release calendar instead of just the architecture diagram.
Frequently Asked Questions
What is the main difference between unlocked packages and managed packages in Salesforce?
Unlocked packages are meant for internal, org-specific metadata and don't carry the licensing or IP protection features of managed packages. Managed packages are built for ISVs distributing to customer orgs, with namespace protection and controlled upgrade paths. Most consultancies and internal Salesforce teams use unlocked packages because they get version control benefits without the overhead built for external distribution.
Can unlocked packages fully replace unpackaged metadata deployments?
No, not entirely. Certain metadata types, including many org-wide settings and sharing rules, don't package cleanly and have to stay in an unpackaged layer. A realistic goal is packaging the majority of custom objects, Apex, and Lightning components while keeping a small, well-tested unpackaged layer for the rest.
Why do unlocked package deployments fail in CI even when they work manually?
This usually happens because manual deployments rely on an install order the person remembers, while CI runs packages in whatever sequence the script defines. If package dependencies aren't declared explicitly and tested against fresh scratch orgs, CI will surface ordering problems that a familiar sandbox environment was quietly hiding.
How many unlocked packages should a mid-size Salesforce org plan for?
There's no fixed number, but most successful rollouts start with a handful of packages tied to clear business domains rather than dozens of tiny ones. Over-fragmenting packages creates a dependency management burden that outweighs the modularity benefit. A good rule of thumb is to package by team ownership boundary first, then split further only if a specific domain grows too large to review as one unit.
Do unlocked packages help with sandbox seeding and test data management?
Not directly. Unlocked packages manage metadata, not data, so seeding a sandbox with production-like records still requires a separate tool or process. Pairing package-based metadata deployments with a dedicated data generation tool like SproutEzee gives teams both a clean metadata baseline and realistic test data in the same environment.