Software design notes.

Software design notes. Most of them come from 'A Philosophy of Software Design' Book.

Software design notes

This post should give a general idea how good software desing should look like and what is all about - clarity.

Managing Complexity: The Core of Software Design

Complexity is the enemy. Every bug, delay, or architectural mess traces back to one root cause — code that became too complex to reason about. The primary goal of software design is not just to make things work, but to make them understandable.

Deep Modules Hide Complexity

A good design hides as much internal logic as possible. Shallow modules expose too much and achieve too little. Deep modules expose little and achieve a lot.

The difference is in abstraction depth — not size. Deep modules act like black boxes: you interact through a small interface and trust that the internals do their job. This reduces the number of moving parts visible at higher levels.

Think of it like a sorting function. You don’t care how it sorts — only that it returns a sorted result.

Hide Information, Expose Behavior

Every component should describe what it does, not how it does it. Internal structures, data formats, and helper logic should remain invisible. When you leak implementation details, you create unnecessary coupling. That coupling multiplies complexity across the system.

Good abstraction isolates change. If you can rewrite the internals of a module without touching its consumers, you designed it right.

Design the Interface First

Implementation is easy to fix. Interfaces are not. Start by defining clear function signatures, inputs, and expected outputs before touching the implementation. This process clarifies purpose and prevents internal details from leaking into the interface.

Working Code Isn’t Enough

Code that runs is only the first checkpoint. Maintainability, readability, and clarity determine whether it survives.

Every “quick fix” adds a layer of design debt. You might not pay immediately, but the interest compounds — in debugging sessions, failed tests, and lost weekends.

Refactoring is not cleanup. It’s design maintenance.

Handle Design Debt Early

Design debt accumulates invisibly — in tangled dependencies and poorly named abstractions. Once it spreads, it’s expensive to clean. Review designs early, refactor continuously, and isolate complexity before it leaks outward.

Design It Twice

First ideas are prototypes. The second pass is design. Iteration is how abstraction matures — the first version teaches you what not to do.

Build, observe, redesign.

Pull Complexity Downwards

Keep complexity contained in low-level modules. When upper layers stay clean, your system remains predictable. If the complex logic is isolated and reused, it becomes manageable. The goal is not to eliminate complexity, but to concentrate it where it can be controlled.

Comments Should Explain Why

Code already shows what happens. Comments should show why. Describe intent, reasoning, and design tradeoffs. A good comment documents thought — not syntax.

Consistency and Naming

Consistency lowers the cognitive load. When names, patterns, and conventions follow the same logic, developers can navigate code instinctively. Breaking this rhythm is like changing the recursion pattern halfway through the call — you confuse both the reader and the runtime.

Use Patterns, Don’t Worship Them

Design patterns are tools — not templates. Use them only when they reduce complexity or make intent clearer. Adding a pattern where it’s not needed increases abstraction cost without real benefit.

Summary

Software design is not about cleverness — it’s about clarity. Each abstraction layer should remove unnecessary thought from the next one up. When the system feels simple to reason about, you have done the hardest part right.

Sources

  • A Philosophy of Software Design - John Ousterhout (2018)
Built with Hugo
Theme Stack designed by Jimmy