> ## Content Index
> Fetch the complete content index at: https://www.antoinebuteau.com/llms.txt
> Use this file to discover other available public pages before exploring further.

# Lessons from Sandi Metz
- URL: https://www.antoinebuteau.com/lessons-from-sandi-metz/
- Published: 2026-05-30T18:20:26.000Z
- Updated: 2026-07-18T21:57:18.000Z
- Description: Sandi Metz, a software engineer and author, champions simple, changeable object-oriented code over clever abstraction. Her practical guidance on testing and refactoring centers a durable judgment call: when duplication is tolerable and when design should evolve.
- Author: Antoine Buteau
- Tags: Profile, Developer Tools, Science & Technology Thinkers Profiles

![Visual summary of operating lessons from Sandi Metz.](https://www.antoinebuteau.com/content/images/2026/05/lessons-from-sandi-metz-profile-infographic.webp)

## Lessons from Sandi Metz

Software engineer Sandi Metz writes clear, practical guides to object-oriented programming. In books like *Practical Object-Oriented Design in Ruby* and *99 Bottles of OOP*, she consistently argues for simple, changeable code over clever abstractions. This collection gathers her core advice on testing, refactoring, and knowing when to tolerate duplication.

### Part 1: The Cost of Abstraction and Duplication

1. **On the wrong abstraction:** "Duplication is far cheaper than the wrong abstraction." — [*Source: \[SandiMetz.com*](https://sandimetz.com/blog/2016/1/20/the-wrong-abstraction?ref=antoinebuteau.com)*\]*
2. **On managing duplication:** "It is cheaper to manage temporary duplication than to recover from incorrect abstractions." — [*Source: \[99 Bottles of OOP*](https://sandimetz.com/99bottles?ref=antoinebuteau.com)*\]*
3. **On recovering from bad design:** "When the abstraction is wrong, the fastest way forward is back." — [*Source: \[SandiMetz.com*](https://sandimetz.com/blog/2016/1/20/the-wrong-abstraction?ref=antoinebuteau.com)*\]*
4. **On fixing a bad abstraction:** "Once an abstraction is proved wrong the best strategy is to re-introduce duplication and let it show you what's right." — [*Source: \[SandiMetz.com*](https://sandimetz.com/blog/2016/1/20/the-wrong-abstraction?ref=antoinebuteau.com)*\]*
5. **On premature DRYing:** "We are killing ourselves DRYing too early." — [*Source: \[99 Bottles of OOP*](https://sandimetz.com/99bottles?ref=antoinebuteau.com)*\]*
6. **On discovering abstractions:** "Abstractions are found, not created." — [*Source: \[99 Bottles of OOP*](https://sandimetz.com/99bottles?ref=antoinebuteau.com)*\]*
7. **On committing to code structures:** "When you DRY your code, you are making a commitment to an abstraction. If you don't know enough about your domain, then your abstractions are likely incorrect." — [*Source: \[99 Bottles of OOP*](https://sandimetz.com/99bottles?ref=antoinebuteau.com)*\]*
8. **On acceptable repetition:** "If the code is understandable and doesn't change, then duplication costs nothing." — [*Source: \[99 Bottles of OOP*](https://sandimetz.com/99bottles?ref=antoinebuteau.com)*\]*
9. **On balancing concretions:** "Concrete code brings a clear comprehension over what is going on. Abstract code allows us to easily change the behavior. As developers, we must find that balance." — [*Source: \[99 Bottles of OOP*](https://sandimetz.com/99bottles?ref=antoinebuteau.com)*\]*
10. **On choosing between the two:** "Prefer duplication over the wrong abstraction." — [*Source: \[RailsConf 2014*](https://www.youtube.com/watch?v=8bZh5LMaSmE&ref=antoinebuteau.com)*\]*

### Part 2: Object-Oriented Design and Messages

1. **On the true nature of objects:** "You don't send messages because you have objects, you have objects because you send messages." — [*Source: \[Practical Object-Oriented Design in Ruby*](https://www.goodreads.com/book/show/13501725-practical-object-oriented-design-in-ruby?ref=antoinebuteau.com)*\]*
2. **On inheritance:** "Inheritance is, at its core, a mechanism for automatic message delegation." — [*Source: \[Practical Object-Oriented Design in Ruby*](https://www.goodreads.com/book/show/13501725-practical-object-oriented-design-in-ruby?ref=antoinebuteau.com)*\]*
3. **On public interfaces:** "Create public methods that allow senders to get what they want without knowing how your class implements its behavior." — [*Source: \[Practical Object-Oriented Design in Ruby*](https://www.goodreads.com/book/show/13501725-practical-object-oriented-design-in-ruby?ref=antoinebuteau.com)*\]*
4. **On dependency management:** "Depend on things that change less often than you do." — [*Source: \[Practical Object-Oriented Design in Ruby*](https://www.goodreads.com/book/show/13501725-practical-object-oriented-design-in-ruby?ref=antoinebuteau.com)*\]*
5. **On the danger of dependencies:** "Every dependency is like a little dot of glue that causes your class to stick to the things it touches." — [*Source: \[Practical Object-Oriented Design in Ruby*](https://www.goodreads.com/book/show/13501725-practical-object-oriented-design-in-ruby?ref=antoinebuteau.com)*\]*
6. **On technical debt:** "Dependencies are killing you. Design might save you." — [*Source: \[Practical Object-Oriented Design in Ruby*](https://www.goodreads.com/book/show/13501725-practical-object-oriented-design-in-ruby?ref=antoinebuteau.com)*\]*
7. **On behavior over state:** "Objects should rely on the behavior of other objects, rather than interrogating their state." — [*Source: \[Practical Object-Oriented Design in Ruby*](https://www.goodreads.com/book/show/13501725-practical-object-oriented-design-in-ruby?ref=antoinebuteau.com)*\]*
8. **On procedural vs object-oriented mindsets:** "Are you going to write procedures or trust objects?" — [*Source: \[RailsConf 2014*](https://www.youtube.com/watch?v=8bZh5LMaSmE&ref=antoinebuteau.com)*\]*
9. **On coupling:** "Code that can tolerate change couples to stability." — [*Source: \[RailsConf 2016*](https://www.youtube.com/watch?v=qEjs2A%5FB1t4&ref=antoinebuteau.com)*\]*
10. **On the purpose of design:** "The fundamental purpose of design is to allow you to do design later, and its primary goal is to reduce the cost of change." — [*Source: \[Practical Object-Oriented Design in Ruby*](https://www.goodreads.com/book/show/13501725-practical-object-oriented-design-in-ruby?ref=antoinebuteau.com)*\]*

### Part 3: The Magic Tricks of Testing

1. **On the purpose of tests:** "Tests are supposed to save us money." — [*Source: \[RailsConf 2013*](https://www.youtube.com/watch?v=URSWYvyc42M&ref=antoinebuteau.com)*\]*
2. **On test absence:** "The best kind of test is the one you don't have to write." — [*Source: \[RailsConf 2013*](https://www.youtube.com/watch?v=URSWYvyc42M&ref=antoinebuteau.com)*\]*
3. **On implementation details:** "Test the interface, not the implementation." — [*Source: \[RailsConf 2013*](https://www.youtube.com/watch?v=URSWYvyc42M&ref=antoinebuteau.com)*\]*
4. **On the freedom to refactor:** "If I test only the interface, it means I can change the implementation without breaking the test." — [*Source: \[RailsConf 2013*](https://www.youtube.com/watch?v=URSWYvyc42M&ref=antoinebuteau.com)*\]*
5. **On private methods:** "Don't test private methods." — [*Source: \[RailsConf 2013*](https://www.youtube.com/watch?v=URSWYvyc42M&ref=antoinebuteau.com)*\]*
6. **On internal messages:** "Ignore both Queries and Commands sent to self!" — [*Source: \[RailsConf 2013*](https://www.youtube.com/watch?v=URSWYvyc42M&ref=antoinebuteau.com)*\]*
7. **On the emotional arc of testing:** "Tests often get worse right before they're about to get better." — [*Source: \[RailsConf 2013*](https://www.youtube.com/watch?v=URSWYvyc42M&ref=antoinebuteau.com)*\]*
8. **On test-driven design constraints:** "TDD will punish you if you don't understand design." — [*Source: \[RailsConf 2013*](https://www.youtube.com/watch?v=URSWYvyc42M&ref=antoinebuteau.com)*\]*
9. **On failing tests during refactoring:** "If you rearrange code without changing behavior and tests begin to fail, then the tests themselves are flawed." — [*Source: \[99 Bottles of OOP*](https://sandimetz.com/99bottles?ref=antoinebuteau.com)*\]*
10. **On where abstractions belong:** "Tests are not the place for abstractions—they are the place for concretions." — [*Source: \[99 Bottles of OOP*](https://sandimetz.com/99bottles?ref=antoinebuteau.com)*\]*

### Part 4: Managing Changeability

1. **On design vs perfection:** "Design is more the art of preserving changeability than it is the act of achieving perfection." — [*Source: \[Practical Object-Oriented Design in Ruby*](https://www.goodreads.com/book/show/13501725-practical-object-oriented-design-in-ruby?ref=antoinebuteau.com)*\]*
2. **On software lifespan:** "Your application needs to work right now just once; it must be easy to change forever." — [*Source: \[Practical Object-Oriented Design in Ruby*](https://www.goodreads.com/book/show/13501725-practical-object-oriented-design-in-ruby?ref=antoinebuteau.com)*\]*
3. **On undesigned applications:** "Successful (working) but undesigned applications carry the seeds of their own destruction; they are easy to write, but gradually become impossible to change." — [*Source: \[Practical Object-Oriented Design in Ruby*](https://www.goodreads.com/book/show/13501725-practical-object-oriented-design-in-ruby?ref=antoinebuteau.com)*\]*
4. **On future knowledge:** "The future is uncertain and you will never know less than you know right now." — [*Source: \[Practical Object-Oriented Design in Ruby*](https://www.goodreads.com/book/show/13501725-practical-object-oriented-design-in-ruby?ref=antoinebuteau.com)*\]*
5. **On making code work:** "Anyone can arrange code to make it work right now." — [*Source: \[Practical Object-Oriented Design in Ruby*](https://www.goodreads.com/book/show/13501725-practical-object-oriented-design-in-ruby?ref=antoinebuteau.com)*\]*
6. **On code failing:** "The first way design fails is due to lack of it." — [*Source: \[Practical Object-Oriented Design in Ruby*](https://www.goodreads.com/book/show/13501725-practical-object-oriented-design-in-ruby?ref=antoinebuteau.com)*\]*
7. **On the cost of decisions:** "Every decision you make includes two costs: one to implement it and another to change it when you discover that you were wrong." — [*Source: \[Practical Object-Oriented Design in Ruby*](https://www.goodreads.com/book/show/13501725-practical-object-oriented-design-in-ruby?ref=antoinebuteau.com)*\]*
8. **On the single responsibility principle sentence test:** "Attempt to describe a class in one sentence. If the simplest description you can devise uses the word 'and,' the class likely has more than one responsibility." — [*Source: \[Practical Object-Oriented Design in Ruby*](https://www.goodreads.com/book/show/13501725-practical-object-oriented-design-in-ruby?ref=antoinebuteau.com)*\]*
9. **On disjointed responsibilities:** "If it uses the word 'or,' then the class has more than one responsibility and they aren't even very related." — [*Source: \[Practical Object-Oriented Design in Ruby*](https://www.goodreads.com/book/show/13501725-practical-object-oriented-design-in-ruby?ref=antoinebuteau.com)*\]*

### Part 5: Refactoring Strategy

1. **On making changes:** "Make the change easy (warning: this might be hard), then make the easy change." — [*Source: \[RailsConf 2014*](https://www.youtube.com/watch?v=8bZh5LMaSmE&ref=antoinebuteau.com)*\]*
2. **On incremental refactoring:** "The plan is to nibble away, one code smell at a time, in faith that the path to openness will be revealed." — [*Source: \[99 Bottles of OOP*](https://sandimetz.com/99bottles?ref=antoinebuteau.com)*\]*
3. **On tolerating ugly code:** "Shameless Green requires a tolerance for duplication, an abstention from cleverness, and patience to wait for change." — [*Source: \[99 Bottles of OOP*](https://sandimetz.com/99bottles?ref=antoinebuteau.com)*\]*
4. **On seeking answers:** "You don't know the answer in advance, instead you are seeking it." — [*Source: \[99 Bottles of OOP*](https://sandimetz.com/99bottles?ref=antoinebuteau.com)*\]*
5. **On intuition in programming:** "I'm a big believer in how things feel in programming." — [*Source: \[99 Bottles of OOP*](https://sandimetz.com/99bottles?ref=antoinebuteau.com)*\]*
6. **On hiding complexity:** "Avoid hiding the dirt under the carpet by extracting messy code into private methods with poor names." — [*Source: \[RailsConf 2016*](https://www.youtube.com/watch?v=qEjs2A%5FB1t4&ref=antoinebuteau.com)*\]*
7. **On learning the magic tricks:** "Good tests aren't magic. They're magic tricks — and you can learn them." — [*Source: \[RailsConf 2013*](https://www.youtube.com/watch?v=URSWYvyc42M&ref=antoinebuteau.com)*\]*
8. **On the programmer's ego:** "Abstention from cleverness is a core requirement for writing code that is easy to replace." — [*Source: \[99 Bottles of OOP*](https://sandimetz.com/99bottles?ref=antoinebuteau.com)*\]*
9. **On discovering paths:** "When you are stuck in a bad design, reintroducing the original code often reveals the correct path forward naturally." — [*Source: \[SandiMetz.com*](https://sandimetz.com/blog/2016/1/20/the-wrong-abstraction?ref=antoinebuteau.com)*\]*

### Part 6: Sandi's Rules for Developers

1. **On class sizing constraints:** "Classes can be no longer than 100 lines of code." — [*Source: \[The Bike Shed*](https://www.thoughtbot.com/blog/the-bike-shed-episode-1-sandi-and-dereks-rules?ref=antoinebuteau.com)*\]*
2. **On parameter limits:** "Pass no more than 4 parameters into a method." — [*Source: \[The Bike Shed*](https://www.thoughtbot.com/blog/the-bike-shed-episode-1-sandi-and-dereks-rules?ref=antoinebuteau.com)*\]*
3. **On controllers in MVC:** "Controllers can instantiate only one object." — [*Source: \[The Bike Shed*](https://www.thoughtbot.com/blog/the-bike-shed-episode-1-sandi-and-dereks-rules?ref=antoinebuteau.com)*\]*
4. **On view knowledge:** "Views can only know about one instance variable." — [*Source: \[The Bike Shed*](https://www.thoughtbot.com/blog/the-bike-shed-episode-1-sandi-and-dereks-rules?ref=antoinebuteau.com)*\]*
5. **On method brevity:** "Methods can be no longer than 5 lines of code." — [*Source: \[The Bike Shed*](https://www.thoughtbot.com/blog/the-bike-shed-episode-1-sandi-and-dereks-rules?ref=antoinebuteau.com)*\]*
6. **On breaking the rules:** "Rule 0: You can break any of these rules if you can convince your pair/reviewer why it's necessary." — [*Source: \[The Bike Shed*](https://www.thoughtbot.com/blog/the-bike-shed-episode-1-sandi-and-dereks-rules?ref=antoinebuteau.com)*\]*
7. **On applying constraints:** "The constraints are not laws, but forcing functions to ensure you consciously decide when complexity is actually warranted." — [*Source: \[Ruby Rogues*](https://devchat.tv/ruby-rogues/087-rr-book-club-practical-object-oriented-design-in-ruby-with-sandi-metz/?ref=antoinebuteau.com)*\]*
8. **On enforcing brevity:** "Keeping methods under five lines forces you to give names to intermediate concepts, making the code self-documenting." — [*Source: \[Ruby Rogues*](https://devchat.tv/ruby-rogues/087-rr-book-club-practical-object-oriented-design-in-ruby-with-sandi-metz/?ref=antoinebuteau.com)*\]*
9. **On hash parameters:** "Even when grouping parameters into a hash to bypass the parameter limit, the complexity remains and must be managed." — [*Source: \[The Bike Shed*](https://www.thoughtbot.com/blog/the-bike-shed-episode-1-sandi-and-dereks-rules?ref=antoinebuteau.com)*\]*

### Part 7: The TRUE Framework and Code Smells

1. **On transparency:** "The consequences of change should be obvious." — [*Source: \[Practical Object-Oriented Design in Ruby*](https://www.goodreads.com/book/show/13501725-practical-object-oriented-design-in-ruby?ref=antoinebuteau.com)*\]*
2. **On reasonableness:** "The cost of any change should be proportional to the benefits." — [*Source: \[Practical Object-Oriented Design in Ruby*](https://www.goodreads.com/book/show/13501725-practical-object-oriented-design-in-ruby?ref=antoinebuteau.com)*\]*
3. **On usability:** "Existing code should be usable in new and unexpected contexts." — [*Source: \[Practical Object-Oriented Design in Ruby*](https://www.goodreads.com/book/show/13501725-practical-object-oriented-design-in-ruby?ref=antoinebuteau.com)*\]*
4. **On exemplary code:** "The code itself should encourage those who change it to perpetuate these qualities." — [*Source: \[Practical Object-Oriented Design in Ruby*](https://www.goodreads.com/book/show/13501725-practical-object-oriented-design-in-ruby?ref=antoinebuteau.com)*\]*
5. **On evaluating abstractions:** "Concrete code is easy to understand but costly to extend. Abstract code may initially seem more obscure but, once understood, is far easier to change." — [*Source: \[Practical Object-Oriented Design in Ruby*](https://www.goodreads.com/book/show/13501725-practical-object-oriented-design-in-ruby?ref=antoinebuteau.com)*\]*
6. **On simple behavior:** "A class should do the smallest possible useful thing; it should have a single responsibility." — [*Source: \[Practical Object-Oriented Design in Ruby*](https://www.goodreads.com/book/show/13501725-practical-object-oriented-design-in-ruby?ref=antoinebuteau.com)*\]*
7. **On testing commands:** "Assert on the direct public side effect." — [*Source: \[RailsConf 2013*](https://www.youtube.com/watch?v=URSWYvyc42M&ref=antoinebuteau.com)*\]*
8. **On testing queries:** "Assert on the result." — [*Source: \[RailsConf 2013*](https://www.youtube.com/watch?v=URSWYvyc42M&ref=antoinebuteau.com)*\]*
9. **On code aesthetics:** "The goal is to write cost-effective, maintainable, and pleasing code." — [*Source: \[99 Bottles of OOP*](https://sandimetz.com/99bottles?ref=antoinebuteau.com)*\]*

### Part 8: Architecture and Mindset

1. **On the difficulty of mending:** "Making is easy, mending is a challenge." — [*Source: \[Maintainable.fm*](https://maintainable.fm/episodes/sandi-metz-making-is-easy-mending-is-a-challenge?ref=antoinebuteau.com)*\]*
2. **On mocking limitations:** "Only use mocks for outgoing commands (messages that have side effects in other objects)." — [*Source: \[RailsConf 2013*](https://www.youtube.com/watch?v=URSWYvyc42M&ref=antoinebuteau.com)*\]*
3. **On trusting collaborators:** "Don't test that an outgoing query works; that is the responsibility of the object receiving the message." — [*Source: \[RailsConf 2013*](https://www.youtube.com/watch?v=URSWYvyc42M&ref=antoinebuteau.com)*\]*
4. **On subjective code quality:** "Everybody complains about the weather but nobody does anything about it. Likewise, everyone has an opinion about what good code looks like, but those opinions don't help you create it." — [*Source: \[99 Bottles of OOP*](https://sandimetz.com/99bottles?ref=antoinebuteau.com)*\]*
5. **On the lifecycle of a codebase:** "The original abstraction creates a trap for future developers who feel honor-bound to use it, leading directly to unmaintainable technical debt." — [*Source: \[SandiMetz.com*](https://sandimetz.com/blog/2016/1/20/the-wrong-abstraction?ref=antoinebuteau.com)*\]*
6. **On DRYing lyrics in tests:** "DRYing out the lyrics in the test would force you to introduce an abstraction. Abstractions belong in code." — [*Source: \[99 Bottles of OOP*](https://sandimetz.com/99bottles?ref=antoinebuteau.com)*\]*
7. **On avoiding assumptions:** "If you don't know what's going to change, don't guess. Build what you need today and design it so it can change tomorrow." — [*Source: \[Practical Object-Oriented Design in Ruby*](https://www.goodreads.com/book/show/13501725-practical-object-oriented-design-in-ruby?ref=antoinebuteau.com)*\]*
8. **On developer empathy:** "Code is read many more times than it is written, so structure it for the human who comes next." — [*Source: \[Practical Object-Oriented Design in Ruby*](https://www.goodreads.com/book/show/13501725-practical-object-oriented-design-in-ruby?ref=antoinebuteau.com)*\]*
9. **On novice capabilities:** "Novice programmers don't yet have the skills to write simple code." — [*Source: \[Practical Object-Oriented Design in Ruby*](https://www.goodreads.com/book/show/13501725-practical-object-oriented-design-in-ruby?ref=antoinebuteau.com)*\]*