Skip to content
M03Technical choices and abstractionsLesson 2 of 4

Premature complexity

How to spot premature complexity, why AI makes it more tempting and which things can't be postponed at all.

Goal
Separate what can wait from what has to be right from day one, and cut complexity that today only costs you.

Linkr has 200 users. The architecture the AI assistant suggested, and that was eagerly accepted, has five microservices, Kubernetes, Kafka for click events, a service mesh, federated GraphQL and a plugin system “for when we have integrations”.

Every deploy takes 40 minutes. A simple bug crosses three services. Half the time goes into maintaining the infrastructure instead of the product. And the system doesn’t even have a tested backup.

Every component you add charges interest forever: it has to be understood, updated, monitored, debugged and explained to whoever joins the team. You pay for writing it once; you pay for maintaining it every month.

With AI, the cost of writing has dropped to almost zero. The cost of maintaining hasn’t dropped at all. Generating a plugin system takes five minutes; figuring out why it broke at 3 a.m. still takes hours. That’s why the bar for adding components has to get stricter.

Sign The question that takes it apart
“We’ll need it when we scale” Scale to how much? When? What happens if we do it later?
An abstraction with a single use What’s the second concrete case, today?
Configuration for everything Has anyone ever asked to change this?
Big-company infrastructure How many people run this? Who’s on call?
Optimization without measurement What measurement showed it was slow?
Multi-cloud or multi-region “for safety” Which real incident would this prevent, and what does it cost?

A useful heuristic: design for 10× your current volume, not 1000×. If you have 1,000 users, the system needs to handle 10,000 comfortably. By the time you get close to that, you’ll have data, revenue and context for the next decision, none of which you have now.

In his essay Choose Boring Technology, Dan McKinley suggested that every company has a few “innovation tokens” to spend. Each new, little-known technology uses up a token: you’ll discover its failure modes in production, on your own.

Spend tokens where your edge is. For everything else, use what’s known, documented and full of Stack Overflow answers: Postgres, Redis, a well-organized monolith, a simple deploy provider.

A well-used Postgres can handle far more than most products will ever need: job queues, text search, JSON, vectors for AI, pub/sub. Each one of those saves you a system to operate.

YAGNI (“you aren’t gonna need it”) is no excuse for skipping the fundamentals. Some things are one-way doors (3.1): fixing them later costs far more, or can’t be done at all.

Can wait Don’t postpone
Microservices Data model integrity (constraints, keys, correct types)
Kubernetes Tested backups (actually restore one, at least once)
Distributed cache Correct authentication and authorization
Multi-region Idempotency wherever money is involved
Plugin system Secrets out of the code
Federated GraphQL Logs and an “it’s down” alert
Performance optimization Versioned migrations (no changing the database by hand)
Sophisticated feature flags A way to roll back

The right column is cheap at the start and expensive later. The left column is expensive at the start and almost always unnecessary.

In practice: the “what if we do it later?” test

Section titled “In practice: the “what if we do it later?” test”

Before adding a component, answer in writing:

  1. What concrete problem, today, does this solve? If the answer starts with “when”, it’s speculation.
  2. What happens if we do it 6 months from now? If the answer is “the same work, with more information”, postpone it.
  3. Who will run this at 3 a.m.? If the answer is “nobody knows”, don’t add it.
  4. What’s the simplest path that covers the next 10×?

An example applied to Linkr:

Proposal: Kafka for click events.
1. Today's problem: record ~30 clicks/s without slowing down the redirect.
2. If postponed: a queue in Postgres or Redis handles current volume and 10×.
3. Operations: nobody on the team has run Kafka.
4. Simplest path to 10×: enqueue the click (pg-boss/BullMQ) and aggregate in batches.
Decision: postpone. Revisit above ~2,000 clicks/s sustained.
  • Confusing “simple” with “sloppy”. A monolith with well-separated modules, tests and migrations is simple. A 4,000-line file is just a mess.
  • Postponing without recording the trigger. “We’ll do it later” without saying when turns into “never” or “too late”.
  • Rewriting too early. If the simple system starts to strain, measure and optimize the bottleneck first (M26). You rarely need to replace everything.
  • What AI tends to generate: big-company reference architectures, abstraction layers “for the future” and dependencies for tasks the standard library already handles.

Go deeper

Materials in English unless noted.

  • Article
    Choose Boring TechnologyDan McKinley

    The talk about innovation tokens, as annotated slides.

  • Article
    YagniMartin Fowler

    The costs of building too early and what YAGNI doesn't give you permission to skip.

  • Article
    MonolithFirstMartin Fowler

    Why almost every successful system started as a monolith.

  • Article
    Just Use Postgres for EverythingStephan Schmidt

    A practical list of jobs Postgres can do before you need another tool.