Introduction
You've audited your fields. You've removed the unused ones. Your data model is cleaner, and your team feels the difference.
But field cleanup is just the beginning. Technical debt lives elsewhere too—in custom objects that served temporary purposes, in legacy automation gathering dust, and in code nobody remembers writing.
This post tackles the broader landscape of organizational clutter. These are often more impactful to remove than individual fields, but they're also more sensitive. Custom objects support business logic. Workflow rules have been running for years. Code has dependencies you might not immediately see.
We'll walk through how to identify, evaluate, and safely remove these larger sources of technical debt.
The Three Categories of Organizational Bloat
Beyond fields, most technical debt falls into three buckets: custom objects, legacy automation, and orphaned code.
Category 1: Custom Objects That Outlived Their Purpose
Custom objects are created for specific business needs. But businesses evolve. A custom object created for a pilot program might still exist years after the pilot ended. A junction object built to solve a problem before a Salesforce upgrade might be redundant now.
Common Culprits
Pilot/Temporary Objects: Created for limited initiatives that were never integrated into core processes. They still exist, still count against your org limits, and still confuse new team members.
Duplicate Functionality: Built to solve a problem before Salesforce released a native solution. For example, custom objects built to manage hierarchies before territories became more robust, or custom many-to-many junction objects before proper relationship fields were available.
Convenience Objects: Created to make a specific report easier, but now the report isn't used and neither is the object.
Misspelled or Superseded Objects: Sometimes a new object replaces an old one, but the old one is never deleted. Both versions exist, contributing to confusion.
Integration Staging Objects: Created to temporarily hold data during integrations, but the integration is now handled differently (or doesn't exist anymore).
How to Identify Candidate Objects for Removal
Step 1: Audit Object Usage
For each custom object, determine:
- Is it referenced in any active workflows, flows, or process builder?
- Are there records stored in it?
- Is it used in any reports or dashboards?
- Is it referenced in any Apex code?
- Is it included in any page layouts or record types?
- Are any integrations writing to or reading from it?
Step 2: Calculate Impact
- How many records does it contain? (Empty objects are safer to remove)
- When was it last modified? (Objects not touched in 2+ years are suspect)
- Who owns the object? (Interview the owner about its purpose)
- Are there any managed packages depending on it?
Step 3: Document the Business Purpose
This is critical. Talk to the object owner. Why was it created? Is that still relevant? Sometimes you'll find the object serves a purpose you weren't aware of. Other times you'll confirm it's purely historical.
Step 4: Make the Call
Safe to remove immediately:
- Zero records
- No references in automation
- Not in any page layouts
- Not used in reports
- No integration dependencies
- Owner confirms it's not needed
Needs planning before removal:
- Contains records (archive them first)
- Referenced in active processes (update or deactivate those first)
- Part of integrations (migrate to new structure first)
- Managed package dependencies (check package documentation)
Keep it:
- Core to business processes
- Referenced in active automation
- Contains current business data
- Integration dependencies
Safe Removal Process
Don't just delete a custom object. Follow this sequence:
- Backup the data: Export records to a CSV or archive in a data lake if you might need historical reference
- Remove from layouts: Delete the object from all page layouts and record types
- Remove from automation: Deactivate or update any flows, process builder, or workflows that reference it
- Update code: Search Apex codebase and remove references. Update SOQL queries
- Remove from reports/dashboards: Delete or rebuild reports that depend on the object
- Update integrations: Modify any API configurations or middleware
- Notify stakeholders: Send communication that the object is being removed
- Delete the object: Finally, delete the custom object itself
- Document the removal: Note the date, reason, and archived data location
Category 2: Legacy Automation (Workflows and Process Builder)
This is where you often find the most significant technical debt. Workflow rules and Process Builder served Salesforce organizations well for years, but Flows are the future. They're more powerful, more maintainable, and more scalable.
Why Workflows and Process Builder Are Technical Debt
Workflows and Process Builder still work, which is part of the problem. Because they work, nobody removes them. But they're increasingly difficult to maintain:
- Hard to debug: Process Builder especially is a black box when something goes wrong. Logs are unclear, and error messages are cryptic
- Limited conditional logic: Modern business needs require complex conditions that are awkward in Process Builder and impossible in Workflows
- Poor version control: Workflows and Process Builder don't export cleanly to source control, making it hard to track changes or do safe deployments
- Performance issues: At scale, Process Builder can create bottlenecks and recursive issues that are hard to diagnose
- Knowledge debt: Process Builder syntax is being phased out. New Salesforce developers may not even understand how to read it
How to Identify Legacy Automation Candidates
Step 1: Inventory Your Automation
List all active:
- Workflow rules
- Process Builder processes
- (If applicable) Flow-based processes that could be simplified
Step 2: Categorize by Age and Complexity
- Old and simple: Workflows created 5+ years ago that do one thing (e.g., send an email or update a field). These are migration candidates
- Old and complex: Process Builder with many conditional branches. Harder to migrate, but highest payoff
- Recent and redundant: New workflows or Process Builder that duplicate what a Flow does. These should be removed immediately
Step 3: Assess Maintenance Burden
Ask yourself:
- How many people understand how this automation works?
- How often do we modify it?
- When was it last changed?
- Have there been any bugs reported?
- Is it actively being monitored?
Step 4: Identify Candidates for Migration to Flows
Prioritize by impact:
- High-impact, low-complexity: Workflows that run frequently and do simple things (field updates, email alerts). Easy to migrate, high ROI
- High-complexity automations: Process Builder with multiple branches. Harder to migrate but biggest efficiency gains
- Duplicate automation: Any automation that overlaps with existing Flows. Delete immediately
- Poorly documented automation: Automation that nobody remembers why it exists. Review and remove if not critical
The Workflow → Flow Migration Path
Each type of workflow has a Flow equivalent:
Record-triggered workflows → Record-triggered flows
- Most common type of workflow
- Direct migration path
- Flow version is more flexible and performant
Scheduled workflows → Scheduled flows
- Workflows scheduled to run at specific times
- Use scheduled flows for equivalent functionality
- Consider time-based workflow rules → scheduled flows with more flexible scheduling
Approval workflows → Approval flows
- Workflows triggering approval processes
- Can be replaced with Flow-based approval handling
- Note: Salesforce Approval Process is still valid, but consider if Flows could do it better
Process Builder processes → Cloud flows
- Process Builder actions become Flow actions
- Most Process Builder logic maps to Flow decision elements
- Flows provide better visibility and debugging
Migration Checklist
For each automation you're migrating:
Document the current behavior thoroughly
Create the Flow equivalent
Test extensively in a sandbox (including edge cases)
Run both in parallel for a period (if possible)
Monitor the Flow for issues
Get stakeholder approval before deactivating old automation
Deactivate (don't delete immediately) the old workflow/process
Wait 30 days to confirm no issues
Delete the old workflow/process
Document what was replaced and when
Managing Automation Sprawl
Beyond migration, establish governance to prevent future sprawl:
- Automation inventory: Maintain a current list of all active automation
- Documentation requirements: Every new automation must have documented purpose and business justification
- Regular audits: Quarterly review of automation. If something hasn't been touched in 6 months, review whether it's still needed
- Naming conventions: Use consistent names that indicate automation type and purpose
- Owner assignment: Every automation has a named owner responsible for maintenance
Category 3: Orphaned Code and Dead Apex
Apex code is meant to be permanent, but sometimes it becomes obsolete. Managed packages get updated. Integrations change. Business processes evolve. The code persists.
Dead code creates maintenance burden because:
- It increases the complexity of your codebase
- It obscures the code that actually matters
- It increases code review surface area
- It contributes to deployment complexity
- It can cause unexpected side effects if accidentally triggered
Identifying Dead Code
Dead code signals:
- Classes with zero test coverage that nobody has updated in 2+ years
- Batch jobs that never run
- Scheduled jobs with no active schedule
- Classes that are imported but never instantiated
- Trigger handlers for objects that no longer exist
- Code referencing deleted fields or custom objects
- Classes with a comment like "// TO BE DELETED" from years ago
How to find it:
Use your IDE's find/replace feature:
- Search for class names in your Salesforce org code. If a class is never referenced, it's likely dead
- Check Apex Job logs to see what code actually executes
- Review scheduled jobs to identify ones that never run
- Examine trigger handlers for objects; if there are no active triggers on the object, the handler code is dead
Use Salesforce native tools:
- Code Coverage report shows how much of your code is tested
- Deployment validation shows errors if code references deleted items
- Apex Job logs in Setup > Monitoring show what code actually runs
Safe Code Retirement
Don't just delete code. Follow this process:
- Verify it's truly unused: Double-check that it's not called from managed packages, integrations, or other code
- Check test coverage: If tests reference the code, understand why
- Archive the code: Save the code in a comment or version control before deletion (you should have version control—if you don't, address that immediately)
- Remove tests: Clean up associated test classes
- Delete the class: Remove it from your org
- Monitor for issues: Watch for any errors in the next 48 hours
Example: Dead Code Cleanup
Imagine you find a batch job called "UpdateLeadStatusBatch" that:
- Was created 4 years ago
- Has zero references in your active code
- Is scheduled to run daily, but actually never runs (logs show no executions for 12 months)
- Has one test class with 60% coverage
Process:
- Search your codebase: No other code calls this batch
- Search your org: No scheduled apex job for this batch (so it's not running anyway)
- Confirm with team: Nobody knows why it exists or what it was supposed to do
- Archive: Save the code to a GitHub comment or document
- Delete: Remove UpdateLeadStatusBatch and its test class
- Monitor: Check logs for 48 hours—no errors appear
That's safe deletion.
Establishing Code Governance
Prevent code rot:
- Pull dead code removal into your regular sprints: Dedicate time to identifying and removing unused code
- Code review standards: Every code review should include a check: "Is there any dead code we could remove here?"
- Apex Job monitoring: Regularly review scheduled jobs and batch executions. Delete jobs that never run
- Migration tracking: When you replace code with new code (e.g., Workflows → Flows), explicitly mark the old code as deprecated and schedule it for removal
- Documentation: Document the purpose of non-obvious code. If the purpose isn't clear, it's probably dead
Bringing It All Together: The Cleanup Action Plan
After completing Part 1 (field cleanup), you have momentum. Use it.
Week 1:
- Identify 5-10 candidate custom objects for removal
- List all active workflows and process builder
- Search for obviously dead code (zero references, no tests, not executed)
Week 2:
- Document findings
- Assess removal difficulty for each candidate
- Plan migration path for legacy automation
Week 3-4:
- Remove simple custom objects (no data, no references)
- Begin workflow → flow migration for high-value automation
- Delete obviously dead code (run validation to catch dependencies)
Weeks 5+:
- Continue migration work
- Monitor for issues
- Document lessons learned
- Update governance policies to prevent future accumulation
The Payoff
Organizations that complete this cleanup phase report:
- 20-30% reduction in time spent on maintenance: Less code to monitor, fewer automation rules to understand
- Faster deployments: Cleaner codebases deploy faster with fewer errors
- Improved data quality: Fewer competing automations means better consistency
- Better team velocity: New hires understand the org more quickly when there's less clutter
- Lower cognitive load: Teams feel the difference when working with intentional, clean systems
Next Steps: Prepare Your Org for Modernization
By completing Part 1 and Part 2, you've addressed tactical technical debt. Your org is cleaner.
But to truly modernize—to adopt the latest Salesforce capabilities—you need strategy. Part 3 is about preparing your organization for the next generation of features.
We'll discuss how to assess whether your org is ready, identify what modernizations matter most, and plan changes that stick.
Reflection
Before you read Part 3, take time to complete your cleanup work. The real value of this series comes not from reading, but from applying. An org that's intentionally maintained beats a shiny new org with poor governance every time.
What's the oldest piece of automation still running in your org? Share in the comments—we'd love to hear what you find.
