A lot has been told about the guard statement since its appearance in Swift. Indeed, it simplifies code and makes it more readable. But is guard really a silver bullet?
A lot has been told about the function size. It’s obvious to everyone that functions should be small. They should be as small as possible. Nobody likes to read, understand, and refactor big functions. But how small they should be exactly?
The first rule of functions is that they should be small. The second rule of functions is that they should be smaller than that. — Robert C. Martin, Clean Code book
More specifically, Robert C. Martin recommends keeping functions not more than six lines long and definitely not more than ten lines.
Following this simple rule makes magic. The code suddenly becomes more understandable. What previously was a thirty-line function with several indentation levels and intermediate variables that had to be kept in the head is now ten functions, each having a self-documenting specific name.
A lot has been told about the single responsibility. It applies not only to objects but to functions as well. It’s not a secret to anybody that functions should do one thing and one thing only.
Yet this is violated over and over again. The function size seems to play the major role here. So if a thirty-line function is refactored to be ten three-line functions, the following of the single responsibility principle on the function level is automatically improved.
One Level of Abstraction
Not that much has been told about functions having one level of abstraction. It is another instrument that helps creating functions with single responsibilities.
What does it mean? In short, high-level code, like controlling the whole process, shouldn’t be mixed with small details, like variable increments or Boolean checks.
This example is taken from The Swift Programming Language book.
Of course, the vend(itemNamed:) function is just an example of using the guard statement but one can often see similar functions in the production code. This function has all three problems described above.
- It is pretty long, sixteen lines, multiple parts separated by empty lines.
- It does several things. It gets an item by name, validates parameters, and it implements the logic of vending an item.
- It has several abstraction levels. The high-level vending process is hidden among the finer-level details like Boolean checks, usage of specific constants, math operations, etc.
How could the function look like after refactoring?
The total number of lines increased, but it should not be forgotten that the number of lines is not an end in itself. The refactored code provides a number of benefits compared to the old one.
- The main vending function is now short and contains only high-level steps of vending an item. If the reader is not interested in the details, she can understand the vending process by quickly looking at this high-level function.
- The functions have more respect for the single responsibility principle. Some of them could be broken down even further, but even in their current form, each one is more understandable and readable. They separate the old bigger chunk of code into smaller, self-documenting pieces.
- Each function operates on a single level of abstraction. The reader can easily move between these levels as needed. How does the vending process look like? The item is determined by name and validated, the deposit is reduced, the item is removed from the inventory, and dispensed. How the item is validated? The count and the price are checked. How exactly the count is checked? The count is compared to zero. If small details don’t interest the reader, they can always be skipped.
The guard statement is a handy instrument for reducing the number of nested structures in functions. The problem lies not in the instrument itself but in its usage. The guard statement promotes the creation of bigger functions that do several things and act on multiple levels of abstractions. By writing small and focused functions, one can find herself not needing the guard statement at all.