In the fast-paced world of Salesforce development, it’s tempting to build “God Classes”—those massive, thousand-line Apex triggers or helper classes that handle everything from data validation and external API calls to complex financial calculations. While this might seem efficient in the short term, it creates a maintenance nightmare that scales poorly. The Single Responsibility Principle (SRP) will help us in solving this issue and make maintenance efficient.
What is the Single Responsibility Principle (SRP)?
The Single Responsibility Principle, introduced by Robert C. Martin (Uncle Bob), states:
“A class should have one, and only one, reason to change.”
Notice the definition is not “a class should do only one thing.” It is more precise than that. It says a class should have only one reason to change—meaning it serves only one business concern. If two different stakeholders (a billing manager and a marketing manager) can both ask you to change the same class, that class has too many responsibilities.
In Salesforce terms, if a bug in your email notification logic can break your account validation, your classes are not following SRP.
Let us see one apex class that handles the accounts logic based on triggers.
This class can be changed for the following reasons.
- The business changes its validation rules for accounts.
- The marketing team changes the email template or introduces SMS.
- The sales ops team changes the follow-up task duration from 7 days to 14 days.
- The IT team changes the ERP endpoint or sync logic.
Any one of those changes could accidentally break the other functionality. This is a very small example; in real-life projects, there can be more than 4 reasons to change. Let us make it better using the single responsibility principle
Now each class has exactly one reason to change. If the email address changes, you only touch it. If the follow-up task logic changes, you only touch AccountTaskService. No cross-contamination. This example can be further made configurable using custom metadata changes or custom settings.
Benefits of SRP in Apex
1. Isolated Unit Testing
Each class can be tested independently. You can test AccountValidator without worrying about email side effects. Your test methods are shorter, faster, and more reliable.
2. Parallel Development Without Merge Conflicts
When two developers work simultaneously — one on email templates, one on validation — they work in separate files. No Git merge conflicts, no risk of overwriting each other’s code.
3. Easier Debugging
When the ERP sync breaks, you go directly to AccountERPSyncService. You don’t have to sift through 600 lines searching for the callout.
4. Reusability
AccountTaskService.createFollowUpTasks() can now be called from a Queueable, a Flow-invocable method, or another trigger—not just AccountTriggerHandler. Because it is isolated, it is genuinely reusable.
5. Lower Cognitive Load
Smaller files are easier to understand. A new developer joining the team can read AccountNotificationService in two minutes and understand it completely. They can’t do that with a 600-line god class.
Drawbacks of SRP
Being balanced means understanding the trade-offs too.
1. More Files to Navigate
A project that follows SRP will have significantly more classes than one that doesn’t. For a simple requirement, navigating across AccountValidator, AccountTaskService, and AccountNotificationService, understanding the full picture takes more clicks than reading one class.
2. Over-Engineering Risk
SRP can be taken too far. Creating a separate class for every two-line method is harmful. The principle applies to business concerns and reasons to change — not to individual lines of code.
3. Discoverability
In a large org, a new developer might not know that AccountTaskService exists and might accidentally write duplicate logic in a new class. Good documentation and folder structure mitigate this.
When Should You Apply SRP?
Apply SRP when:
- A class has more than one business concern (validation + notification + sync).
- Multiple different stakeholders (sales, marketing, IT) have reasons to change the same class.
- A class grows beyond 100–150 lines of business logic.
- You are struggling to write a focused unit test because the class has too many side effects.
- The class is changed frequently — frequent changes are a sign of multiple responsibilities.
When Should You NOT Apply SRP?
Avoid overapplying SRP when:
- The class is a simple, cohesive utility with no sign of growing (e.g., a class with two date-formatting methods).
- Splitting the class would create a leaky abstraction—where the split class only makes sense in the context of the original class and can never be reused independently.
- You are working on a one-time script or anonymous Apex block. Architecture principles apply to production code, not throwaway scripts.
- The team is small (1–2 developers), and the org is in early prototyping. Don’t over-architect a proof-of-concept that might be thrown away.
Quick Reference: SRP Decision Table
| Scenario | Apply SRP? | Action |
|---|---|---|
| Trigger handler doing validation + email + DML | Yes | Split into Validator, Notification Service, and DML Service |
| A 50-line utility class with 3 formatting methods | No | Leave as-is—already cohesive |
| A service class changed by 3 different teams | Yes | Decompose by the team/stakeholder boundary |
| Anonymous Apex for a one-time data fix | No | Script-level code, not production architecture |
| A Queueable that queries, transforms, and calls out | Yes | Separate query, transform, and callout responsibilities |
Summary
The Single Responsibility Principle is not about creating tiny classes—it’s about creating focused classes. A class should serve one business concern, be changed by one team, and be testable without the side effects of unrelated logic.
In Salesforce Apex, SRP is most powerfully applied to trigger handlers, service classes, and batch jobs where the temptation to accumulate responsibilities is highest. The payoff — isolated tests, parallel development, and a codebase that new developers can navigate confidently — is one of the highest returns of any design principle.
References
- The Art of Naming (Clean Code for Salesforce Developers)
- Top 10 PMD Issues Salesforce Developers Should Focus on in Apex
- Top Mistakes Developers Make in Salesforce Apex Triggers
- How to Handle Bulkification in Apex with Real-World Use Cases
Other Important Posts
- The Ultimate Guide to Apex Order of Execution for Developers
- How to Confidently Manage Transactions in Salesforce Apex
- How to Manage Technical Debt in Salesforce
- The Ultimate Checklist for Efficiently Querying Large Data Sets
- Optimizing Salesforce Apex Code
- Efficient Ways to Debug Salesforce Platform Events
- How to Handle Bulkification in Apex with Real-World Use Cases
- Handle Heap Size for Apex Code Optimization
- Build Scalable Solutions with Salesforce
- How to Correctly Publish Platform Event Using Salesforce Apex
