Nobody’s app is named “App”

Naming is important. As has been pointed out many times before, computers faithfully execute our written instructions, but we don’t write those instructions to be easily understood for the benefit of computers.

Decoupling is important. Highly coupled systems are challenging to change, and this challenge increases exponentially as the degree of coupling and the number of coupled modules increase.

Consider that not all forms of coupling are created equal. Consider also that decoupling is not without its own burdens, burdens that are compounded when the intermediate layer that forms the basis of decoupling hides contextual information or otherwise complicates grokkability beyond the utility it provides, or when it has no domain validity.

A Tempting Abstraction

As an example of a decoupling abstraction invalid according to its domain, consider the “Parent’s Day” example from a classic episode of Ruby Tapas. In the US, we have holidays for both Father’s Day and Mother’s Day. Their dates vary from year to year. While there are certainly arguments for — say for example — extracting a ParentsDay base class or interface with a calculate method that inheritors or implementers will provide, it is critical to note that Parent’s Day is not a meaningful domain concept for holidays. If we make the arguably small and understandable mistake of implementing it, there is a good chance we will be stuck with it, making the mistake more costly than necessary— above all if we are a developer lead and have the misfortune of working in a culture where the technical decisions of senior people are rarely challenged or revisited.

Here we have what I would propose to you is a classic case of premature abstraction; a programmer is unwilling to tolerate the specter of duplication long enough to let abstractions emerge naturally. If our domain is holidays and our mechanism of polymorphism is inheritance, there actually are one or more domain-meaningful base classes we could extract here. Not only is ParentsDay not one of them, it actively conceals better alternatives by virtue of its presence and implicit authority (if that idea resonates with you but you’ve never heard it clearly articulated, you may want to check out Sandi Metz’s “The Wrong Abstraction”.)

Default Export

Consider default export in JavaScript — well, consider first that I am a bit of a JS interloper at this point in my career, so take this with a grain of salt. Since returning to front-end development last year for the first time since my boot camp days, my perception has been that the community support around default export is very strong, and its use is enforced in some noteworthy standard lint configurations. Being so beloved, it shouldn’t be surprising that default export is not without its benefits, providing developers with a handy way to decouple imports from the abstractions they map to.

But what are some of the costs of adopting default export as your codebase standard?

Here are a few:

  • The auto-import experience for non-named exports is non-existent.
  • Default exports make it easy for renamed abstractions to become conceptually unhinged from their imports.
  • If you’re going to rename an abstraction, by not having to rename its imports, you lose the opportunity to better understand and improve application design by being confronted by the modules that have it as a dependency.
  • In most contexts, other mechanisms can provide the same assurances of imports remaining unbroken with far less baggage and far more ancillary benefits — e.g. typescript, linters, and good test coverage.

I’m tired so I’m going to take an app

It is very common to see the notion of “app” featuring prominently in a codebase, whether in a directory, config files, and/or environmental variables.

Let’s say, for example, that I have an app called MuscleGrinders written in Rails for tracking weight lifting sessions and results. I’ll likely have a slew of stuff with “app” or “application” in the file or variable name — to name just a few:

  • /app
  • application.rb
  • application_controller.rb
  • application_record.rb
  • application_job.rb

And so on.

This generic reference to “application” helps make a potential change to the MuscleGrinders’ name a non-invasive affair, especially if we have a constant to hold it. But it does so by destroying important contextual cues. It makes onboarding more difficult, increasing the time needed to robustly establish one of the more important conceptual anchors with which to associate the codebase. It makes context more difficult to acquire and hold for developers working on multiple projects. It makes life harder for those of us who are neurodivergent — whether through ADHD, ASD, GAD, or another condition that can complicate both the acquisition and holding of context. And — in my fallible opinion and for most contexts — it does so without sufficient payoff to offset these costs.

If you are concerned about creating a highly accessible codebase — and if you’re working in an ecosystem that will let you do this without straying worrisomely from the beaten path — you might strongly consider doing something like this:

  • /muscle_grinders_web
  • muscle_grinders.rb
  • muscle_grinders_controller.rb

…and so on.

If you balk at the coupling, consider what the true cost of that coupling is. Heuristic pattern recognition has a tendency to be dangerously undernuanced — “coupling therefore bad” is just as much a killer of meaningful exploration and discussion as its relative, “duplication therefore bad”.

Software Engineer & Coach @ Yakcellent Solutions