Apex methods should do one thing, but what is one thing?

This is a preview of Chapter 3 of my upcoming book: Clean Apex Code for Salesforce. You can read a preview of chapter 1, here.


Methods are at the heart of Apex programming. Aside from anonymous Apex, all Apex code runs inside methods. We use methods to organize logic and to create an entry point to our code. Given their importance, it makes sense to dedicate an entire chapter to learning how to keep them clean.

Common wisdom says two things make methods clean: they do one thing, and they are short. But these concepts are just the end result of many design principles. Studying them without exploring those principles won't teach you how to properly think about methods.

In this chapter, we will dive into the principles behind these guidelines, so we can understand how to keep our methods clean and effective.

Why should methods do one thing

Before we define what “one thing” is, let’s first explore why methods should do one thing. Consider the following example

public static User createUser(String firstName, String lastName, String username) {

    List<User> users = [SELECT Id FROM User WHERE Username = :username 
										    AND Business_Area__c == 'CRM Team'];
										    
    if (!users.isEmpty()) {
        return users[0];
    }
    else {
        User newUser = new User();
        newUser.Username = username;
        newUser.FirstName = firstName;
        newUser.LastName = LastName;
        newUser.Email = username;
        newUser.Alias = username;

        insert newUser;

        String chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz';
        String password = '';
        while (pass.length() < 8) {
            Integer idx = Math.mod(Math.abs(Crypto.getRandomInteger()), chars.length());
            password += chars.substring(idx, idx+1);
        }
    
        System.setPassword(newUser.Id,password);

        return newUser;
    }
}

How many things do you think this method does? Let’s count them:

  1. It queries users from the database
  2. If an existing user exists, it returns it
  3. If it doesn’t exist, it creates a new one
  4. It generates a password from a random string
  5. It resets the user password
  6. Returns a new user

Clearly, this method does more than one thing. And that in and on itself isn’t bad, the problem arises from the consequences of doing more than one thing. Let’s explore these consequences.

Mixing different levels of abstraction

The first problem with methods that do more than one thing is that they can often mix different levels of abstraction. Abstraction is a significant topic that we will cover in detail in future chapters. For now, we can define abstraction as a mental model we use to understand concepts.

For example, when you drive a car, you are operating it at a higher level of abstraction, which includes the steering wheel, pedals, and other controls. You are not concerned about the internals of the car and how moving the steering wheel actually makes the wheels turn. These details are hidden from you because exposing them would make driving a car much more complicated.

In contrast, these details are crucial for a car mechanic who is inspecting a fault in the car. The mechanic operates at a lower level of abstraction. The higher the level of abstraction, the fewer details we are exposed to; the lower the level of abstraction, the more details and complexity will be visible.

Another example of abstraction is when you see a map in a zoo or amusement park. That map isn’t an accurate representation of the actual distances between attractions; it’s a simple mental model to help you find where you are and figure out the path to the next attraction. Such a map would be useless to an architect tasked with expanding the park. Likewise, if you were provided with an architectural map to find your way in the park, you’d probably have a hard time reading it because it exposes too many details that are unnecessary to you. The amusement park map is at a higher level of abstraction compared to the architectural map.

What does this have to do with methods? Let's look at our previous example. In the same method where we create a user using simple DML logic, we also have complex logic to generate a random string using the Crypto and Math classes. While generating a password might be part of creating a user, the logic for generating a random string is at a much lower level of abstraction than the simple DML logic.

When we mix different levels of abstraction, we make it harder for other developers to understand the code. To fully understand how a user is created, we force them to understand the complex logic for generating random strings. It's like driving a car while simultaneously being responsible for making sure the oil circulates through the engine.

Mixing different levels of abstraction also causes unnecessary coupling between our logic. Every time we need to change how a password is generate, we have to change the createUser() method. And an arbitrary change inside createUser() could break our password generating logic.

In this case, the right solution would be to extract this logic to its own class so that it is completely independent. For example

public class Password {

    public static String generatePassword() {

        String chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz';
        String password = '';
        
        while (pass.length() < 8) {
            Integer idx = Math.mod(Math.abs(Crypto.getRandomInteger()), chars.length());
            password += chars.substring(idx, idx+1);
        }
        return password;
    }
}
💡
The Site class has some methods for reseting password. This use case is simply an illustration and not an encouragement to re-create existing logic from the Apex library.

Then in our method, we can reuse this logic like this

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