The Complete Guide to Writing More Maintainable, Manageable, Pleasing, and Powerful Ruby Applications Ruby’s widely admired ease of use has a Too many Ruby and Rails applications have been created without concern for their long-term maintenance or evolution. The Web is awash in Ruby code that is now virtually impossible to change or extend. This text helps you solve that problem by using powerful real-world object-oriented design techniques, which it thoroughly explains using simple and practical Ruby examples. Sandi Metz has distilled a lifetime of conversations and presentations about object-oriented design into a set of Ruby-focused practices for crafting manageable, extensible, and pleasing code. She shows you how to build new applications that can survive success and repair existing applications that have become impossible to change. Each technique is illustrated with extended examples, all downloadable from the companion Web site, . The first title to focus squarely on object-oriented Ruby application design, Practical Object-Oriented Design in Ruby will guide you to superior outcomes, whatever your previous Ruby experience. Novice Ruby programmers will find specific rules to live by; intermediate Ruby programmers will find valuable principles they can flexibly interpret and apply; and advanced Ruby programmers will find a common language they can use to lead development and guide their colleagues. This guide will help you
Sandi Metz is a programmer, teacher, author, and sometime consultant. In the past 30+ years she has written innumerable applications and creates practical solutions that produce working software that is easy to change. She has spoken about object-oriented design and refactoring at international Ruby conferences since 2009.
Full disclosure: I helped contribute early reviews of much of the content of this book.
Sandi does a really great job, but not for the reason you'd expect: POODR is more pragmatic than one would expect. Especially in the Rails world, where OO design is looked down upon as 'academic' and 'not actually worthwhile,' one would expect all the usual explanations and reasonings.
But not this book. Sandi does a great job of explaining the tensions between all of these different maxims, and acknowledges that software is always in flux; in many ways, it's more of a process than a product. Some techniques are useful at different points in that process, and sometimes, business constraints don't allow for constructing perfect software, if such a thing were even possible.
I'm still slightly concerned with the primary metaphor for much of the design: a bicycle. Too many people think of objects as only being concrete 'things' that you can touch or feel, and while the bicycle metaphor works well, I'm hoping that it doesn't further encourage this style of thinking. This is, again, a Rails concern, and POODR doesn't really concern itself with Rails. But in Rails apps, there's a real problem with monolithic models, and while POODR _should_ help, I'm not sure it goes far enough in that regard.
But seriously, that's the only complaint that I have about it, it is a truly excellent book.
While this book starts out being excellent, it just turns out as yet another of those "static typing is wrong because type casts are dangerous and you need to write type annotations everywhere". Then in the last chapter it basically re-implements a type system via tests, by checking if a class has certain methods.
That, dear reader, is exactly what static typing is for, and in 2014, we do have type inference even in C++.
This book is an excellent book on good OO design. I would recommend it to anyone. But I would not recommend it to anyone without a large warning sign stuck to it:
"Warning: The portions about statically vs dynamically types languages show a very marked lack of balance, objectivity and depth of knowledge. Safest to simply not believe anything said about statically typed languages in this book. Most of it is not exactly wrong, but still very misleading."
Sandi: Should you happen to read this I would strongly recommend removing the bash-static-typing section in the next edition, or working very hard to make it balanced. Your book is truly not enhanced by a semi-informed attack on static typing. And it is otherwise such a good book! Had the book not even mentioned static typing it would have gotten 5 stars.
Examples:
"the huge gains in efficiency provided by removing the compile/make cycle. This trade is a bargain. Take it"
Competent programmers do not sit around waiting for the compiler and running tests manually. They use tools that do this automatically in the background for them. Within a second or two of two of changing code the tools will have detected the change, compiled the code, executed the tests, and notified you if you broke something..
See for instance ;
There is no huge gain to be found here. Maybe there was when all statically typed languages took long to compile and these tools did not exist. That was last century!
"Any language that allows casting a variable to a new type is vulnerable"
Casting is highly unusual in code written by competent programmers. Casting is a code stink. This is an argument along the lines of "Red lights are useless, you can just drive right past!"
"Programmers find the code easier to understand when it does not contain type declarations; they can infer an object’s type from its context."
Then why do you spend 90% of the testing trying to add types back through tests? Why do you explicitly say that you do this to make the types visible? Why do you talk time and time and time again about how to make the implicit types visible so that it is possible to understand the code?
"the belief that static typing is fundamentally preferable often persists because it is self-reinforcing. Programmers who fear dynamic typing tend to check the classes of objects in their code"
Manual type checks are not common in code written by competent programmers. It is another code stink. Just like in a dynamic language.
"The notion that static typing provides safety, comforting though it may be, is an illusion"
Then tell me again why the book spends 90% of the testing effort manually building a fragile type system without once mentioning how this is not needed with static typing. It's almost all "responds_to_size", "responds_to_width" This is just type checking. Something that a static language compiler does for you.
"Metaprogramming, used wisely, has great value; ease of metaprogramming is a strong argument in favor of dynamic typing".
Meta-programming might be somewhat harder in static languages. But our whole code base would be impossible without it. See tools such as NHibernate, Entity Framework, NServiceBus. There are a plethora of libraries and frameworks, written in static languages, out there that are based on meta programming.
"Duck typing is built on dynamic typing; to use duck typing you must embrace this dynamism." .. "Duck typing detaches these public interfaces from specific classes, creating virtual types that are defined by what they do instead of by who they are."
Exactly what you get in a static language by simply extracting an interface and implementing it. This add up to a grand total of one more word in the method that uses the interface, one word per type that implements it, and one line per method/message in the interface declaration.
"Duck typing reveals underlying abstractions that might otherwise be invisible. Depending on these abstractions reduces risk and increases flexibility, making your application cheaper to maintain and easier to change."
And when these abstractions get names in the code they actually become visible, without having to hunt for their meaning in tests somewhere else. You can see it right there in the code. And you do not need to spend 90% of your testing type checking these types.
"Creating code that fails with reasonable error messages takes minor effort in the present but provides value forever. Each error message is a small thing, but small things accumulate to produce big effects and it is this attention to detail that marks you as a serious programmer. Always document template method requirements by implementing matching methods that raise useful error"
But using language that guarantees this out of the box is useless, because such errors dont occur in reality. was that not what you said?
"There is a level of design abstraction where it is almost impossible to safely make any change unless the code has tests ... Tests are your record of the interface of every abstraction and as such they are the wall at your back. They let you put off design decisions and create abstractions to any useful depth"
With static languages the actual abstractions have concrete representations in the code. You do not need to go searching in tests to see the abstractions and the design.
Given a well designed code base I would say that a highly abstracted code base is far easier to refactor safely without tests than a code base with few abstractions. This of course goes for static languages with the excellent tool support that they afford.
* Tools support
Not once does the book mention what might be the greatest advantage of all to static languages. The excellent tools support that the ability to do static analysis affords.
Tools that do Refactoring Code navigation Metrics UML Diagrams Architecture diagrams Type checking
It is impossible(even theoretically) to make such tools, that are truly reliable/accurate, for dynamically typed languages.
In my opinion the greatest of these tools are the refactoring tools. I can very quickly completely transform the structure of large portions of code in a large code base in C# with 99% assurance that it will all work exactly the same afterwards. In seconds I rename a class/method/interface. Another few seconds and I move half the files in one folder/namespace to another one where they fit better. Another few seconds and I extract a new interface for use as a "duck".
All of that took about 1 minute of actually changing code. Obviously the thinking part is apt to take more, but doing is almost instant. And SAFE. I do this all the time in C# and it just works!
I've done these types of refactorings in dynamic languages. Even in well written code it is likely that such refactorings are so hard to perform that I will just not do it. We would probably be talking, at least, an hour for each step. More likely hours or days. If we did not have very near 100% test coverage I would never dare to attempt it. How often do you see 100% code coverage?
The list does go on and on but I'm tired now and I hardly think I need more examples to make the point that the position this book takes on this issue is partisan and/or uninformed. I'll stop here.
This book is spot on when it comes to object orientation. It contains many of the lessons I had to learn the hard way during the last 8 years while extensively studying books & blogs on oo. Though POODR certainly doesn't come up with a lot of new ideas (at least I've heard most of them before), it compiles the ideas into a wonderful whole, where each part fits nicely with the rest of the book. Although definitely opinionated, the book shines by never being dogmatic and clearly explaining the decision process and tradeoffs behind every advice Sandi gives.
If there's one thing I would definitely take away from this book, it's from the last part on testing. Sandi describes a technique there on how to make a test code base that extensively relies mocks & stubs much more trustworthy, by adding interface related tests on both sides to ensure contract adherence. It's a technique I've heard only a few times about (the last time in 2009 by JB Rainsberger ), but never seen actually anyone describing in detail in code. Although I've applied parts of the idea, like ensuring interface adherence for ducks and hierarchies, I often missed proper tests for the stubs used in the consumer related specs. I will definitely try to use this technique more in the future.
Some minor points of critique: For one thing, one of the usual things to criticize for this kind of book (I guess): It doesn't really show end to end code with persistence and presentation added to the mix, which definitely is a place where novices and even often intermediary designers hit a wall applying those principles. The other thing that I missed (and maybe this goes hand in hand with the first one), is the notion that one single model might not be the best choice for all aspects of an applications and sometimes you have to weigh if and when to insert model borders (E. Evans famous Bounded Contexts come in mind here).
That being said I really enjoyed the book and I will definitely recommend it to colleagues.
Great book about object-oriented design, tackling the main principles of OO. Very well written, it explains how to manage dependencies, how to implement duck types and how to use inheritance and composition. It finishes with a great chapter about testing. I do recommend!
(ps.: if you're a beginner with Ruby - like me - and you want to learn more about the language features FIRST, you should pick another book. This one is focused in OOD!)
An object has a dependancy when it knows: - 1) The name of another class. - 2) The name of a message that it intends to send to someone other than self. - 3) The argument that a message requires. - 4) The order of those arguments.
The book is full of valuable insights and good, elaborate explanations. Well worth the read.
The only thing I didn't like are code examples with real-world objects like Bike, Gear and Mechanic. You can write two or three good examples that way, but trying to adhere to it throughout the whole book make these examples worse and worse. The further into the book, the worse the code is.
Having been a Ruby programmer full-time for a year now, this book finally made "click" many of the Best Practices I've seen and used in code but haven't really been able to articulate. It got a little long-winded and redundant at some points, probably because it's geared more towards people who haven't been exposed much to OO, but overall it was definitely worth reading.
These are some of the parts that were most valuable to me:
"Sometimes the value of having the feature right now is so great that it outweighs any future increase in costs."
"Design is more the art of preserving changeability that it is the act of achieving perfection."
On designing a class: "If the simplest description you can devise uses the word 'and,' the class likely has more than one responsibility. If it uses the word 'or,' then the class has more than one responsibility and they aren't even very related."
"When faced with an imperfect and muddled class ... ask yourself: 'What is the future cost of doing nothing today?'"
"If you can control the input, pass in a useful object, but if you are compelled to take a messy structure, hide the mess even from yourself."
On classes with too many dependencies: "Because they increase the chance that [the class] will be forced to change, these dependencies turn minor code tweaks into major undertakings where small changes cascade through the application, forcing many changes." "depend on things that change less often than you do."
"Classes control what's in your source code repository; messages reflect the living, animated application."
"public methods should read like a description of responsibilities."
"Interfaces evolve and to do so they must first be born. It is important that a well-defined interface exist than it be perfect."
"Even if the original author did not define a [good] public interface it is not too late to create one for yourself."
"The general rule for refactoring into a new inheritance hierarchy is to arrange code so that you can promote abstractions rather than demote concretions."
"Identifying the correct abstraction is easiest if you have access to at least three existing concrete classes."
"Because `extend` adds the module's behavior directly to an object, extending a class with a module creates class methods in that class and extending an instance of a class with a module creates instance methods in that instance."
"Aggregation is exactly like composition except that the contained object has an independent life [outside of the has-a relationship]."
"If you cannot explicitly defend inheritance as a better solution, use composition.
"Inheritance is best suited to adding functionality to existing classes when you will use most of the old code and add relatively small amounts of new code."
"Tests provide the only reliable documentation of design. The story they tell remains true long after paper documents become obsolete and human memory fails. Write your tests as if you expect your future self to have amnesia."
"Costly tests do not necessarily mean that the application is poorly designed. It is quite technically possible to write bad tests for well-designed code... The best way to [design good tests] is to write loosely coupled tests about only the things that matter."
"It is an unfortunate truth that the most complex code is usually written by the least qualified person... Novice programmers don't yet have the skills to write simple code."
"Do not test an incoming message that has no dependents; delete it; Your application is improved by ruthlessly eliminating code that is not actively being used. Such code is negative cash flow, it adds testing and maintenance burdens but provides no value. Deleting unused code saves money right now, if you do not do so you must test it."
If using an object is cheap, injecting an actual one instead of a double into a test is fine.
"An object with many private methods exudes the design smell of having too many responsibilities. If your object has so many private methods that you dare not leave them untested, consider extracting the methods into a new object."
"When you treat test doubles as you would any other role player and test them to prove their correctness, you avoid test brittleness and can stub without fear of consequence."
"On testing abstract base classes: "If you leverage Liskov and create new subclasses that are used exclusively for testing, consider requiring these subclasses to pass your subclass responsibility test to ensure they don't accidentally become obsolete."
This book helped tie together my understanding of OOP best practices in Ruby and has produced immediate benefits in the quality of code I'm writing. It contains great examples of refactoring code, along with checklists, red flags and questions to ask yourself throughout the design process.
The author does a great job of following the development and improvement of a sample app throughout the book (an app for a bike shop). The continuous narrative helps you see how the pieces fit together, though a few examples from other domains would have been helpful in some places. At the same time, that's really an exercise for the reader.
I found Chapter 4's discussion of creating a public interface particularly helpful in determining the proper responsibilities of objects by "Asking for 'What' Instead of Telling 'How'." The continued discussion of messages when identifying duck types in Chapter 5 helped reinforce the concepts. The chapters on modules and testing also helped tighten up my thinking on a few topics.
As I've shifted from hobbyist to freelancer to full time developer over the past few years, there have been a few books or tutorials that have really helped me improve (along with bugging friends and countless hours of troubleshooting), and this is definitely one of them. Highly recommended.
In my humble opinion, this book's greatest strength lies in its advice to think first not of objects themselves and their responsibilities, but as actors and messages passed between them. Perhaps you already knew this; I didn't, and doing so has been a tremendous new tool in my arsenal to tackle problems. Seeing and defining public interfaces and abstractions has become a lot easier. It's become so much clearer now, it's like the idea just popped into my head.
Chapter 4: Creating Flexible Interfaces has been a joy. While reading it, I couldn't drop the book for the life of me. It just clicked at the time: this is what I'm reading this book for.
Why the 3-star? I'm gonna share another humble reader's opinion here: While I know this is a Ruby book, I guess it could have done without the obvious dynamic/weak typing bias, only to spend half the book later providing tips to prevent problems inherent in the typing system.
Starting with a story about technical debt, sandi metz lead us step by step through obstacles we have to face when designing a program. This book shows us when to use different kinds of designing techniques such as :inherit, duck-typing, composition. At the last chapter, Sandi tells us a fresh view about testing, with concepts of inbound message, outbound message. I highly recommend this book for any ruby-ist who want to write code better.
cuốn hay th� 2, sau Structure and Interpretation of Computer Programs, hay hơn c� mấy cuốn kinh điển của Martin Flower, Uncle Bob, GoF... cực kì liên quan tới những gì đã trải qua, đọc xong áp dụng vô ngay và thấy được kết qu� ch� không phải lăn tăn quá nhiều khi áp dụng architect, design pattern.
Dùng t� ng� đơn giản, không c� ch� ra mấy cải jargon v� vẩn làm màu.
If you wan't to learn basic of object oriented design and don't have much experience in the field - then the book is right for you. It present's good basic ideas to follow to improve your OO design and provides simple examples that are understandable to everyone.
If you want information beyond the basic topics, then this book about that. Ideas presented in the book are good, but the examples are not so much. Examples are very well chosen to fit the narrative of the book and are far from reality. I.e. example in chapter 8 works very well to present composition why composition is better alternative in that situation, but in reality, if sub-types were not mere data-bags, the whole example wouldn't have worked.
Discussion about application layer how to structure it, which is IMO the most interesting part about OO design, is not provided.
Granted that this is the very first book about software development I've ever finished, this is the best I've ever read. It was clear and understandable to me as a beginner (with some exceptions) but also comprehensive and fairly complex. The writing was excellent and persuasive about the appeal of writing well-organized code. The first few chapters were slightly more clear and persuasive than the later chapters, which veer away from general principles into the weeds of specific techniques, but the frequent specific examples and step-by-step explanations of them kept it all concrete and understandable. If you're looking for a book on Object-Oriented Programming (OOP) and intend to program in Ruby, I'd recommend it; but I can't speak to how it compares to other books that use other languages or frameworks for its examples.
An accessible, concise guide to OOP. Sandi Metz is best known for her talks and writing about Ruby, but the principles here are applicable to any language, really.
This is a good read and OOP Ruby reference to have. It presents many important principles to aid in software design. I appreciate the book's principles to analyzing tradeoffs and decision-making in general. Conceptualizing object-orientation as message passing is refreshing and can be quite an eye-opening gem (heh).
However, the merits of dynamic typing (e.g. implicit dependencies on objects accepting a type of message using duck typing) tend to be more useful for accelerating small-to-medium projects yet in my experience fall apart for larger, enterprise codebases. I found some of the chapters' code examples a bit too short or contrived to be fully convinced by their fitness for purpose & future in code that's meant to run at scale. For example, it'd be nice to see more examples of composition vs. inheritance using classes that are likely to come up in the real world rather than physical objects like Gear, Wheel, Bicycle, etc. to round out my understanding.
Ruby has static typesystems / type checkers nowadays which can help catch software defects and smoothly guide developers to writing correct code that would otherwise need to be validated when running the code, e.g. requiring many metaprogramming unit test cases.
Overall, I'd still heartily recommend this book, especially since admittedly, the code style and approaches presented are consistent with standard Ruby / Pythonic (minus type hinting) style and lots of fans of those languages would definitely vouch for them.
I loved it. The thing about Sandi Metz is she has that wonderful mix of good communication and years of experience. This book is a great demonstration of the power of that combination. It's got a laid back style that makes it feel like you and Sandi are pairing on the code under examination. As Sandi's explaining to you why she's thinking what she's thinking you spend the length of the book saying "yes, I hand't thought about it like that but now it's obvious". There aren't many programming books like that and reading this will make you want to bin a drier and less accessible treatments of OO thinking. Even better a number of the principles explained here are language agnostic, your Python is better Python after reading this.
I don't have anything bad to say about this book, which I think is a first for me. It's just that good.
1) I love this book. There are parts I could nitpick (like a lot of reviewers, I strongly disagree with author on static typing; dynamic typing is definitely my least favourite part about Ruby) but I've gained so much from this book that I can overlook the small bits I didn't like.
2) A lot of books of this type tend spread the content pretty thin and have a lot of hype and used car salesman tricks and negging. This book has a brief introductory section, and then she is 100% content right until the end.
3) This book improves my code at lot every time I read it. I spent the last month or so paging through it and doing hours of joyful refactoring. Nothing in the book is exactly revolutionary, but she puts together a lot of little good observations and gives her reasoning in a convincing manner. I wish all books could have such a positive effect on my actions and my attitude.
Là một lập trình viên Ruby cũng được 4 năm rồi, mà mãi gi� này, mình mới có kiên nhẫn đọc và đọc hết quyển Practical Object Oriented Design in Ruby (2nd edition), dù đã mua quyển này t� khá lâu. Cảm nhận của mình, quyển này đi rất chi tiết (nhiều khi cảm thấy Sandi viết khá dài dòng, đây có l� là điểm tr� duy nhất). Dù quyển sách có tên Practical Object Oriented Design in Ruby, nhưng sách ch� s� dụng ngôn ng� Ruby đ� làm code mẫu minh họa, nếu bạn đang làm việc với một ngôn ng� lập trình hướng đối tượng, hoặc muốn tìm hiểu v� nó, và có thôi thúc muốn viết code tốt, một chương trình đẹp, d� thay đổi, thì mình khuyên bạn nên đọc.
This is a good book for learning about object oriented design using the Ruby programming language. From a pragmatic point of view, it could be better with some exercises; but from a conceptual one it's great. I'll be referencing this book a lot every time I'm working in a new app till I get everything in it right. Even if the design of good unit tests can be a book on itself, Sandi manage to provide enough information to start creating better tests on your Ruby applications. A must read.
Excellent read, no matter how much experience you have or if you use Ruby or not.
I was a bit skeptical at first, 'cause I thought the book was going to tell me things that I already knew. Boy was I wrong! I already feel that I've learn a lot and can't wait to apply all this new knowledge on my next and existing projects.
I think it's definitely a must have in your library as a reference. Very well written, simple yet very understandable examples, and lots of insights.
I read this book in a time where functional programming is gaining a lot of traction and it was both refreshing and reassuring to read it.
This book approaches OOP with grace and simplicity, showing the strength of Ruby and OOP in small but concrete examples. As much experience I have on the field, this book taught me a lot and I think everyone who takes programming seriously and loves Ruby must read it. I'll definitely read it again a couple of times.
Well-crafted analogies that really drive the point home. The last section on testing is gold. There's a 30 min talk based on it (by the author) that is available for free:
Would've rated 5 stars if it wasn't for the unnecessary dissing of statically typed languages.
I am going to make this book compulsory reading for every Ruby developer I work with from here on out, and if I need to compel them too hard, I probably won't stay working with them for much longer.