
Introduction
Apex Classes and Methods form the foundational layer of custom development on the Salesforce Platform. They provide the flexibility to create sophisticated business logic, automate workflows, and interact with data in ways that go beyond declarative tools. However, managing Apex Classes and Methods in a Salesforce org comes with unique challenges: strict size limitations, various governor limits, the complexity of unit testing, considerations around code coverage, and the inherent need for robust code quality.
This white paper explores a comprehensive set of strategies and best practices for managing Apex Classes and Methods in a Salesforce environment. We will discuss how the Metazoa Snapshot product can help you address common challenges such as identifying unused code, improving code quality and coverage, and avoiding potential pitfalls around governor limits and technical debt. We will also cover advanced techniques for handling large datasets, ensuring effective release management, and planning for long-term solutions to Apex size limits.
What You’ll Learn
- The different ways Apex Classes and Methods can be used to create powerful custom functionality on Salesforce.
- Techniques to handle bulk data within Apex Triggers without exceeding governor limits.
- Best practices for orchestrating synchronous and asynchronous transactions.
- How to utilize interfaces, inheritance, and design patterns for maintainable and reusable code.
- Strategies for identifying and removing unused or duplicate Apex Classes and Methods.
- Methods to improve code quality, including static analysis and AI-assisted remediation.
- Approaches to maintain high code coverage and robust testing, including best practices for test classes.
- Effective release management tactics, including code backups and version control.
- Ways to address the Apex character limit problem over the long term, such as managed packages or org splits.
Understanding Apex Classes and Methods
Apex is an object-oriented programming language purpose-built for the Salesforce Platform. Apex Classes can be referenced by other classes or triggered directly by user interactions, scheduled jobs, or external web service calls. The ability to reuse logic in multiple classes or contexts leads to a more modular design. However, this same flexibility can generate complexity and technical debt if classes and methods are not properly managed.
Methods within Apex Classes can either be public, global, private, or protected. Public and global methods can be called from outside the class or from external packages, while private methods should be used only for internal class logic. Identifying and managing these methods is important to maintain clear boundaries and ensure code reusability.
Limitations and Constraints in Apex Development
Multiple aspects of Salesforce impose limits on Apex:
- Total Character Limit: By default, Apex Classes in your org must collectively fit within a 6 MB unpackaged limit. While Salesforce Support can sometimes grant modest extensions, this limit cannot expand indefinitely, making it important to monitor and optimize code.
- Governor Limits: Salesforce enforces a variety of execution limits to maintain multitenancy. These include maximum SOQL queries, DML statements, CPU usage, heap size, concurrent callouts, and more. Violating any of these can cause runtime errors.
- Other Considerations: Poorly written Apex Triggers can impede performance or cause security vulnerabilities. Additionally, triggers that reference numerous classes or flows can quickly become large, unmanageable, and prone to errors.
Understanding these constraints is paramount for a Salesforce administrator or developer who seeks to keep an organization efficient, secure, and scalable.
Strategies for Handling Bulk Data
Salesforce often deals with large data volumes, whether due to data migrations or everyday business operations:
- Bulk Triggers: Use Trigger.New and Trigger.Old (or their Map counterparts) to handle batches of records. Never assume a trigger runs once per record.
- Efficient DML: Use collections for DML operations to avoid hitting DML governor limits when inserting or updating large datasets. For example, gather multiple records into a list before calling the Database.insert or Database.update methods.
- SOQL Queries: Curb the number of queries inside loops. Move queries outside loops and store results in maps for repeated lookups.
Avoiding Governor Limits with Asynchronous Processing
When a given process requires significant computational power or might exceed synchronous limits, consider:
- Future Methods
- Batch Apex Classes
- Queueable Apex
These asynchronous approaches divide large operations into smaller chunks or postpone heavy lifting. Properly structuring logic in synchronous versus asynchronous contexts is a critical skill for any Salesforce administrator managing custom code.
Overcoming the Apex Character Limit
Given the 6 MB limit, large organizations that accumulate extensive code over time can encounter serious challenges. Here are ways to plan for sustainable growth:
- Short-Term Cleanup: Remove or refactor any Apex Classes or Methods that are no longer being used. The activities in this white paper, such as searching for unused or duplicate methods, will have an immediate effect on code size.
- Long-Term Solutions: Consider adopting managed packages (such as second-generation packaging) for large custom functionalities so that the code no longer counts against your unpackaged limit. In some cases, an org clone, split, or reboot may be necessary for extremely large codebases or monolithic designs.
Identifying Unused Apex Classes and Methods
Unused Apex Classes and Methods can clutter your codebase, increase maintenance costs, and create confusion among development and administrative teams. Metazoa Snapshot’s Forgotten Assets report highlights:
- Classes Not Invoked: The analysis tool examines up to 15 different asset types (including Apex Classes, Pages, Triggers, Aura Bundles, and others) to see which classes are being called. Unused classes might be ripe for removal or decommissioning.
- Methods Not Invoked: Tracks which methods across your org never get used, thereby revealing potential areas to reduce code volume and simplify maintenance.
Removing these assets can reduce your code footprint and free you from the constraints of the 6 MB limit. However, always test any removal or change thoroughly in a Sandbox to avoid interrupting critical processes.
Duplicate Apex Methods
Sometimes developers copy and paste familiar business logic across multiple classes to save time. While duplication might seem convenient in the short term, it can lead to:
- Increased Complexity: Updating multiple copies of the same method can introduce bugs.
- Code Bloat: Duplicates consume space and make the codebase harder to read.
The Metazoa Snapshot Forgotten Assets report scans for duplicate methods, ignoring comments, casing, and whitespace. Finding and centralizing common logic into shared classes or service layers encourages better long-term maintainability.
Code Quality, Security, and Maintainability
High-quality code is not just about correctness; it also needs to meet security standards, follow best practices, and remain easy to maintain.
- PMD Integration: Static analysis tools can detect over 60 distinct issues related to best practices, code style, design, documentation, error handling, performance, and security.
- AI-Assisted Improvement: Using AI-based suggestions, you can automatically repair common code issues, streamline classes, and ensure better coding standards across your org.
- Security: Pay close attention to classes calling external services or being called externally. Confirm that processes involving sensitive data conform to your organization’s security policies.
- Version Control: Use an external version control system—such as GitHub or Bitbucket—to track changes and preserve a complete history, facilitating rollback and collaboration among developers.
Ensuring Adequate Code Coverage
Salesforce mandates a 75% minimum code coverage for deployment to production. This requirement ensures you have a baseline of testing:
- Unit Testing: Write thorough unit tests that cover positive, negative, bulk, and single-record scenarios. This practice ensures resilience and consistent behavior as your org evolves.
- Snapshot Code Coverage Report: This feature surfaces coverage details and helps you identify low-coverage areas. With AI-assisted improvements, you can generate new or enhanced test methods for classes that lack coverage.
- Frequent Testing: Schedule regular regression suites to monitor coverage and catch regressions early in the development lifecycle, ensuring new code adheres to best practices.
Duplicate Lines in Apex Classes
Some teams may resort to adding duplicate lines to inflate code coverage percentages. This is a risky practice that circumvents important testing rules:
- Org Health Scan: Snapshot can detect classes with large numbers of duplicated lines. These lines should be replaced with meaningful tests or consolidated to maintain code quality.
Apex Classes That Have Not Been Executed Recently
Classes that do not execute within a certain timeframe could be outdated, replaced by newer solutions, or simply abandoned:
- Event Monitoring: If you have Salesforce Shield, you can use Event Monitoring data to identify classes triggered in a given period. Though this may not capture internal calls, it provides a strong starting point to find forgotten or legacy code.
Best Practices for Apex Triggers
Effective use of triggers can offer powerful automation, but poorly designed triggers can cause complications and performance bottlenecks:
- Single Trigger per Object: Having more than one trigger on the same object can cause unpredictable execution order and hamper maintainability.
- Mixing Automations: Complex intermingling of triggers, workflows, processes, and flows can cause race conditions or unexpected behavior. Keep triggers simple and lean toward using flows or apex in separate contexts.
- Minimal Logic in Triggers: Triggers should delegate heavy logic to handler classes or frameworks, ensuring readability and segregation of concerns.
- Avoid Recursion: Prevent infinite loops by using static variables to track whether a trigger has already fired once per transaction.
Best Practices for Apex Classes
Clean, modular classes aid collaboration and facilitate quick adjustments when business requirements evolve:
- Use of Interfaces and Inheritance: Embrace the object-oriented principles of inheritance and interfaces to share common logic. Abstract classes can provide shared code, while interfaces can define a contract that multiple classes must fulfill.
- Apex Classes That Call External Services: Validate external callouts for authenticity and resilience. Log any failures and handle them gracefully to protect the user experience.
- Apex Classes with Too Many Lines of Code: Avoid “god classes” by breaking them down into logical service layers or utility classes. Smaller classes are typically easier to understand, test, and maintain.
- Avoid Hardcoding Ids: Referencing object or record Ids directly in code can break deployments between environments. Instead, query or store references in custom settings or custom metadata types.
- Ensure Balanced Test Classes: The total number of Apex Classes should track with the total number of test classes you have. Disproportionately large or small sets could indicate untested classes or an over-dependency on test classes.
- Exception Handling: Wrap critical operations in try-catch blocks for robust error management. Log exceptions in a custom object if necessary, including the class name and line number to expedite debugging.
- Naming Conventions and Comments: Use clear and consistent naming conventions (e.g., CaseTrigger, AccountSyncHelper) and include explanatory comments for complex logic. This practice aids future administrators and developers in understanding and maintaining the code.
Apex Class Permissions
Some classes might require special permissions to be invoked by certain users or processes:
- Profiles and Permission Sets: Use the Profiles and Permission Sets report (in Snapshot or a similar tool) to determine which users can access specific classes. Compare these results with the Forgotten Assets report to find potential misconfigurations or unused code.
Release Management: Backup, Testing, and Deployment
Effective release management ensures that code changes are stable, thoroughly tested, and easily reversible if needed:
- Version Control: Storing your code in a source repository like GitHub or Bitbucket allows multiple developers to work in parallel, maintain revision history, and create stable release branches.
- Backup Strategy: Regular, automated backups of your Apex Classes, metadata, and settings guard against accidental deletions or overwrites.
- Sandbox Migration: Decommission or refine older code in Sandboxes first. Validate changes with testing and user acceptance before deploying to production. This approach significantly reduces the risk of disruption.
- Test Coverage: Ensure each release meets Salesforce’s 75% code coverage requirement and aligns with your internal guidelines for robust test scenarios.
Impact Analysis
Capturing and visualizing the relationships among metadata is key to understanding the broader impact of changes:
- Comprehensive Report: Snapshot generates an impact analysis that covers all major metadata types and dependent relationships, providing much more coverage than standard Salesforce dependency analyses.
- Refactoring Support: If you intend to remove or refactor a class, the impact analysis reveals all references to that class—including triggers, pages, or flows that depend on it.
Additional Strategies for Apex Management
In certain situations, organizations may face extremely large codebases or extended operational requirements that surpass the short-term fixes outlined above:
- Org Clone, Split, or Reboot: Large legacy orgs sometimes undergo a partitioning so that Sales, Marketing, and Support can each reside in a separate org. This might significantly drop the total code footprint in each environment.
- Second-Generation Managed Packages: Packaging your code in managed packages removes it from the unpackaged code limit. This often involves introducing namespaces and adjusting global access modifiers.
Both of these strategies can be substantial undertakings: moving references to new objects or rewriting classes to reference dynamic sObject calls may be necessary. Always weigh the effort and planning necessary for large-scale reorganizations against the potential benefits of a more scalable, flexible, and maintainable org.
Snapshot’s Capabilities for Apex Management
Metazoa’s Snapshot addresses many of the challenges associated with Apex Class and Method management, offering features that allow administrators to:
- Identify Forgotten Assets: Locate classes or methods that are not invoked, or never used, and remove them to reduce code complexity and overhead.
- Detect Duplicate Methods: Consolidate similar code for easier maintenance and reduced size.
- Improve Code Quality: Use PMD-based scanning to detect code style, design, performance, and security issues. Then, apply AI-driven suggestions for automated fixes.
- Enhance Code Coverage: Get insight into your coverage percentages and automatically generate improved test methods for classes that lack testing.
- Assess Org Health: Scan for best practice violations around triggers, workflows, apex calls, and code duplication.
- Perform Impact Analysis: Discover how each metadata component is related to others, enabling safe refactoring and removal of obsolete assets.
- Streamline Release Management: Leverage test deployments to verify that removing a class or changing references will not break your org.
- Support Long-Term Growth: Combine code cleanup, packaging strategies, and advanced org management techniques to address the 6 MB limit in the best way for your business.
Conclusion
Maintaining healthy Apex Classes and Methods is critical for the long-term sustainability of any Salesforce org. By identifying and removing unused code, consolidating duplicated functionality, aligning with best practices, and enforcing rigorous testing, you can foster a secure, performant, and scalable environment.
Metazoa Snapshot offers a suite of specialized reports, AI-assisted improvements, and monitoring capabilities to tackle these challenges head-on. Whether you are addressing the immediate risk of hitting governor limits or planning a more extensive reorganization of your org, Snapshot’s comprehensive features support administrators at every step of the journey.
Ready to learn more? You can download a PDF version of this comprehensive white paper directly from the Metazoa website. We also offer professional services for org splits, clones, or reboots, as well as implementation guidance for managed packages. Don’t hesitate to explore the additional resources and case studies regarding Apex Class and Method management to ensure the very best results as you develop and maintain your Salesforce environment.
Key Takeaways
- Handle Bulk Data in Triggers: Build triggers to manage collections of records efficiently, avoiding governor limit violations.
- Control Governor Limits: Use asynchronous processing (future methods, batch, queueable) for large volumes of data or long-running tasks.
- Adopt a Common Trigger Framework: Centralize logic in handler classes, ensuring that each object has a single well-managed trigger.
- Use Interfaces and Inheritance: Embrace object-oriented principles for flexible, reusable class designs.
- Exception Handling: Wrap complex code in try-catch blocks, storing error details for debugging and analytics.
- Maintain Clean Code: Follow naming conventions, use comments, and regularly scan for duplicate or obsolete logic.
- Backup & Source Control: Keep your code in repositories like GitHub or Bitbucket, track changes, and synchronize multiple deployments.
- Release Management & Testing: Maintain code coverage above 75% and test major changes in a Sandbox before moving to production.
- Address the Apex Limit Early: Monitor your org’s code size and plan ahead by removing dead code or leveraging managed packaging.