← Back to Blog

Engineering

Salesforce Technical Debt Is Slowing You Down — Here's How We Remediate It

Every Salesforce org over three years old has technical debt. That’s not a criticism — it’s physics. Business requirements change, teams turn over, platform features evolve, and the decisions that made sense in 2019 are now the reason your deployments take four hours and your batch jobs fail at 2 AM.

The question isn’t whether you have technical debt. It’s whether you know where it is, how much it’s costing you, and what to do about it.

We’ve remediated technical debt in Salesforce orgs ranging from 50-user startups to 5,000-user enterprises. What follows is the methodology we’ve refined over 14 years of doing this work — not theory, but the actual process we run.

What Technical Debt Looks Like in Salesforce

Technical debt in Salesforce is different from technical debt in a general software codebase. The platform’s declarative tools make it easy to build things quickly, which means it’s also easy to accumulate debt quickly. Here’s what we typically find:

Automation sprawl. The org has Workflow Rules, Process Builders, Record-Triggered Flows, and Apex triggers all firing on the same objects — sometimes on the same events. Nobody knows the full execution order. A simple Account update triggers seventeen automations, three of which conflict with each other, and the result depends on which one fires first.

Trigger spaghetti. Multiple triggers per object with no framework. Business logic scattered across trigger bodies instead of handler classes. No bulkification. No separation of concerns. Adding a new requirement means reading 800 lines of trigger code and hoping your change doesn’t break the six other things happening in the same transaction.

Hardcoded IDs and org-specific references. Record Type IDs, Profile IDs, Queue IDs — all hardcoded as string literals across Apex classes. The code works in production but fails in every sandbox because the IDs are different. Deployments require manual find-and-replace. One client had 347 hardcoded IDs across 52 classes.

Test classes that test nothing. 75% code coverage achieved by inserting records and calling methods without a single assertion. The tests pass, the deployment succeeds, and nobody knows whether the code actually works. We’ve seen orgs with 90% coverage and zero meaningful test assertions.

Dead code and abandoned features. Classes that haven’t been modified in five years. Triggers with if(false) blocks that someone was afraid to delete. Custom objects with no records. Fields with null values across every row. Visualforce pages that redirect to Lightning components that redirect to Flows. Each one is a landmine for the next developer.

Governor limit time bombs. Queries inside loops. DML inside loops. Non-selective SOQL on objects with millions of records. The code works fine in the sandbox with 200 records, passes QA, deploys to production, and blows up the first time someone runs a data load or a batch job processes more than 200 records.

No source control. Changes deployed directly through the Setup UI. No version history. No code review. No way to know who changed what, when, or why. Rolling back means someone remembering what it used to look like.

Why Most Remediation Efforts Fail

We’ve inherited more remediation projects than we’ve started from scratch. Here’s why the first attempt usually doesn’t work:

Boiling the ocean. Someone decides to “fix everything” and creates a 200-item backlog. The team spends three months refactoring code that wasn’t causing problems while the actual pain points — the batch job that fails every Tuesday, the deployment that takes four hours — remain untouched. Remediation fatigue sets in, the project loses sponsorship, and the org is marginally better but the worst problems are still there.

Refactoring without understanding. A new developer rewrites a trigger “the right way” without understanding why the original developer made those choices. Turns out the weird conditional logic was handling an edge case that only shows up during fiscal year close. The rewrite is cleaner and also broken.

No regression testing. The team refactors a Process Builder into a Flow and doesn’t test the 14 downstream automations that depend on field values the Process Builder was setting. Everything looks fine in the sandbox. Production breaks on Monday morning.

Treating symptoms instead of causes. The batch job is failing because it hits governor limits. Someone increases the batch size workaround and adds retry logic. The actual problem is a query inside a loop three layers deep in a utility class. The workaround holds for six months, then breaks again when the data volume increases.

Our Remediation Methodology

We break remediation into four phases. Each one delivers standalone value — you don’t need to complete all four to see results.

Phase 1: Assessment

Before touching any code, we need to understand the org. This typically takes three to five days for a mid-market org.

Automation inventory. We catalog every automation on every object — Workflow Rules, Process Builders, Flows, Apex triggers, validation rules, assignment rules, escalation rules. We map the execution order and identify conflicts, redundancies, and dependencies. For every automation, we document: what it does, when it fires, what it depends on, and what depends on it.

Code quality analysis. We run static analysis across the entire Apex codebase. We’re looking for governor limit violations (queries in loops, DML in loops, non-selective SOQL), security vulnerabilities (CRUD/FLS violations, SOQL injection, hardcoded credentials), bulkification failures, and test coverage gaps. Not just coverage percentage — meaningful coverage with assertions.

Architecture review. How is the codebase organized? Is there a trigger framework? Are there shared utilities? Is there a data access layer? Or is every class a standalone island with its own query patterns, its own error handling, and its own way of doing things?

Performance profiling. What are the slowest transactions? Where are the governor limits being approached? Which batch jobs are failing or running longer than they should? We use debug logs, the Query Plan tool, and transaction analysis to find the actual bottlenecks — not the ones people assume.

Dependency mapping. What breaks if we change this? We map the dependency graph between classes, triggers, Flows, and declarative automations. This is the document that prevents the “fixed the trigger, broke the Flow” problem.

The output is a technical debt register — a prioritized list of every issue, categorized by severity and effort, with clear recommendations. This is the roadmap for everything that follows.

Phase 2: Stabilize

Before improving anything, we stabilize what’s broken. This is the phase that delivers the most immediate value.

Fix the fires. The batch job that fails every Tuesday. The deployment that takes four hours because of test failures. The governor limit exception that shows up when someone converts a Lead. These are the issues that are actively costing time and trust. Fix them first.

Establish source control. If the org doesn’t have source control, we set it up — Git repository, branching strategy, CI/CD pipeline with Salesforce CLI. Every change going forward is tracked, reviewed, and deployable. This is non-negotiable. You cannot remediate technical debt if you can’t track what you’re changing.

Write regression tests. Before refactoring anything, we write tests for the current behavior. Not ideal behavior — current behavior, including the bugs. These tests become the safety net that tells us whether our refactoring changed something it shouldn’t have. We use meaningful assertions, not just coverage.

Document tribal knowledge. The developer who built the original system is gone. The business rules are in someone’s head. Before we change anything, we capture what the system is supposed to do — from the people who use it, not just the people who built it.

Phase 3: Remediate

Now we fix things, in priority order based on the assessment.

Consolidate automations. Workflow Rules and Process Builders get migrated to Flows or Apex, depending on complexity. The goal is one automation framework per object — not three different tools all firing on the same event. We’ve seen this single change reduce deployment failures by 60% or more.

Implement a trigger framework. One trigger per object. Business logic in handler classes. Clear separation between trigger context and business logic. We typically use a lightweight framework — enough structure to enforce patterns without over-engineering.

Remove hardcoded references. Record Type IDs become Schema.SObjectType.Account.getRecordTypeInfosByDeveloperName(). Profile references become Custom Permissions. Queue IDs become Custom Metadata. The code becomes sandbox-safe and deployable without manual intervention.

Fix governor limit violations. Queries come out of loops. DML operations get bulkified. Non-selective SOQL gets indexed or restructured. We profile each fix to verify the improvement — not just that it passes tests, but that it performs better under production data volumes.

Replace meaningful test coverage. We don’t just increase the coverage number. We write tests that assert business outcomes — this input produces this output, this edge case is handled, this error condition is caught. Tests become documentation of what the code is supposed to do.

Clean up dead code. Unused classes get deleted, not commented out. Abandoned custom objects get documented and removed. Fields with 100% null values get evaluated and cleaned up. Every piece of dead code is a question mark for the next developer. Remove the question marks.

Phase 4: Harden

The final phase prevents new debt from accumulating.

Code review standards. Every change gets reviewed by a senior developer before deployment. We establish clear standards — no queries in loops, no hardcoded IDs, assertions in every test method, documented business logic. These aren’t aspirational guidelines. They’re enforced in every pull request.

CI/CD pipeline. Automated test runs on every commit. Static analysis as a deployment gate. Org-compare tools to catch changes made outside the deployment pipeline. If someone changes something directly in production, the pipeline flags it.

Architecture decision records. When we make a non-obvious technical decision — why we chose Apex over Flow for this automation, why we structured the data model this way — we document it. The next developer doesn’t have to guess why something was built a certain way.

Monitoring and alerting. Governor limit usage gets monitored, not just reacted to. Batch job durations get tracked. Error rates get dashboarded. You see the next problem forming before it becomes a fire.

What This Costs and What It Saves

We’re not going to pretend remediation is cheap. A full four-phase engagement for a mid-market org typically runs six to twelve weeks of senior engineering time.

But the math works in your favor:

Deployment time. We’ve taken orgs from four-hour deployments with manual steps to 20-minute automated pipelines. If your team deploys twice a month, that’s reclaimed engineering time every single cycle.

Incident response. One client was spending 15 hours per week firefighting batch job failures and governor limit exceptions. After remediation, that dropped to under two hours. That’s a senior developer’s time freed up to build features instead of fighting fires.

Developer velocity. When a new developer can understand the codebase — because there’s a trigger framework, because tests document behavior, because dead code is gone — they’re productive in days instead of weeks. Every hire after remediation onboards faster.

Platform upgrades. Salesforce releases three major updates per year. Technical debt makes every release a risk — will our Workflow Rules still work? Will the deprecated API break our integration? A clean codebase absorbs platform changes without drama.

When to Start

The best time to remediate technical debt was three years ago. The second best time is now.

If you’re seeing any of these signals, your org is telling you it’s time:

  • Deployments regularly fail and require manual intervention
  • Developers are afraid to modify certain classes or triggers
  • Batch jobs fail or time out regularly
  • You can’t confidently answer “what happens when this field changes?”
  • New developers take weeks to become productive
  • Your team spends more time on maintenance than new features
  • You’re hitting governor limits in production

We don’t require a commitment to all four phases. Most clients start with the assessment — three to five days of senior engineering analysis that produces a prioritized roadmap with clear effort estimates. You know exactly what you’re dealing with and what it’ll take to fix it.

From there, you decide what to tackle and when. Some clients run through all four phases in a single engagement. Others take the roadmap and knock out the highest-priority items quarter by quarter. Either approach works.

FAQ

How do you prioritize what to fix first?

We score every issue on two axes: business impact and remediation effort. High-impact, low-effort items go first — these are your quick wins that build momentum and demonstrate value. High-impact, high-effort items get planned into sprints. Low-impact items go on the backlog and get addressed opportunistically. We never fix something just because it’s not “best practice” — it has to be causing a measurable problem or creating measurable risk.

Can we remediate while still building new features?

Yes, and we recommend it. A dedicated remediation sprint that stops all feature work for three months is a hard sell to stakeholders and usually unnecessary. We typically allocate 20-30% of sprint capacity to remediation work alongside feature development. The stabilization work in Phase 2 often removes blockers that make feature development faster.

What if we don’t have source control set up?

That’s the first thing we fix. We can’t safely refactor code without version control, and we won’t try. Setting up Git, a branching strategy, and a CI/CD pipeline with Salesforce CLI is typically a one-week effort that pays for itself immediately.

How do you handle changes that other teams depend on?

The dependency map from Phase 1 is critical here. Before changing any automation or class, we identify every downstream dependency — other automations, integrations, reports, dashboards, managed packages. We write regression tests for those dependencies before making changes, and we coordinate timing with affected teams.

Do you work with managed packages and ISV code?

We work around managed packages — we can’t modify their code, but we can optimize how your custom code interacts with them. If a managed package is the source of technical debt (common with older packages), we’ll document the issue and recommend alternatives or workarounds.

What about org cleanup beyond code — unused fields, stale reports, old page layouts?

Our assessment covers the full org, not just code. Metadata cleanup — removing unused fields, consolidating page layouts, retiring old reports — is part of Phase 3. It’s less glamorous than refactoring Apex, but removing 200 unused fields from an object meaningfully improves the experience for every admin and developer who touches it.