Simyl
simylflow
Course Home
Module 2: Technical Practices
Lesson 5 of 5
14 min

Simple Design

Build the simplest thing that works. Resist the urge to over-engineer.

1The Simplest Thing That Works

XP has a mantra: Do the simplest thing that could possibly work.

This isn't about being lazy or writing hacky code. Simple code is often harder to write than complex code. It requires:

  • Understanding the problem deeply
  • Resisting the urge to generalize
  • Saying "no" to speculation
  • Trusting that you can change things later

Simple code is:

  • Easy to understand
  • Easy to change
  • Easy to test
  • Just enough for current needs

Complex code is:

  • Hard to understand
  • Risky to change
  • Hard to test
  • Built for imagined future needs that may never come

Simple Is Hard

"I would have written a shorter letter, but I did not have the time." —Blaise Pascal. Simple code requires more thought, not less.

2YAGNI: You Aren't Gonna Need It

YAGNI is the principle that you should only build what you need right now.

Don't add:

  • Features because they "might be useful"
  • Configuration options because users "might want them"
  • Extension points because the code "might need them"
  • Abstractions for hypothetical future requirements

Why? Because:

  • You're usually wrong about what you'll need
  • Unused code still has maintenance cost
  • Every abstraction has a price in complexity
  • Building for the future delays delivering for today

The XP bet: If you need something later, you can add it later. With TDD, refactoring, and CI, change is cheap. So don't pay for the future before you have to.

This is a radical idea. Traditional software engineering says anticipate change and design for flexibility. XP says wait until you actually need it.

Good YAGNI

The team needs to send emails. They implement sending via SMTP. Later, they need to send via SendGrid. They refactor. Total cost: less than building a pluggable email abstraction up front.

Violating YAGNI

The team needs to send emails. They build a generic 'MessageProvider' interface with pluggable 'MessageTransport' and 'MessageFormatter' abstractions. They only ever use SMTP with one format. The abstractions slow down every change.

3The Four Rules of Simple Design

Kent Beck defined simple design as code that:

1. Passes all the tests The code works. This is non-negotiable. A beautiful design that doesn't work is worthless.

2. Reveals intention The code is easy to understand. Good names, clear structure, readable organization. Someone new can pick it up and understand what it does.

3. Has no duplication (DRY) Each piece of knowledge is expressed once and only once. Duplication creates maintenance burden and bug risk.

4. Has the fewest elements No extra classes, methods, variables, or abstractions. Everything that exists has a reason to exist.

The rules are in priority order. Passing tests trumps everything. But once tests pass, favor clarity over DRYness. And only add elements that serve the first three rules.

4Incremental Design

Simple design doesn't mean no design. It means incremental design—evolving the design as you learn.

The process:

  1. Start with the simplest implementation that works
  2. As you add features, notice friction (this is getting awkward)
  3. Refactor to address the friction
  4. The design emerges from real needs, not speculation

This is why XP emphasizes refactoring so heavily. If you can't change the code safely, you can't do incremental design. You're stuck with whatever you built first.

Big Up-Front Design (BUFD) fails because:

  • You don't know enough at the start
  • Requirements change
  • Designs don't survive contact with reality
  • You pay for complexity you never use

Incremental design succeeds because:

  • You design based on real needs
  • The design evolves with understanding
  • You never pay for what you don't use
  • Refactoring keeps the design clean

When you feel the urge to add an abstraction, ask: "Do I need this now, or am I guessing?" If you're guessing, wait.

5When Complexity Is Warranted

Simple design doesn't mean avoiding all complexity. Sometimes complexity is necessary.

Add complexity when:

  • Tests require it (make it pass)
  • Clarity requires it (reveal intention)
  • Removing duplication requires it (DRY)
  • Current, real requirements need it

Don't add complexity for:

  • "What if?" scenarios
  • Possible future features
  • Frameworks you think you might need
  • Making the code "more flexible"

The difference is between essential complexity (inherent in the problem) and accidental complexity (introduced by your solution). XP minimizes accidental complexity by refusing to build for imaginary requirements.

When you do need an abstraction, let it emerge from removing duplication. The "Rule of Three" is helpful: don't abstract until you see the same pattern three times. By then you understand the actual variations, not imagined ones.

Key Takeaways
  • Simple code is harder to write but easier to change
  • YAGNI: don't build for requirements you don't have yet
  • The four rules: passes tests, reveals intention, no duplication, fewest elements
  • Let design emerge incrementally through refactoring
  • Add complexity when tests, clarity, or DRY require it—not for speculation
Common Pitfalls to Avoid
  • Confusing simple with simplistic (simple is elegant, not crude)
  • Building abstractions before you need them
  • Assuming you know what future requirements will be
  • Skipping refactoring, which prevents design evolution

Practice Exercises