Sandi Metz’s Practical Object-Oriented Design in Ruby (commonly known as POODR) is a guide to writing Ruby code that is flexible, maintainable, and easy to change over time. Rather than treating object-oriented design as a collection of abstract principles, Metz grounds every concept in concrete, evolving code examples — most famously a bicycle shop application that grows in complexity across chapters. Her voice is unusually warm and direct for a technical book; she writes as though she is sitting beside you at a keyboard, explaining not just what to do but why certain design decisions make future change cheaper and less painful.
The book’s central argument is that the true cost of software is not in writing it but in changing it, and that good object-oriented design is fundamentally about managing dependencies. Metz works through the core tools available to a Ruby programmer — classes, modules, inheritance, composition, duck typing, and message-passing — and shows how each can be used well or badly depending on whether it reduces or multiplies the cost of future modification. Throughout, she draws on the SOLID principles and related ideas without being doctrinaire, always returning to the practical question: does this design make your application easier to work with next month?
Key takeaways
-
Single Responsibility Principle as a design litmus test. Every class and every method should do one thing. Metz offers a simple heuristic: describe what a class does in one sentence, and if that sentence contains the word “and,” the class probably has too many responsibilities. Small, focused objects are easier to reuse and far easier to change.
-
Dependency management is the heart of OOP. Objects inevitably need to know about other objects, but how they know matters enormously. Metz advocates for dependency injection — passing collaborators in rather than hard-coding them — so that classes depend on abstractions rather than concrete implementations, keeping each object ignorant of who, exactly, it is talking to.
-
Duck typing over rigid class hierarchies. Ruby’s dynamic type system allows objects to be treated by what they do (their interface) rather than what they are (their class). Metz shows how leaning into duck typing — designing around shared message interfaces rather than shared ancestry — produces more flexible code and frees designs from the brittleness of deep class checking.
-
Prefer composition over inheritance, but use inheritance well when you do use it. Inheritance is appropriate only when objects genuinely share an “is-a” relationship and when the hierarchy is shallow and stable. When it is appropriate, Metz teaches how to use template methods and hook messages to keep subclasses from depending on the internal logic of their superclass. For most other sharing needs, she recommends modules or straightforward object composition.
-
Design tests that serve the code, not the other way around. A chapter on testing argues that tests should be coupled to the public interface of objects, not to their private implementation. Metz introduces the idea of role tests using shared example groups, so that any object claiming to play a certain role can be verified against the same expectations — a technique that keeps test suites honest and maintainable as designs evolve.
-
The cost of change is the ultimate metric. Metz repeatedly returns to the idea that there is no universally “correct” design, only designs that are more or less expensive to change given what you know right now. She introduces concepts like the Dependency Inversion Principle and the Law of Demeter not as rules to follow religiously but as guides for reducing the coupling that makes change painful.
-
Writing code for a future reader. Underlying every chapter is an ethical and professional argument: the people who will maintain your code deserve clarity. Metz frames good design partly as an act of respect toward your collaborators and your future self, making the book as much about craft and professional values as about syntax and patterns.