Magic Numbers Represent Concepts

Literals represent something.

Magic numbers are a form of literal.

This insight goes at least as far back as the book Structured Design by Edward Yourdon and Larry L. Constantine (from 1979!), on page 94.

The number 79 can truly represent the result of 80-1, which in turn can express two other concepts:

  • When height = 80
  • and offset = 1,
  • the resulting number 79 represents a relation between the height and this offset.

In that case, 79 does not stand on its own, in isolation. The literal, at this moment in time, may have the same value as the expression height - offset, but it’s not expressed this way, and so cannot change for the correct reasons. (The literal, as a matter of fact, cannot change at all on its own.)

Working with “magic numbers”, that is numbers that are not named using variables/constants in the first place, poses a problem when constraints or relations change.

If height changes to 40, you cannot reliably “Search & Replace” every occurence of 79 in your project with 39 – some occurences of the integer literal 79 could have meant something different before. (To complicate things further: “thanks” to Swift’s type inference, the literal number 79 can be e.g. an Int or a Double or a Float, depending on where you put it.)

As a rule of thumb, favor expression of concepts.

Favor readability and intelligibility: where does the number come from?

As you write code and look for ways to express numerical relations with variables, you remove a ton of future-you problems. That’s why we should give literals meaningful names.

Reduce clutter with namespaces

To namespace constants like this, you can (ab)use Swift enums: declare an empty enum as a namespace and attach static let constants to it.

The following example is quite convoluted, but brings the point across when multiple things have a concept of “width”:

enum Layout {
    static let height = 80
    static let width = 100
    static let padding = 1
    static let margin = (top: 0, right: 10, bottom: 0, left: 10)
}

func putStuffOnScreen() {
    let previousFrame: CGRect = // ...
    // ... 
    let newFrame = CGRect(
        x: previousFrame.x + previousFrame.width + Layout.margin.left,
        y: previousFrame.y + Layout.margin.top, 
        width: Layout.width + 2 * Layout.padding,
        height: Layout.height + 2 * Layout.padding)
    // ...
}