Logic Fault

Have you considered running your software on a computer?


On YAGNI

Let’s talk about modularity/swappability, sometimes called “abstraction” (v.). This is the practice of writing the ability to “change one part without touching other parts” into your codebase. This is sometimes equated this with the “L” or “D” in SOLID, but the practice extends farther than that.

Examples include:

Let’s make all of our data access SQL-engine agnostic so we can switch from MySQL to Postgres if we want

…and:

Let’s introduce two abstract interfaces, a DTO, and two repository classes so we can transfer a single string from one table on one database to another, just in case we someday develop a need for a GeneralPurposeDatabaseOneToDatabaseTwoStringTransferenceEngine.

Most modularity is literally useless, as in: it is never used because the planned “swap” or “add a case” never becomes necessary.

The vast majority of projects that do hope to take advantage of swappability either start and then fail or are never finished. This is because modularizing according to design patterns is not very good at seeing the future, and in practice things turn out to have been modularized in the wrong way, or depend on behavior that wasn’t abstracted out.

This goes beyond “SOLID principles only provide a little benefit to future swap-outs”; dogmatic adherence to a design pattern tends to add so much structure to a codebase that actually performing the swap-out ends up being harder with the design pattern than without.

Abstractions are good, generally, when their goal is to hide or reduce complexity. Abstractions are less good, generally, when their goal is to facilitate a future change whose specifics are not yet known.

Abstraction with the goal of swappability should only be done:

  1. Retroactively, when swappability requirements are added.
  2. When the need for swappability is known to a high degree of certainty up front. Example: “I’m building out new functionality for a dog grooming salon so we can get it in front of users soon, but I know that the overarching product feature requires it to work for cat grooming as well before we go GA in six months”.

Put another way: there are two points on a timeline: the point at which modularity is actually needed in production (i.e. going from “billing system for dog grooming requests, which are backed by database table X” to “dog grooming requests as well as cat grooming requests backed by database table Y (1)To people saying “this is a very easy change to make if you had just designed your schema correctly around generic PetGroomingRequests or whatever up front”: you are so close to getting the point. Consider that hindsight is 20/20, mutative database migrations are widely considered to be scary things that should be avoided, and that most code is–in one way or another–a prototype that got promoted to production. If that doesn’t get you there, substitute “cat grooming” with “cat boarding/adoption services (because the company pivoted into that market)” and think about it some more. ”, and the point at which support for swapping or interacting with different concrete things is added to the codebase.

The closer together in time (in either direction) those two points are, the more likely the swappability support is to be correct. If they’re too far apart, the risk is any/all of: