Second edition · Paperback and Kindle
A Practical Guide to Building Software That Can Change
Software design principles, trade-offs and architecture for working developers. Learn when each principle helps, what it costs, and when to leave it alone, by following one real system from its first messy version to a design your team can change without fear.
- 371 pages
- Java 17, no frameworks
- Tested code on GitHub
Chapter 1 opens with this
The one-line change that took a week
“It should have been a one-line change.”
The product owner lowers the annual discount from 20% to 15%. A developer changes the number, runs the tests and deploys. Two days later the checkout page promises one price and the invoice charges another.
Nothing was wrong with the syntax. The same business rule lived in two places, and only one of them received the change. The book starts here, and every chapter adds one more requirement that tests the design.
public BigDecimal priceFor(String plan, boolean annual) { BigDecimal monthly = switch (plan) { case "BASIC" -> new BigDecimal("9.99"); case "PRO" -> new BigDecimal("19.99"); default -> throw new IllegalArgumentException( "Unknown plan: " + plan); }; if (annual) { return monthly.multiply(BigDecimal.valueOf(12)) .multiply(new BigDecimal("0.80")) .setScale(2, RoundingMode.HALF_UP); } return monthly; }
public BigDecimal amountDue(Subscription subscription) { BigDecimal monthly = subscription.getPlan().equals("PRO") ? new BigDecimal("19.99") : new BigDecimal("9.99"); // unknown plan? Basic if (subscription.isAnnual()) { return monthly.multiply(new BigDecimal("12")) .multiply(new BigDecimal("0.8")) .setScale(2, RoundingMode.HALF_UP); } return monthly; }
2 copies of one pricing rule, already disagreeing about unknown plans.
public class PricingPolicy {
private static final BigDecimal ANNUAL_DISCOUNT =
new BigDecimal("0.15");
private static final int MONTHS_PER_YEAR = 12;
public BigDecimal priceFor(Plan plan, BillingCycle cycle) {
if (cycle == BillingCycle.MONTHLY) {
return plan.monthlyPrice();
}
BigDecimal fullYear = plan.monthlyPrice()
.multiply(BigDecimal.valueOf(MONTHS_PER_YEAR));
return fullYear
.multiply(BigDecimal.ONE.subtract(ANNUAL_DISCOUNT))
.setScale(2, RoundingMode.HALF_UP);
}
}
public BigDecimal priceFor(Plan plan, BillingCycle cycle) { return pricing.priceFor(plan, cycle); } public BigDecimal amountDue(Subscription subscription) { return pricing.priceFor(subscription.plan(), subscription.cycle()); }
1 line to change the discount. An unknown plan cannot even reach the pricing code.
One system, eleven chapters
Every principle arrives when a real requirement needs it
You follow SubFlow, a subscription-billing service, from a procedural first version to a tested, modular system with a public API, two payment providers and prices that marketing changes without a deployment. The requirements are ordinary. That is the point: you will meet every one of them at work.
Part I · Changeable code
- 01Prices copied in three placesOne home for pricing; layers
- 02Invalid money; partner pricesValue objects, valid states, composition
- 03Promotions, a second payment provider, SMSSOLID: pricing rules and payment ports
- 04Customers charged twice after a timeoutIdempotent charges, fakes, contract tests
- 05The mobile team wants the pricing codeWhen principles conflict; decision records
Part II · Changeable systems
- 06A partner retries; webhooks arrive twiceAPI contracts, idempotency keys, the outbox
- 0710,000 requests a minute; provider outagesModular monolith, circuit breaker, failover
- 08New developers need to understand itC4 and UML diagrams the build keeps true
Part III · Design in practice
- 09Three systems from a blank pageA repeatable seven-step walk-through
- 10“Not now” from the head of productThe skills that get a design approved
- 11Where do you go from here?A growth path and a last exercise
How each principle is taught
The whole picture, including the part most books skip
DRY, KISS, YAGNI, coupling and cohesion, SOLID, testability, boundaries and architecture all follow the same ten steps. Steps 7 and 8 are the reason this book exists: a principle applied without judgment can make code worse, so each one comes with its costs and with the cases where you should not use it.
Chapter 5 then puts principles against each other, DRY against simplicity or reuse against independent evolution, and gives you the questions that settle the argument.
- The design problem
- A problematic example
- Symptoms
- The principle
- The refactoring, step by step
- Benefits
- Costs and trade-offs
- When not to apply it
- Exercises on your own code
- A short checklist
Free with every copy, and free here
Try the one-page design review on your current pull request
Appendix A collects every checklist in the book, plus a one-page review of 24 questions for the moment before you start a change or send a pull request. Here are six of them. Tick the ones your current change passes.
0 of 6 checked
- Ch. 1
- Ch. 2
- Ch. 1, 3
- Ch. 4, 9
- Ch. 4
- Ch. 5
Who it is for
For developers who can already write working code
New to object-oriented design
A structured path from your first refactoring to architecture, with every example running in tests.
Read all chapters in orderExperienced, and want better judgment
The trade-offs and the “when not to” that most introductions leave out.
Chapters 1, 3, 4 and 5Moving toward a senior or lead role
Boundaries, architecture, diagrams, and how to explain a design to managers and teams.
Chapters 1, 5 to 10Working mostly on APIs and integrations
Idempotency, error contracts, versioning, failures and three systems designed from scratch.
Chapters 1, 4, 6 and 9Questions
Before you buy
- I don’t write Java. Is it for me?
- Yes. The examples use plain Java 17 because it reads clearly, but the principles apply equally to C#, Kotlin, TypeScript, Python and other languages. No framework knowledge is needed.
- I read the first edition. What is new?
- Everything was rewritten around one growing system, and there are four new chapters: testability, conflicting principles, boundaries and APIs, and architecture. UML became a chapter on communicating design, and four appendices were added.
- Where is the code?
- On GitHub at github.com/mgamio/subFlow, one Maven module per chapter, with the “before” versions kept next to the refactored ones and tests that prove the behavior stayed the same.
- Paperback or Kindle?
- The paperback is 6 × 9 inches with figures and code printed for reading on paper. The Kindle edition has the same content, with linked chapters for reading on any device.
Stop fearing the next change request.
Get the second edition and the companion code, and make your next change the easy kind.