Deployments fail. It's not a matter of if, but when. After debugging hundreds of failed deployments, I've built a checklist that I run through every time. Here it is.
Step 1: Read the Error Message (Really Read It)
I know this sounds obvious, but most people skim the error message. Salesforce error messages are often verbose but contain the exact information you need.
Component Errors:
1. MyController -- Error: Method does not exist or incorrect signature: void getAccounts()
That tells you exactly what's wrong and where.
Step 2: Check the Deploy Status Page
In your target org, go to Setup > Deploy > Deployment Status. The UI sometimes shows more detail than the CLI output, especially for component dependencies.
Step 3: Verify API Versions
Mismatched API versions between components can cause mysterious failures:
grep -r "apiVersion" force-app/ --include="*-meta.xml" | sort -t'>' -k2 | uniq -f1 -c | sort -rn
If you see a mix of 58.0, 59.0, and 60.0, that could be your problem.
Step 4: Check for Missing Dependencies
The most common deployment failure I see:
- Missing custom fields referenced in Apex, flows, or page layouts
- Missing permission sets or profiles
- Missing custom metadata types used by Apex code
Run a targeted retrieve to check what exists in the target org:
sf project retrieve start -o target-org -m "CustomField:Account.My_Field__c"
If it comes back empty, there's your answer.
Step 5: Test Coverage
For production deployments, you need 75% code coverage. But the real gotcha is per-class coverage — even one class at 0% can block you.
sf apex run test -o target-org -l RunLocalTests --code-coverage -w 15
Look at the per-class breakdown, not just the org-wide number.
Step 6: Check for Locked Components
Some components can't be modified if:
- They're part of a managed package
- There's an active deployment already running
- The component is referenced by an active flow
Check deployment status first:
sf project deploy report -o target-org
Step 7: Try a Smaller Deployment
When all else fails, narrow the scope. Deploy one component at a time to isolate which one is causing the failure:
sf project deploy start -o target-org -m "ApexClass:MyController" --dry-run
This is tedious but it works every single time.
The TL;DR
- Read the actual error message
- Check Deploy Status in the org UI
- Verify API versions are consistent
- Look for missing dependencies in the target org
- Check test coverage per-class, not just org-wide
- Rule out locked/managed components
- Narrow the deployment scope to isolate the issue
Bookmark this and come back to it the next time a deployment fails at 11 PM on a Friday. Ask me how I know.