Engineering

Simple software is harder than it looks

March 18, 2025
6 min read

Simple software is harder than it looks

The hardest part of building software isn't writing code. It's deciding what not to write. Every abstraction, every service boundary, every config flag is a bet on the future — and most of those bets turn out to be wrong.

When we start a project at Bluestone, we work backwards from the smallest thing that could solve the problem. Not because we're lazy. Because simple systems stay cheap to run, easy to hand over, and boring to debug at 2am.

Over-engineering is usually fear in a lab coat

Most over-engineered code is built by people trying to prove something. It looks like this:

  • **Premature microservices.** A three-endpoint CRUD app split across five repos, a message bus, and a service mesh — before the product has ten users.
  • **Abstractions with one caller.** Interfaces, factories, and strategy patterns wrapped around a single concrete implementation that nobody will ever swap out.
  • **Config hell.** Thirty environment variables, four YAML files, and a "plugin system" to control behaviour that could have been a function argument.
  • **Framework tourism.** A new library for every problem, each with its own conventions, upgrade path, and security surface.

The tell is always the same. The code is optimised for a future that hasn't happened yet, and it makes the present measurably worse — slower to change, harder to onboard, more expensive to host.

What simple actually buys you

Simple software compounds. Each decision you don't make now is a decision you get to make later, with more information. A few things we've watched happen, over and over:

  • **Handover gets real.** When the stack is Postgres, a single Next.js app, and one worker, a new developer can ship in their first week. When it's seven services glued together with tribal knowledge, onboarding takes months — if it ever finishes.
  • **Bugs shrink.** Fewer moving parts means fewer places for state to disagree. Most production incidents we see on other teams' code are integration failures, not algorithm failures.
  • **Bills go down.** A boring monolith on a single box will outperform most "cloud-native" architectures for the first few years of a product's life, at a fraction of the cost.
  • **You can actually delete code.** Simple systems are easy to remove things from. Complicated ones tend to grow forever, because nobody can safely prove a piece is unused.

How we decide what to build

We don't have a rulebook, but we have a short set of questions we ask before writing a line of code. They're not clever — they just prevent us from outsmarting ourselves.

1. What breaks if we don't build this?

If the honest answer is "nothing, for at least six months," we don't build it. That includes caches, queues, admin dashboards, and feature flags. Most of them can wait until there's a real reason.

2. Can one person hold it in their head?

If a single engineer can't explain the whole system on a whiteboard in ten minutes, something is wrong. Either the problem genuinely is that complex (rare), or the design is doing too much (common).

3. What's the dumbest version that would work?

A CSV in a bucket. A cron job. A form that emails someone. These sound like jokes until you realise they solve the problem for a year and cost nothing. Start there. Upgrade only when the dumb version visibly breaks.

4. Who owns this in two years?

We build most things knowing we'll hand them off. If the answer to "who maintains this" is "only the person who wrote it," we rewrite it until that's no longer true. Clever code is a liability on a small team.

The engineering tax nobody talks about

Every feature has a maintenance cost that scales with how much machinery sits behind it. A "simple" feature built on a complicated stack isn't simple — it's just a tidy interface on top of ongoing work.

When we quote a project, we think about that tax explicitly. What does it cost to keep this running, patched, and understandable a year from now, when we're not in the room? If that number is high, the architecture is wrong, even if it ships on time.

Simple software isn't about doing less. It's about doing exactly what the problem requires, and nothing else. That sounds obvious. In practice, almost nobody does it.

---

If you're staring at a stack that feels heavier than the problem it solves, we can help you cut it back. Contact us and we'll take a look.

Got a project in mind?

Start a conversation with Bluestone. We build simple, robust software and hand you full ownership of the code, the infrastructure, and the IP.

Start a conversation