On Functions

Karan Chawla / April 12, 2021

3 min read

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

The identation level of functions should not be greater than one or two. This, of course, makes functions easier to read and understand.

Do one thing — Functions should DO ONE THING. They should do it well. They should do it ONLY.

One way to know whether 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.

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

The stepdown rule — We want the code to read like a top-down narrative. We want every function to be followed by those at the next level of abstraction so that we can read the program, descending one level of abstraction at a time as we read down the list of functions. The general rule of switch statements is that they can be tolerated if they appear only once, are used to create polymorphic objects, and are hidden behind an inheritance relationship so that the rest of the system can't see them.

Use Descriptive Names

You know you are working with clean code when each routine turns out to be pretty much what you expected. It's not at all uncommon that hunting for a good name results in a favorable restructuring of the code.

Function Arguments

Ideal number of function arguments are none and more than three requires very special justification — and then shouldn't be used anyway.

Three very common reasons to pass a single argument to a method is because —

  • You may be asking a question about that argument such as FileExists(string file_name)
  • Or you may be operating on that argument, transforming it into something else and returning it, such as ReadFile(string file_name)
  • The event form — The overall program is meant to interpret the function call as an event and use the argument to alter the state of the system, such as, void PasswordAttemptFailedNTimes(int attempts) Use this form with care and make sure the names and contexts are carefully chosen such that the reader knows this is an event.

Flag arguments — Passing a bool into a function is a truly terrible practice. It immediately complicates the signature of the method, loudly proclaiming that this function does more than one thing. It does one thing if the flag is true and another thing if the flag is false.

Have no side effects — Side effects are lies. Your function promises to do one thing, but it also does other hidden things. They should never happen.

Anything that forces you to check the function signature is equivalent to a double-take. It's a cognitive break and should be avoided. For e.g. appendFooter(s) which makes one look at void appendFooter(s) to understand exactly what it might do.

Command Query Separation — Function should do something or answer something, but not both.

Don't repeat yourself — The DRY principle. Duplication may be the root of all evil in software. Your real goal is to tell the story of the system, and that the functions you write need to fit cleanly together into a clear and precise language to help you with that telling.

Subscribe to the newsletter

Get emails from me about future of technology, philosophy, and human behavior.

- subscribers