Functions

Functions should be small and do one thing.

Small

The first rule of functions is that they should be small. The second rule of functions is that they should be smaller than that.

Refer to Write Short Units of Code

Do One Thing

One way to know that a function is doing more than “one thing” is if you can extract another function from it with a name that is not merely a restatement of its implementation.

See examples in the "Small" section.

One Level of Abstraction per Function

In order to make sure our functions are doing “one thing,” we need to make sure that the statements within our function are all at the same level of abstraction.

Abstraction is a fundamental concept in OOPS. It talks about hiding the “how” part and only exposing “what” to the outer world.

According to SLAP (Single Level of Abstraction Principle), source code inside each method should refer to concepts and mechanisms relevant to just one level of “operational complexity” of your application.

Apply refactoring technique "Extract Method".

See examples in the "Small" section.

Reading Code from Top to Bottom: The Stepdown Rule

We want every function to be followed by those at the next level of abstraction so that we can read code from top to bottom.

See examples in the "Small" section.

Avoid nested structures

Avoid negative conditionals

Negatives are just a bit harder to understand than positives. So, when possible, conditionals should be expressed as positives.

Switch Statements

It’s hard to make a small switch statement. By their nature, switch statements always do N things. Unfortunately, we can’t always avoid switch statements, but we can make sure that each switch statement is buried in a low-level class and is never repeated. We do this, of course, with polymorphism.

There are several problems with the switch statement.

  • First, it’s large, and when new enum types are added, it will grow.

  • Second, it very clearly does more than one thing.

  • Third, it violates the Single Responsibility Principle (SRP) because there is more than one reason for it to change.

  • Fourth, it violates the Open Closed Principle (OCP) because it must change whenever new types are added.

Use Descriptive Names

Don’t be afraid to make a name long. A long descriptive name is better than a short enigmatic name. A long descriptive name is better than a long descriptive comment.

Be consistent in your names. Use the same phrases, nouns, and verbs in the function names you choose for your modules.

Prefer fewer arguments

The ideal number of arguments for a function is zero (niladic). Next comes one (monadic), followed closely by two (dyadic). Three arguments (triadic) should be avoided where possible. More than three (polyadic) requires very special justification—and then shouldn’t be used anyway.

Apply refactoring technique "Replace Method Parameters with Method Object": When a function seems to need more than two or three arguments, it is likely that some of those arguments ought to be wrapped into a class of their own.

Functions that take variable arguments can be monads, dyads, or even triads. But it would be a mistake to give them more arguments than that.

Arguments are even harder from a testing point of view. Imagine the difficulty of writing all the test cases to ensure that all the various combinations of arguments work properly.

Sometimes we want to pass a variable number of arguments into a function. Consider to use Argument Lists

Read more: Keep Unit Interfaces Small

Avoid flag arguments

Function with flag argument does more than one thing. It does one thing if the flag is true and another if the flag is false!

Split the method into several independent methods that can be called from the client without the flag.

Have No Side Effects

Side effects are lies. Your function promises to do one thing, but it also does other hidden things.

The side effect is the call to update(). The get user function, by its name, says that it gets the user by email. The name does not imply that it updates the last access of the user.

Output Arguments

Anything that gets modified or mutated but is not returned by the function. An array/object passed to a function, which changes the body of the array or changes the data of the object but does not return it, then it is the output argument.

Output arguments should be avoided. If your function must change the state of something, have it change the state of its owning object.

Separate command from query

Functions should either do something or answer something, but not both.

if (attributeExists("username")) {

setAttribute("username", "gpcoder");

}

Prefer exceptions to returning error codes

Returning error codes from command functions lead to deeply nested structures and maybe a violation of command query separation.

Another exmaple:

When you use exceptions rather than error codes, then new exceptions are derivatives of the exception class. They can be added without forcing any recompilation or redeployment.

It is better to extract the bodies of the try and catch blocks out into functions of their own.

Put all code inside a try block

If the keyword try exists in a function, it should be the very first word in the function and there should be nothing after the catch/finally blocks.

The DRY principle

Refer to DRY Principle

Structured Programming

Every block within a function should have one entry and one exit. Following these rules means that there should only be one return statement in a function, no break or continue statements in a loop, and never, ever, any goto statements.

If you keep your functions small, then the occasional multiple return, break, or continue the statement does no harm and can sometimes even be more expressive than the single-entry, single-exit rule. On the other hand, goto only makes sense in large functions, so it should be avoided.

Write, test, and refine

When I write functions, they come out long and complicated. But I also have unit tests that cover every one of those clumsy lines of code. So then I massage and refine that code, splitting out functions, changing names, eliminating duplication.

— Robert C. Martin

Last updated