Reducing technical debt in Salesforce starts with the native diagnostics. Health Check and Optimizer will point at misconfigurations, security vulnerabilities, and outdated settings you had forgotten were there. What they cannot do is stop the debt arriving in the first place. The ten practices below are the ones that do, and most of them are habits rather than features.
1. Resist unbridled AI-assisted coding
AI assistants produce a lot of plausible Apex very quickly, and plausible is exactly the problem. Treat whatever comes out as a draft that an experienced Salesforce person still has to read and validate. Understanding what the generated code actually does, and why, matters as much as understanding the requirement it was meant to satisfy. It works like driver assistance in a car: it helps, and it does not transfer the responsibility.
2. Document requirements before development
Write the business process and the requirement down before anyone opens a dev org. The document that matters is the "why": what hurts today, and what the business actually needs. User stories and solution design should be built on that. A problem you have written down and understood produces a solution that survives the next change of mind.
3. Stay across business, industry, and ecosystem changes
Watch how your business needs shift, where the industry is heading, and what Salesforce is doing to the platform. See a change coming and you can plan for it. Get surprised by it and you ship something rushed, and rushed work is where most technical debt comes from. Staying informed also keeps the Salesforce investment aligned with business objectives and regulatory requirements.
4. Avoid hardcoding values
Hardcoded values, and IDs above all, are one of the biggest sources of technical debt in an org. IDs differ between sandbox and production, so anything depending on a literal ID breaks the moment it moves. Do it dynamically instead. A Get Records element in Flow will fetch the Record Type ID or whatever configuration value you need, and the solution stops caring which environment it is running in.
// Example of avoiding hardcoding in Apex
List<Account> accountsToUpdate = new List<Account>();
// Instead of hardcoding a Record Type Id:
// Id recordTypeId = '012xxxxxxxxxxxx';
// Query for the Record Type dynamically:
RecordType rt = [SELECT Id FROM RecordType WHERE SobjectType = 'Account' AND Name = 'My Custom Account Type' LIMIT 1];
for (Account acc : [SELECT Id, Name FROM Account WHERE RecordTypeId = :rt.Id]) {
acc.Some_Field__c = 'New Value';
accountsToUpdate.add(acc);
}
update accountsToUpdate;
5. Build a culture of sound architecture
Building exactly what the user asked for, word for word, is the fastest route to an org nobody can maintain. The job is to find the need underneath the request, look at what the org already does, and design something that still scales as the business grows. That means questioning requests and putting a different implementation on the table. Write architectural decisions down with the reasoning behind them. The next person to touch the org treats the platform better when they know why it looks the way it does.
6. Learn to say no strategically
Turning a request down, or offering something else instead, is part of managing technical debt. It protects the health of the org. A team asking for a heavily customized Record Type and a pile of automations to go with it is often better served by a standard process that works for everyone. Judge each request on its long-term impact and whether it scales.
7. Make code reviews mandatory
Every piece of Apex and every significant declarative automation should go through review. Reviews surface problems early, keep coding standards honest, and spread knowledge around the team instead of leaving it in one person's head. In practice they are where hardcoded values, inefficient SOQL, and shortcuts around best practice get caught while they are still cheap to fix.
8. Refactor and re-architect when it is needed
Some debt is unavoidable the first time you build something. Go back through existing code and configuration on a schedule and look for the parts that have turned inefficient or painful to change, then book real time to refactor or re-architect them. Small problems left alone become large ones.
9. Maintain test coverage that means something
High Apex coverage is not optional if you care about debt. Good unit tests prove the code works, document what it was meant to do, and give you a net when you refactor. Cover the edge cases, the negative paths, and how the code behaves at governor limits, not just the happy path that gets you to the percentage.
@isTest
private class MyApexClassTest {
@isTest
static void testMyMethod() {
// Setup
Account acc = new Account(Name='Test Account');
insert acc;
// Execute
Test.startTest();
MyApexClass.performAction(acc.Id);
Test.stopTest();
// Assert
Account updatedAcc = [SELECT Id, Custom_Field__c FROM Account WHERE Id = :acc.Id];
System.assertEquals('Expected Value', updatedAcc.Custom_Field__c);
}
@isTest
static void testMyMethodWithEdgeCase() {
// Setup for an edge case
Account acc = new Account(Name='Another Test Account');
// ... add logic for edge case ...
insert acc;
// Execute
Test.startTest();
MyApexClass.performAction(acc.Id);
Test.stopTest();
// Assert for edge case
// ... assertions ...
}
}
10. Run Health Check and Optimizer
Health Check and Optimizer ship with the platform, so the only cost is the time it takes to read the output. They flag security vulnerabilities and settings worth fixing, and they are good at showing where debt is accumulating behind outdated or suboptimal configuration.
Leave a Comment