Clean Apex Code: Boolean parameters—Pattern or anti-pattern?

This is an exclusive preview of my upcoming book, Clean Apex Code.


The use of boolean parameters is cause for a lot of debate on the internet. The main criticism is that a boolean parameter makes a function do more than one thing. A common example of this is the insert method of the Database class from the standard Apex library. One of the versions of this method has the following signature

insert(sObject[] recordsToInsert, Boolean allOrNone)

The allOrNone parameter determines whether an error should cause the entire operation to fail, or whether we want to allow for partial success. By definition, those are two different things. For illustration purposes, this is how the parameter is used


//allows partial success
Database.SaveResult[] results = Database.insert(newAccounts,**false**);

//atomic, a single failure causes the entire batch to fail
Database.SaveResult[] results = Database.insert(newAccounts,**true**);

We can’t see what the code looks like inside of the Database.insert() method, but we can imagine it looks something like this:

Database.SaveResult[] insert(sObject[] recordsToInsert, Boolean allOrNone) {
		//some general logic here
		
		if (allOrNone) {
		    //some logic here
		    //that throws an exception if the insert fails
		}
		else {
		    //some logic here
		    //that wraps failures inside the SaveResult object
		    //and does not throw an exception
		}
}

The split that occurs inside of the if statement is where two different things happen. And that’s what boolean parameters do: “if this is true, do this, otherwise, do that”.

That doesn’t mean that boolean parameters are bad. They can be useful constructs provided that the following conditions are met:

  • They enhance the main behaviour of the method
  • They operate at the same level of abstraction
  • There’s only one boolean parameter. If more are needed, an alternative technique must be used (we’ll get to this shortly)

Let’s see if the allOrNone parameter meets this criteria. First, the parameter enhances the behaviour of the DML operation by determining how errors should be handled; it isn’t a completely unrelated option. It fits the story. For example, I could say out loud “I want to insert accounts and handle failures gracefully” and it makes sense. This means that the parameter operates at the same level of abstraction as the method being enhanced. Finally, this is the only boolean parameter that you can pass to the insert() method. If you need to pass more, you have to use the DMLOptions class, which we will discuss soon. In short, the allOrNone parameter has the characteristics of a well defined boolean parameter.

To understand what a poorly designed boolean parameter looks like, let’s imagine this fictional parameter:

insert(sObject[] recordsToInsert, Boolean writeToDebugLog)

In this example, the writeToDebugLog parameter determines whether the operation details are written to the debug log. This option doesn’t enhance the behaviour of the DML operation; writing to the debug log is an unrelated concern. Furthermore, whether the operation writes to the debug log or not should not be a concern of the method calling Database.insert() . Writing to the debug log is at a much lower level of abstraction than the general details of the DML operation. Finally, if we were to need more options, the following method signature would also be an anti-pattern

Database.SaveResult[] insert(
	sObject[] recordsToInsert, 
	Boolean writeToDebugLog, 
	Boolean allOrNone
)

The inclusion of two boolean parameters means the method is now doing many more things at different levels of abstractions.

Alternatives to boolean arguments

Some argue that boolean parameters shouldn’t be used at all, and that instead, you should create one method for each of the behaviours you want to provide. In that scenario, we would see the following methods in the Database class


//Database.SaveResult[] results = Database.insert(newAccounts,false);
//Database.SaveResult[] results = Database.insert(newAccounts,true);

Database.SaveResult[] results = Database.insertPartial(newAccounts);
Database.SaveResult[] results = Database.insertAtomic(newAccounts);

Rather than having the boolean parameter determine how failures are handle, we split the logic into two meaningfully different methods. I believe this is easier to read. You can read the words “insert partial” and you can mostly assume what the method does. When you read the insert() method with the allOrNone parameter, it isn’t immediately obvious what that parameter does (I always find myself looking at the documentation to make sure I remember its default value).

Part of the reason this isn’t immediately obvious with the allOrNone parameter is that the name of the parameter isn’t ideal. When we read the words out loud “all or none”, it doesn’t immediately makes us think about atomic vs. partial operations. I believe this parameter should have been called allowPartialSuccess, which would make the method signature much more intuitive and clear

insert(sObject[] recordsToInsert, Boolean allowPartialSuccess)

This brings us to the fourth rule of boolean parameters: If you are going to provide a boolean argument, make sure that the name clearly explains how it will enhance the main functionality of the method. All the guidelines we explored in Chapter 2 for good names apply to boolean parameters as well.

Going back to the topic of whether it is better to split the logic into different methods, the answer is:

You missed the best part 😔. Join the community of 70+ paid subscribers who are embracing a software engineering mindset and benefiting from this exclusive content. Don't be left behind—stand out from the crowd!

Subscribe for exclusive Salesforce Engineering tips, expert DevOps content, and previews from my book 'Clean Apex Code' – by the creator of HappySoup.io!
fullstackdev@pro.com
Subscribe