Functions

Functions

  • They should be small. They should be small and readable, with few nests. In the ideal world, they should be 1 or 2 levels of indentation.

  • Do only one thing. They should make only one thing and that's it.

  • One abstraction level per function.

    When we talk about abstraction levels, we can classify the code in 3 levels:

    1. high: getAdress

    2. medium: inactiveUsers = Users.findInactives

    3. low: .split(" ")

    The ideal is not to mix the abstraction levels in only one function. When we mix, the function becomes a little harder to understand, and we can't read the code step by step, from top to bottom, like an article. The best scenario is that you can read code from top to bottom, like a narrative. ex:

    1. To include the setup and the teardown, we first include the setups, then we include the page content, and after that, the teardown.

    2. To include the setup, we add the setup suite and then the default setup.

    3. To include the test setup, we search for the hierarchy...

  • Avoid switch statements and excessive if/else. When there are several if/else/switch in a single module, we have to modify the code if we want to add any extra case, what makes the code more fragile and difficult to test. Sometimes we need to verify differents situations and take different actions depending on the situation. One way to decrease the switch/if/else is to use polymorphism.

    foreach (var animal in zoo) {
      switch (typeof(animal)) {
        case "dog":
          echo animal.bark();
          break;
    
        case "cat":
          echo animal.meow();
          break;
      }
    }

    this could be refactored in this way:

    foreach (var animal in zoo) {
      echo animal.speak();
    }
  • Less arguments is the way to go. Occasionally we can decrease the argument size creating a class.

    A function with several arguments could be transformed in a class, and this function could be a method in this class. The arguments, much of the time, could be their properties and, this way, we don't need to pass any argument. In some cases, we don't need this much, so we could use other ways to decrease the number of arguments, like grouping then.

    Circle makeCircle(double x, double y, double radius) could be transformed in Circle makeCircle(Point center, double radius)

  • Function are verbs, arguments nouns. When we name function as verbs, and arguments as nouns, it becomes more legible and easier to understand the behavior that we expect, like write(name)

  • Avoid flag as arguments. Avoid passing a boolean as an argument, because it makes the refactor hard. Try to verify the truth or falseness when you call the function, and not inside it.

  • A function shouldn't have collateral effects. If we have a function called checkPassword, it shouldn't do a login inside its body.

  • Output arguments

    Output functions should be avoided. If a function needs to change the state of something, it should change the object. Sometimes we bump into functions like appendFooter(s) and we are not sure if the argument is an input (s is the footer and we are attaching it somewhere?) or output (we will attach s to the footer?). If the first case is the correct one, if s is the footer, the best thing to do is objectInstance.appendFooter

  • A function should change the state of an object, or return some info about it. This is an example of a function that doesn't follow the command Query Separation:

    if name?
      name.change
      true
    else
      false
    end

    A function that changes something and returns true or false should be replaced by two.

  • DRY - Don't repeat yourself. The problem with repeated code is, besides that it inflates the code, any change that we need to make we have to change in several places.

    However, we must be aware that too much DRY can be harmful. We often confuse code duplication with business rule duplication. Sometimes it's okay to duplicate a code as long as you're not duplicating a business rule that should be unique.

    There is another maxim also that says: you must write the same code a maximum of 3 times. The third time you should consider refactoring and reducing duplication.

  • Avoid returning an error code

    When methods return error messages, it violates subtly the Command Query Separation. When we put an error inside an if block, it may cause some confusion, since it may be returning something or an error.

    if name?
      name.change
    else
      raise 'An error has occured'
    end

    In the example above, if it works, it performs an action. If it went wrong, it pops a mistake

Last updated

Was this helpful?