Chapter 2 — A Pragmatic Approach

Topics 8–15 | Pages 73–142


Core Idea

Universal principles that apply at all levels of software development: design, architecture, delivery, and estimation. All of them flow from one root idea — make things easy to change.


Topic 8 — The Essence of Good Design (p75)

Tip 14: Good Design Is Easier to Change Than Bad Design

The ETC principle (Easier to Change) unifies every design principle:

  • Why is decoupling good? → Isolated concerns are easier to change.
  • Why is SRP useful? → A requirement change maps to one module change.
  • Why does naming matter? → You must read code to change it.

ETC is a value, not a rule. It guides choices. When uncertain which path is more changeable: write replaceable code (decoupled, cohesive), and note your reasoning in your daybook to build instincts over time.


Topic 9 — DRY — The Evils of Duplication (p79)

Tip 15: DRY — Don’t Repeat Yourself

Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.

DRY is not about code duplication — it’s about knowledge duplication. The acid test: when a single fact about the domain changes, how many places in the code must you update?

Types of duplication:

TypeExampleFix
Code duplicationRepeated formatting logicExtract function
Documentation duplicationComment restates what code already saysDelete the comment; improve naming
Data structure duplicationlength stored alongside start/endMake it a computed field
API/external schema duplicationCode that mirrors an external specUse code generation, OpenAPI, or schema introspection
Interdeveloper duplicationSame logic written by two teamsFoster communication, appoint a “project librarian,” make reuse easy

Important distinction: Two functions with the same body but different intent are NOT a DRY violation. The coincidental similarity doesn’t mean they represent the same knowledge.

Tip 16: Make It Easy to Reuse
If reusing existing code is harder than writing new code, developers won’t do it, and duplication spreads.


Topic 10 — Orthogonality (p92)

Two things are orthogonal if changing one has no effect on the other. The goal: self-contained components with a single, well-defined purpose (cohesion).

Tip 17: Eliminate Effects Between Unrelated Things

Benefits:

  • Productivity: Changes are localized; each component can be developed and tested independently; combined orthogonal components multiply rather than overlap in capability.
  • Risk reduction: Diseased code is isolated; fixes don’t ripple; third-party dependencies are bounded.

Orthogonality test: If a requirement changes, how many modules must change? The answer should be one.

Coding practices for orthogonality:

  • Write shy code — don’t expose what’s unnecessary; don’t rely on others’ implementations (Law of Demeter)
  • Avoid global data — globals couple every module that touches them
  • Avoid similar functions — structural duplication signals poor separation; use Strategy pattern
  • Use layering — each layer only depends on the layer below it

Orthogonality and testing: If a unit test requires importing half the system to run, the module is not orthogonal. Bug fixes that scatter changes across many files indicate poor orthogonality.

DRY vs. Orthogonality:

  • DRY: minimize knowledge duplication
  • Orthogonality: minimize interdependency
  • Use both together

Topic 11 — Reversibility (p104)

Requirements, vendors, architectures, and environments all change — often in ways you can’t predict. Treating any decision as final is a mistake.

Tip 18: There Are No Final Decisions
Tip 19: Forgo Following Fads

Strategy: abstract away dependencies so swapping them doesn’t require rewriting everything.

  • Hide third-party APIs behind your own abstraction layers
  • Break code into components (even if deployed monolithically at first)
  • Don’t chase the latest architectural trend; ensure code can “roll with the punches”

Architectural fashions (2000–2019): big iron → federation → commodity clusters → VMs → services → containers → serverless → back to iron. You can’t predict which will dominate. Build to swap.


Topic 12 — Tracer Bullets (p109)

Tip 20: Use Tracer Bullets to Find the Target

Tracer bullets = thin, end-to-end slices of functionality that traverse all layers of the system, built early to validate architecture and gather feedback. They are production code — not thrown away.

When to use: requirements are vague, tech stack is unfamiliar, or you need to show progress.

Advantages over “code in modules, then integrate”:

  • Users see something working early and can correct direction
  • Provides a skeleton that keeps the team consistent
  • Enables daily (or more frequent) integration
  • Always have something to demo
  • Progress is visibly measurable use case by use case

Tracer Bullets vs. Prototypes:

Tracer BulletsPrototypes
PurposeValidate architecture end-to-endExplore a specific risk/question
CodeProduction-quality, keptThrowaway
ScopeThin slice of whole systemOne aspect (UI, algorithm, etc.)

Prototype is reconnaissance; tracer is the first real shot.


Topic 13 — Prototypes and Post-it Notes (p118)

Tip 21: Prototype to Learn

Value is in the lessons, not the code. Prototypes answer specific questions about risky/unknown areas: architecture, UI, third-party integrations, algorithms, performance.

What you can ignore in a prototype: correctness, completeness, robustness, style/docs.

Critical warning: Make it crystal clear prototypes are disposable. Stakeholders and managers may try to deploy prototype code. If the culture won’t accept that prototypes get thrown away, use tracer bullets instead.

Use high-level scripting languages for prototypes (Python, Ruby) — they get out of your way faster.


Topic 14 — Domain Languages (p124)

Tip 22: Program Close to the Problem Domain

Domain-specific languages (DSLs) let you express intent in terms of the problem domain.

TypeExamplesTradeoffs
Internal DSLRSpec, Phoenix RouterBound by host language syntax; no parser needed; can use all host language features
External DSLCucumber, Ansible (YAML)Full syntax freedom; requires parser; more effort

Practical advice:

  • Use off-the-shelf external formats (YAML, JSON, CSV) when possible
  • For internal DSLs, simple named functions often suffice — no metaprogramming magic needed
  • Don’t spend more effort on the DSL than you save

Business users rarely read Cucumber specs — they want runnable software, not documents. Their real needs surface when they interact with code.


Topic 15 — Estimating (p133)

Tip 23: Estimate to Avoid Surprises
Tip 24: Iterate the Schedule with the Code

Estimation accuracy scales with time unit used:

DurationExpress in
1–15 daysDays
3–6 weeksWeeks
8–20 weeksMonths
20+ weeksThink hard before estimating

Estimation process:

  1. Understand what’s being asked — scope, implicit assumptions
  2. Build a model — mental sketch of the system or process
  3. Break model into components — identify interaction rules
  4. Give each parameter a value — focus accuracy on high-impact multipliers
  5. Calculate — use ranges, not single numbers; strange results reveal model errors
  6. Track your estimates — record and review; find out why estimates were wrong

For project schedules: don’t try to nail down the full timeline upfront. Use incremental development; refine the estimate each iteration based on actual velocity.

What to say when asked for an estimate: “I’ll get back to you.” Never estimate at the coffee machine.


Key Takeaways

  1. ETC (Easier to Change) is the root principle; all other design rules are special cases.
  2. DRY is about knowledge duplication, not code duplication — the acid test is how many places change when a fact changes.
  3. Orthogonality means independent components; changes stay local, tests stay small.
  4. No architectural decision is final — build to swap vendors, patterns, and platforms.
  5. Tracer bullets get end-to-end working fast, stay in production, and correct direction early.
  6. Prototypes are throwaway; make that explicit or use tracer bullets instead.
  7. When asked for an estimate, say “I’ll get back to you” — and iterate the schedule with the code.