Code Smell: Temporal Coupling

Have you ever had a bug that was caused by calling commands in the wrong order? Take the following for example:

struct Points {
  var value = 0
}

var points = Points()
gameCard.charge(points)
points.value = 2

In this example, you can imagine I’m at an arcade with a game card filled up with points that I can use to play games. When I insert the card into different games, the code above is run to charge my card before starting the game. There is one problem though. I get to play all the games for free! The reason is simple. The number of points I should be charged is set after the transaction occurs. When the order in which commands are run changes the behavior of a system, this is known as temporal coupling.

The solution is pretty simple. Design the API in a way that isn’t dependent on order by making dependencies explicit. Consider the following:

struct Points {
  private let value: Int // Constant.

  init(_ value: Int) {
    self.value = value
  }
}

var points = Points(2)
gameCard.charge(points)

Now, there is no way these commands can be executed in the wrong order. Because the number of points can only be set at the point of instantiation, it can never be modified after the transaction.

An API should always be designed in a way that makes it hard for developers to break. Making dependencies explicit, as shown above, is just one way to do this, and it also helps with avoiding temporal coupling. Adopting this technique in your code base will improve its structural integrity and make your life a little easier at a low cost.

Leave a comment