Contents

Design Debt: Silent Killer in Systems

This article is based on my book — Designing Trading Systems: Trade Booking, Lifecycle, and Flow - a hands-on guide for folks in financial engineering industry.

TL;DR

Design debt is the structural weakness that accumulates when systems are built with unclear boundaries, tangled logic, and no room for future extension. Unlike technical debt, which is visible and easy to clean up, design debt hides deep in the architecture and resurfaces only when the system hits scale, stress, or new business demands. The cost is non-linear: everything works until it suddenly doesn’t. Minimizing design debt early—through clear domain boundaries, flow-first thinking, extension points, and built-in observability—preserves long-term velocity and prevents the system from collapsing under its own weight.

Context

Every engineering organization accumulates some amount of design debt. The danger is when you don’t notice it until the system starts to resist you.

In this post, I want to explore design debt through real-world analogies and practical engineering examples. The goal is simple: understand why design debt forms, how it sabotages systems over time, and how to minimize it early.

What Exactly Is Design Debt?

We often talk about technical debt — shortcuts in implementation:

  • Lack of tests
  • Hard-coded values
  • No refactoring
  • Duplicated logic

Design debt is deeper. It’s structural.

Technical debt is messy furniture. Design debt is load-bearing walls in the wrong place.

Technical debt is annoying. Design debt is painful.

Design debt is when:

  • Boundaries are unclear
  • Modules don’t have extension points
  • Data models collapse multiple domains into one
  • Logic becomes entangled
  • Flows don’t map cleanly to the architecture
  • Systems cannot evolve without major surgery

Real-World Analogies: Where Design Debt Lives

Great systems thinking begins with physical analogies. They make the hidden visible.

Below are a few you can use to frame the idea.

1. Houses and Foundations: When the Basics Are Wrong

If you build a house with:

  • Poor foundation
  • Cheap plumbing
  • Unlabeled wiring
  • No shut-off valves

The house may stand. It might even look good. But every repair becomes horrifyingly expensive.

A single leaking pipe often requires breaking an entire wall.

Design debt is like bad plumbing hidden behind drywall. Invisible… until it ruins everything around it.

Software equivalent:

  • Core module with 10 different responsibilities
  • No clear data boundaries
  • Domain logic living in controllers
  • No versioning strategy for schemas

Early on, everything “just works.” Later, every change feels like cutting open walls.

2. Cities Without Planning: Organic Today, Gridlock Tomorrow

Cities that grow without planning develop:

  • Narrow streets
  • Congested intersections
  • No arterial roads
  • No reserved spaces for future expansion

In year one, it feels fine. In year twenty, it collapses under its own weight.

Retrofitting a subway line into an old city is exponentially harder than planning it from day one.

Software equivalent:

  • A system with no defined “core services”
  • Everything calling everything
  • No observability across services
  • Data inconsistencies across bounded contexts

The system works at small scale. Then traffic spikes… and suddenly nothing works.

3. Factories With Rigid Assembly Lines

Imagine a factory where:

  • Conveyors are bolted permanently
  • Machines are packed tightly
  • Workflow is rigid
  • No bypass routes exist

At low volume: fine. At high volume: catastrophic.

One machine failing halts the entire line.

Software equivalent:

  • Hard-coded workflows
  • No plugin architecture
  • No “workflow as data”
  • One service acting as the orchestrator for everything

The cost of “just add this one more step” becomes massive.

4. Trading Systems: The God Object Trap

Let’s bring it closer to real engineering.

In many trading platforms:

  1. A core Trade object is passed everywhere.

  2. Every group adds fields:

    • Risk adds Greeks
    • Finance adds accounting metadata
    • Reporting adds presentation fields
  3. Over time, the Trade becomes a God object.

The side effects:

  • Changes require cross-team coordination
  • Schema evolution becomes dangerous
  • Pipelines are forced to process more than they need
  • Debugging becomes nearly impossible

This is classic design debt: collapsing multiple bounded contexts into one universal structure.

The Nature of Design Debt: Why It’s a Silent Killer

Design debt doesn’t hurt right away. In fact, early on it often feels like you’re “moving fast”:

  • No need to over-engineer
  • Everything is in one place
  • No interfaces to maintain
  • No abstractions to respect

But design debt has two sinister qualities:

1. Its cost grows non-linearly

  • From 10 features to 50 features: manageable
  • From 50 features to 200 features: impossible without redesign

Design debt doesn’t scale linearly. It compounds.

2. It surfaces under stress

Consider these:

Production outages Regulatory changes Performance spikes New asset classes New business flows

Suddenly:

  • Bugs multiply
  • Debugging becomes archaeology
  • Delivery slows to a crawl
  • Engineers fear touching certain modules

A line I personally like:

Design debt waits for the worst possible moment to send the bill.

Enjoying this post? Check out my book — Designing Trading Systems: Trade Booking, Lifecycle, and Flow for deeper insights into real-world system design.

Common Patterns That Create Design Debt

You will recognize these quickly.

1. God Modules

A module that eventually grows to 5,000 lines because:

  • “Everything important happens here”
  • “Just add one more condition”

These are the hardest to refactor.

2. No Clear Contracts

Functions that take:

{ "data": { "whatever": "we pass around" } }

Or worse, generic untyped blobs.

The caller must remember what’s inside. Tribal knowledge becomes the interface.

3. Leaky Abstractions Everywhere

Upper layers know:

  • DB column names
  • Queue names
  • Internal IDs
  • Internal state transitions

This is system-level spaghetti.

4. No Extension Points

Systems built with no thought for change:

  • Workflows cannot be altered
  • No plug-in mechanism
  • Adding a new type means forking the entire flow

Future changes become invasive surgery.

5. No Observability by Design

When production breaks:

  • No correlation IDs
  • No consistent logging format
  • No system-wide context propagation

Design debt expresses itself as visibility debt.

Symptoms: How to Know Your System Has Design Debt

A few unmistakable signs:

  • Adding a small feature requires touching 5–10 unrelated modules
  • No one can accurately draw the system architecture
  • Everything feels fragile
  • Bugs reappear in different places
  • Debugging takes longer than coding
  • Engineers say: “I hate touching that part of the system”
  • Onboarding new devs takes months, not weeks

If these feel familiar, you’re dealing with accumulated design debt.

How to Minimize Design Debt Early

No system avoids it entirely, but you can reduce it significantly if you design intentionally.

Here are the strategies I use in large systems — especially trading architectures.

1. Define Boundaries Explicitly

Ask:

  • “What are the bounded contexts?”
  • “Which domain owns which data?”
  • “Which flows interact with which components?”

Even a simple diagram helps break monolith thinking.

2. Think in Flows, Not Functions

Flows define the behavior of connected systems.

  • Trade booking
  • Lifecycle transitions
  • Pricing
  • Risk calculations
  • Reporting

Design flows first. Then place components along them.

3. Build Extension Joints

Examples:

  • Plugin points
  • Hook interfaces
  • Event emission points
  • Configuration-driven workflows

These don’t require over-engineering. You just need to leave space for the future.

4. Standardize IDs and Context Passing

This reduces debugging time dramatically.

  • Shared correlation IDs
  • Consistent domain IDs
  • Trace propagation across service boundaries

You cannot add observability later without pain.

5. Thin Vertical Slices Early

Instead of delivering horizontal layers:

Deliver one end-to-end flow that works correctly.

Use it as the “exemplar pattern” for the rest of the system.

6. Review for Scalability—Not Just Correctness

During design reviews, ask:

  • “What happens when we add 10 more workflows?”
  • “What if we support 10 more asset types?”
  • “What if traffic grows by 10x?”
  • “Where will this design break first?”

This type of thinking catches design debt before it hardens.

Closing Thoughts

Design debt forms quietly. It forms when we’re moving fast, under pressure, or trying to hit deadlines.

But in systems thinking — whether in software, cities, factories, or trading platforms — the same truth holds:

Good structure compounds. Bad structure compounds faster.

Design debt isn’t about perfection. It’s about intentionally shaping a system that future you and teams can extend without fear.

Minimize design debt early, and you gain long-term velocity. Ignore it, and the system eventually resists every change you try to make.

Enjoyed this post? Check out my book — Designing Trading Systems: Trade Booking, Lifecycle, and Flow for deeper insights into real-world system design.