Choose boring things

January 20, 2025 (1 year ago)

When I first encountered Ruby on Rails, probably around 2008, it seemed anything but boring. The conventions it imposed strongly suggested rubbed me the wrong way.

But now it's pretty boring

Defining boring

Updating an object in the database is pretty standard for an application with state. If the code to do that requires reasoning about authentication, validation, and transactional effects that's going to feel monotonous but not boring.

This is why frameworks exist, and it's important to be able to trust the framework.

Using Rails as an example, the typical method looks like this:

def update
  if @object.update(sanitized_params)
    redirect_to(@object)
  else
    render(:edit, status: :unprocessable_entity)
  end
end

def sanitized_params
  params.require(:object_name).permit(:attribute1, :attribute2)
end

That is boring code, and even if you've never seen Rails I suspect most developers can follow along with what is happening.

Changes need tests, and the tests will have some boundary to be sensible. I'm generally opposed to including testing the framework in individual unit tests, but the test harness should cover:

  1. Authorization
  2. Validation
  3. Safety (e.g., transactional writes)
  4. Performance (are there dumb queries that will kill your database?)

A test for that method (without testing the framework) will lightly touch on those, the test harness will provide a method for an authenticated session, and the engineer can throw both valid and invalid data at the method. That will take care of 1 and 2.

With boring technology, I should never have to think about 3 and 4. That's aspirational, and we still do time and again, but another property of boring technology is how easy it is to simple go read and understand the source code.

A step down the complexity stack

With boring technology, an engineer shouldn't have to reason about authorization and it should be automatic. This is somewhat idiomatic, which by definition is going to be new (and possibly complex) but quickly becomes boring after sufficient familiarity.

One common pattern is leveraging inheritance, delegating authorization to a parent class. This all happens before the update method, and that code that handles actions tends to look very sparse.

class ObjectController < ParentControllerThatDoesAuthorizationController
  def update
    ...
  end
end

The only hint that there is authorization is the name of the parent controller. If you name it obvious enough, no problem.

It can be even simpler when an application has authorized routes by default, so everything must be authenticated unless stated otherwise.

That looks like this:

class ObjectController < ApplicationController
  skip_before_action :authorize!, except: [:frozzle]

  def update
    # requires auth
  end

  def frozzle
    # open to all
  end
end

Engineers working in the code base have to go out of their way to remove authorization, but it requires familiarity with skip_before_action.

But for engineers that haven't used Rails, skip_before_action doesn't mean anything. The :authorize! seems clear enough and someone can go "Ok, I guess this is skipping authorization" but then will wonder "what is a before_action?"

Having to look stuff up and ask questions isn't boring! It will be, if the answer is quickly found and understood.

Boring is a spectrum, and subjective

When something is boring, what I care about is the time between being introduced to new technology and when it fades into the mental background.

Mihaly Csikszentmihalyi introduced the concept of the "flow channel", showing that as skills develop, what creates anxiety (or it can be interpreted as "excitement" in a positive situation, but will be fatiguing) will eventually, and with practice, become boring. Applying this model to technology, it looks like this:

The blue line is the technology I want to choose. It mostly gets more boring over time. That boredom equates to saved energy.

The red line is hard to stay ahead of. It may feel like it solves some pain in the moment, but the thrash and changes, and complexity cliffs will inevitably take more time and energy.

Sometimes the results will suffer. Boring technology is sometimes a bit harder to nudge into specific workflows, like legacy applications expecting one HTTP request to serve a specific purpose before SPAs became common. But they catch up, because these frameworks tend to be quite durable.