Refactor Salesforce triggers into handler patterns with automated test generation and deployment. Use when modernizing legacy triggers with DML/SOQL in loops…
When to Use This Skill
Use this skill when you need to:
Modernize legacy triggers with DML/SOQL operations inside loops
Refactor triggers that lack clear separation of concerns
Implement bulk-safe patterns in existing trigger code
Generate comprehensive test coverage for refactored triggers
Prerequisites
Before starting, ensure you have:
Salesforce CLI installed and authenticated to your target org
Python 3.9 or higher installed
The baseline trigger deployed (see Setup section)
Setup
Deploy the baseline anti-pattern trigger to analyze and refactor:
// ❌ Anti-pattern: all logic stuffed into the trigger, with DML/SOQL in loops.
trigger OpportunityTrigger on Opportunity (before insert, before update, after update) {
// BEFORE INSERT: validate Closed Won w/ low Amount
if (Trigger.isBefore && Trigger.isInsert) {
for (Opportunity o : Trigger.new) {
if (o.StageName == 'Closed Won' && (o.Amount == null || o.Amount < 1000)) {
o.addError('Closed Won opportunities must have Amount ≥ 1000.');
}
}
}
// BEFORE UPDATE: if Stage changed, overwrite Description
if (Trigger.isBefore && Trigger.isUpdate) {
for (Opportunity o : Trigger.new) {
Opportunity oldO = Trigger.oldMap.get(o.Id);
if (o.StageName != oldO.StageName) {
o.Description = 'Stage changed from ' + oldO.StageName + ' to ' + o.StageName;
}
}
}
// AFTER UPDATE: when Stage becomes Closed Won, create a follow-up Task
if (Trigger.isAfter && Trigger.isUpdate) {
for (Opportunity o : Trigger.new) {
Opportunity oldO = Trigger.oldMap.get(o.Id);
if (o.StageName == 'Closed Won' && oldO.StageName != 'Closed Won') {
Task t = new Task(
WhatId = o.Id,
OwnerId = o.OwnerId,
Subject = 'Send thank-you',
Status = 'Not Started',
Priority = 'Normal',
ActivityDate = Date.today()
);
insert t; // ❌ DML in a loop
}
}
}
}
Deploy this to your org:
sf project deploy start --source-dir force-app/main/default/triggers
Step 1: Analyze the Trigger
Run the analysis script to identify anti-patterns and generate a report:
python scripts/analyze_trigger.py OpportunityTrigger
The script will output:
DML in loops - Line numbers where DML operations occur inside iteration
SOQL in loops - Line numbers where SOQL queries occur inside iteration
Missing bulkification - Areas where collection-based processing is needed
Complexity score - Overall trigger complexity rating (1-10)
Recommended approach - Suggested handler pattern based on trigger contexts
Review the analysis report before proceeding to refactoring.
Step 2: Review Handler Patterns
Consult the handler patterns reference to understand:
Single-responsibility handlers - One handler class per trigger context
Unified handler approach - Single handler with context methods
Bulk collection strategies - How to aggregate DML/SOQL outside loops
Best practices - Error handling, test boundaries, deployment order
Choose the pattern that best fits your trigger's complexity and team conventions.
Step 3: Refactor the Trigger
Create the handler class using the appropriate pattern from the reference guide:
Extract logic into handler methods with descriptive names
Implement bulk-safe collections for DML operations
Add proper error handling using try-catch or Database methods
Update the trigger to delegate only, passing Trigger context variables
Preserve behavior - ensure the refactored code produces identical results
The trigger should be reduced to simple delegation:
trigger OpportunityTrigger on Opportunity (before insert, before update, after update) {
OpportunityTriggerHandler handler = new OpportunityTriggerHandler();
if (Trigger.isBefore && Trigger.isInsert) {
handler.beforeInsert(Trigger.new);
}
if (Trigger.isBefore && Trigger.isUpdate) {
handler.beforeUpdate(Trigger.new, Trigger.oldMap);
}
if (Trigger.isAfter && Trigger.isUpdate) {
handler.afterUpdate(Trigger.new, Trigger.oldMap);
}
}
Step 4: Generate Tests
Use the test template from assets/test_template.apex to scaffold your test class:
Copy the template and rename for your handler
Implement setup methods to create test data
Write unit tests covering each handler method:
Positive cases with valid data
Negative cases with invalid data
Boundary conditions
Add bulk tests with 200+ records to verify bulkification
Test mixed scenarios where only some records qualify for logic
Required test coverage:
Each handler method must have at least 2 test methods (positive + negative)
At least one bulk test with 200+ records
Overall code coverage must be 100%
Step 5: Deploy and Validate
Deploy the refactored trigger, handler, and tests:
# Deploy all components
sf project deploy start --source-dir force-app/main/default
# Run tests
sf apex test run --class-names OpportunityTriggerHandlerTest --result-format human --code-coverage
# Verify no regressions
sf apex test run --test-level RunLocalTests --result-format human
Validation checklist:
All new tests pass with 100% coverage
No new governor limit warnings in debug logs
Existing functionality remains unchanged
Deployment to production planned with rollback strategy
Troubleshooting
Issue: Tests fail with "System.LimitException: Too many DML statements"
Solution: Ensure handler methods collect DML operations and execute outside loops
Issue: Code coverage below 100%
Solution: Add negative test cases and verify all conditional branches are tested
Issue: Behavior differs from original trigger
Solution: Review Trigger context variables (new, old, oldMap) are passed correctly to handler
Next Steps
After successful refactoring:
Document the new handler pattern in your team's wiki
Update code review checklist to enforce handler patterns for new triggers
Identify other legacy triggers for refactoring using this skill
Consider implementing a trigger framework if managing many triggers
1d:["$don't have the plugin yet? install it then click "run inline in claude" again.