Preemptive Pluralization is (Probably) Not Evil

See discussions on Twitter, Reddit, Lobste.rs, and my mixtape

Before you write any code — ask if you could ever possibly want multiple kinds of the thing you are coding. If yes, just do it. Now, not later.

A few common examples to illustrate:

  • You assume that one team has many users, and correspondingly, one user belongs to one team.
    • Eventually, you find that a user may need multiple teams.
    • This is actually fantastic for your business!
    • But you are depressed because you now have to spend 2 months refactoring every line of code and database schema that assumed the one-to-one mapping 😱 More examples here.
  • You assume that loading state only has two states — true / false — so you make a boolean isLoading variable
    • Then you realize you need to track error state, so you do, with isError. You do some work to make sure the 4 combinations of states behave intuitively. You write tests for each of them like the good developer you are. Of course.
    • Then you realize you need an isCanceled state. 8 and counting…
    • Eventually you realize every addition or modification takes exponentially longer to account for each edge case. It’s hard to even tell if you’ve covered them all. Most don’t.
    • The solution is explicit state machines - but at this point you’re too far in to justify a rewrite.
  • Internationalization. If you winced at that, you know the pain.
  • Pagination. To quote Simon Willison, co-creator of Django, “Refactoring an existing non-paginated API to support pagination will break everything. Better to fake pagination but only ever return a single page, just in case”.

You can listen to Ben Orenstein of Tuple discuss this on my mixtape:

I’ve done this refactoring a million times. I’ll be like, I thought there would only ever be one subscription team, user plan, name, address , and it always ends up being like, “Oh, actually there’s more.” I almost never go the other way. What if you just paid the upfront cost of thinking “This is just always a collection”?

Donald Knuth is famous for noting that Premature Optimization is the Root of All Evil (there’s some nuance to that, btw). I am very sympathetic to the appeal to simplicity — if “You Ain’t Gonna Need It”, then don’t use it. But I think Preemptive Pluralization — projecting forward into hypothetical situations when you need N types of a thing — is exempt, even though you are literally optimizing for a future you don’t currently live in.

It is a LOT easier to scale code from a cardinality of 2 to 3 than it is to refactor from a cardinality of 1 to 2. This is a fundamentally under-appreciated nonlinearity. In other words, Preemptive Pluralization can make the difference between “sure, I’ll add that today” and “this is going to take us 2 months and we’ll introduce merge conflicts with every other in-progress feature.”

Write Robust Code

Requirements volatility is a core problem of software engineering. As a software engineer, writing code that does what you ask of it today is the bare minimum. Your real skill comes in what happens next — what you do when requirements inevitably change, whether by new feature request or scaling issues arising from I/O or compute bounds.

It may not be enough to write code for what you foresee in the near term — those are just more requirements. Software design and architecture is all about making it easy to respond to unforeseen changes.

Hillel Wayne has proposed calling these requirement perturbations. If a small, typical feature request can throw your whole design out of whack, then you have fragile code. Clearly you want the opposite of fragile — I am tempted to call it “Antifragile” because that gets clicks — but really the best you can hope for is code that mostly doesn’t fall apart due to 1-2 standard deviation changes in requirements. In other words: robust code. Robust code is optimized for change (more in a future blogpost).

The nonlinearity in how expensive it is to make a change comes from the ”emergent sclerosis” of code. Code that is robust to future changes is far cheaper to write today, than when written later, as delayed technical debt that you must pay up before you can proceed to a feature request. Fragile code is like the payday loan lender of technical debt.

Preemptive Pluralization creates Robust Code.

Is it Even Premature?

I’m so committed to not prematurely optimizing that I want to make a final pitch for why Preemptive Pluralization is not premature.

Let’s address obvious criticisms of Preemptive Pluralization:

  • Increased code complexity: Functional languages and other abstractions can help make array or matrix operations almost as easy to work with as regular operations.
  • Slow performance from doing extra loops: Loops only cost significantly when you have lots of N. By definition, if you are pluralizing prematurely, N = 1.
  • Perf bottlenecks from excessive joins
  • Misleading code: your code communicates current state of things. “Premature” plurality can lead to communicating wrong thing to new observers of the system.

Ultimately I think what makes something premature or not is your definition of what you need to write:

  • If you view “code that works today” as your job, preemptive pluralization is premature.
  • If you view “code that doesn’t blow up in my face a year from now” as your job, then it is not.

Just make Robust Code a design requirement from the start and then figure out what that means :)

More Pluralization Points

More awkward things to pluralize:

  • Single tenant open source -> Multi tenant hosted service
  • Versions -> from no version to v1/v2, or going from “legacy”/“new” to “new new” (hence Stripe just uses dates)
  • Number of independently shipping frontends in your company (hence module federation)
  • Number of clouds in your company (you think you will avoid this… until you can’t, per the Hashimoto lemma)

More from @nivertech on Twitter:

  • in-memory→persistent
  • single node→cluster
  • single language→i18n
  • no pagination→pagination
  • single user→multi-user
  • single tenant→multi-tenant
  • single branding→white-labeled
  • free→paid
  • hard-coded configs→separate config from code
  • hard-coded features→feature flags
  • online-first→offline-first
  • web-first→mobile-first
  • no 3rd party API→API-first
  • DB-specific→ORM
  • local desktop SW→Client/Server
  • Client/Server→P2P
  • Centralized→Decentralized
  • Static→Dynamic
  • SSG→SPA

Acknowledgements and Further Reading

Tagged in: #reflections #programming #advice

Leave a reaction if you liked this post! 🧡
Loading comments...
Webmentions
❤️ 0 💬 46
  • www.reddit.com  mentioned this on 2022-08-29
  • tgcthegnarcompanyinc.kinsta.cloud  mentioned this on 2021-09-24
  • avatar of Magne
    Magne mentioned this on 2021-09-07

    "Before you write any #code — ask if you could ever possibly want multiple kinds of the thing you are coding. If yes, just do it. Now, not later." "It is a LOT easier to scale code from a cardinality of 2 to 3 than it is to

  • avatar of Robert James Kaes
    Robert James Kaes mentioned this on 2021-04-25

    Preemptive Pluralization is (Probably) Not Evil ∊ swyx.io swyx.io/preemptive-plu… via @Instapaper

  • avatar of Justin
    Justin mentioned this on 2021-03-16

    I really like @swyx's Preemptive Pluralization. Weirdly, I've found myself applying this pattern not to software development but to how I template my writing, specifically in preemptively defining (plural) aliases to pages in

  • avatar of Stefano Magni
    Stefano Magni mentioned this on 2021-03-16

    Go upward reading the whole thread, it's worth. Started by "Preemptive Pluralization" (preventing a ton of refactoring when, for example, "an employee can work for more teams instead of a single team as thought six months ago") article by @swyx

  • avatar of Philip Schlesinger
    Philip Schlesinger mentioned this on 2021-03-16

    Preemptive Pluralization is (Probably) Not Evil swyx.io/preemptive-plu…

  • avatar of Robert Balicki
    Robert Balicki mentioned this on 2021-03-16

    This is wise. Support many-to-many relationships in advance. But it's not always so obvious! Sometimes a 1:1 mapping can be implicit and easy to miss, e.g. an accounts table having payment info columns. swyx.io/preemptive-plu…

  • avatar of Cory House
    Cory House mentioned this on 2021-03-15

    Preemptive Pluralization: Before writing code, ask if 2 or more may occur at some point. Consider supporting multiple even if not necessary yet. Why? It’s not much extra work to support multiple, and a lot of work to add later. Thoughtful post by @swyx

  • avatar of Kyle Welch
    Kyle Welch mentioned this on 2021-03-15

    What a great article from @swyx! "It is a LOT easier to scale code from a cardinality of 2 to 3 than it is to refactor from a cardinality of 1 to 2." There are so many projects that I had have fail because we needed to scale from 1 to 2.

  • avatar of Jacky
    Jacky mentioned this on 2021-03-15

    Preemptive Pluralization is (Probably) Not Evil swyx.io/preemptive-plu…

  • avatar of Pinboard Popular
    Pinboard Popular mentioned this on 2021-03-15

    Preemptive Pluralization is (Probably) Not Evil swyx.io/preemptive-plu…

  • avatar of Dj T@l
    Dj T@l mentioned this on 2021-03-15

    Preemptive Pluralization is (Probably) Not Evil ∊ swyx.io swyx.io/preemptive-plu… via @instapaper

  • avatar of Daniel Díaz
    Daniel Díaz mentioned this on 2021-03-14
  • www.progclub.org  mentioned this on 2021-03-14
  • avatar of Adam Pallozzi
    Adam Pallozzi mentioned this on 2021-03-14

    We were using an external data provider in an app. We just had to refactor to add a second data provider. I highly recommend this approach! 6 days refactoring to add data source #2. 10 minutes to add data source #3

  • avatar of Chris Borbidge
    Chris Borbidge mentioned this on 2021-03-14

    Shopify started with one location for inventory. When they introduced multi-location, it was a huge deal and required a lot of change management.

  • avatar of Gustavo Bicalho
    Gustavo Bicalho mentioned this on 2021-03-14

    So "optionally one of foo" is actually "two of foo'" in this view? Which is actually the case for the algebra of types, and implies something like "avoid Maybe, you usually want a list instead"

Subscribe to the newsletter

Join >10,000 subscribers getting occasional updates on new posts and projects!

I also write an AI newsletter and a DevRel/DevTools newsletter.

Latest Posts

Search and see all content