Trigger handler induced design damage

There is a great series of discussions including videos on the subject of the benefits and costs of test driven development. Part of that discussion is the notion of Test-induced design damage where pursuing one principle – testability – to the exclusion of other principles results in damage to the design.

An area in Salesforce where I see risk of damage is the pursuit of this:

Another widely-recognized best practice is to make your Triggers logic-less. That means, the role of the Trigger is just to delegate the logic responsibilities to some other handler class.

quoted from Trigger Frameworks and Apex Trigger Best Practices and often paraphrased. (The term “best practice” is sometimes used to give an idea unmerited gravitas.)

There can be benefits in using separate classes, typically where the logic being implemented has enough complexity to require multiple methods and static constants. Or if a choice has been made to use a carefully selected trigger framework (and not an unproven local invention). And a mixed model – where some code is in the trigger and other parts are moved to classes as needed – also works.

But there are costs:

  • A reduction in cohesion: the logic has moved to a class that is separate from the execution context that includes a number of subtleties as documented in Trigger Context Variables.
  • Over time as more logic is added, the chances of queries being repeated in separate handler class methods or separate handler classes goes up.
  • Some naming challenges: what should the handler class, methods and method parameters be called? And the clutter of more top level classes in an org.
  • Should the test relate to the trigger (the specific context the logic has to run in) or the handler class (with no assumed context)?
  • A risk of failure to bulkify. A handler class that has methods that accept single values rather than collections is an obvious red flag. But with the trigger context not so directly in view when writing the handler class and logic running several method calls down, bulkification can be missed.

Code in a trigger is just like any other code and should be written to Do The Simplest Thing That Could Possibly Work. If the problem being solved can be solved cleanly by keeping the code in the trigger then do that. If not, introduce a class to solve the part of the problem that needs a class. Be prepared to refactor when logic is added later.

The Apex Code Best Practices documentation says “Bulkify your Helper Methods” – if you have any. Those helper methods and classes are not assumed.

Don’t damage your code base by introducing a pattern where the benefits are outweighed by the costs.