Skip to main content

Beware of Unnecessary Indirection

Programmers love hating magic values — use named constants instead, right? Same for small, self-contained units of work — use named functions instead, right? These, and other guidelines should only be followed when understood from first principles.

Magic values are bad for two reasons. First, updating the same value requires edits on all use sites, and one needs to discern whether the same actual value actually represents the same conceptual value — is this 42 the same as this other 42, or is one milliseconds and the other pixels? Second, the value by itself fails to communicate intent. Naming allows to address both of these concerns.

Breaking self-contained units of work into named functions brings out the self-contained-ness, makes code easy to digest, improves testability, communicates intent.

There are times when applying what seems like the above pattern indiscriminately yields negative results. Consider the following example.

object ScheduledTasksService {
    def dao = ScheduledTasksDAO

    def get(name: String): Future[ScheduledTask] = dao.get(name)
    def set(name: String, value: DateTime): Future[Int] = dao.set(name, value)
}

At first glance, there’s nothing wrong with this code. The DAO for this service is the ScheduledTasksDAO, easy enough to understand. But consider what happens to the reader (of the potentially long functions within this file). Every time they come across dao.someFunction, their brain needs to answer the question “what was dao again?” — unnecessary mental overhead. It’s highly unlikely that you’ll be swapping in a different DAO in place of ScheduledTasksDAO. So this naming, this indirection, is pointless — your brain has to make the extra hop, yet you gain nothing from the efforts.

object ScheduledTasksService {
    def get(name: String): Future[ScheduledTask] = ScheduledTasksDAO.get(name)
    def set(name: String, value: DateTime): Future[Int] = ScheduledTasksDAO.set(name, value)
}

Same goes for naming simple values only to use them once — beneficial if you’re naming a complex expression, but not so if the expression that you’re naming is already self-documenting.

As always, do things for the root reasons, like “readability in the current context”, and not for generic rules that are multiple hops away from the root reasons.

Next: Coding Things ⇒

Comments