Make the shape of your functions follow the shape of your data

It’s easier to understand code that’s structured the same way as the data it operates on.

It takes some effort to understand a data structure, and it takes some effort again to understand the code that acts on it. So if the shape of the code follows the shape of the data, you reduce the effort needed to understand the system, because you can predict, from the data structure, how it’s going to look. You only have to learn one structure.

In practice, if you’ve got a data structure consisting of several nested types, it means you write a function to handle each distinct type in your data, and you compose them. Say you need to process a data structure that looks like this:

type Foo struct {
  Bars []Bar
}

type Bar struct { }

You already know what it’s going to look like! You’re expecting this:

func processFoo(foo *Foo) {
  // Do some work
  // to process Foo
  for _,bar := range foo.Bars {
    processBar(bar)
  }
}

func processBar(bar *Bar) {
  // Do a bunch
  // of stuff
  // to process Bar
}

…and not this:

func processFoo(foo *Foo) {
  // Do some work
  // to process Foo
  for _,bar := range foo.Bars {
    // Do a bunch
    // of stuff
    // to process Bar
  }
}

Process one thing at a time

This works best when your functions process one item at a time, rather than a collection. That is, you should prefer this:

func processFoo(foo *Foo) {
  for _,bar := range foo.Bars {
    processbar(bar)
  }
}

func processBars(bar *Bar) {
  // ...
}

…instead of this:

func processFoo(foo *Foo) {
  processBars(foo.Bars)
}

func processBars(bars []*Bar) {
  for _,bar := range bars {
    // ...
  }
}

This keeps your code close to the essence of the problem, and keeps the signature of your functions predictable: one type -> one function. If you have a type, you have a function to process an item of that type. A collection of items is an accidental detail, not something that needs to be reflected in that item’s function.

And, if nothing else, it’s more flexible to code this way. You can always map a single-item function over a collection, but it’s more awkward to use a multiple-item function on a single item.