Search This Blog

Tech Book Face Off: Confident Ruby Vs. Metaprogramming Ruby 2

I'm always on the lookout for books that will help me improve my programming skills by learning to write cleaner, clearer code. For this Tech Book Face Off I found two relatively short books on how to write better programs in Ruby. The first book is Confident Ruby: 32 Patterns for Joyful Coding by Avdi Grimm, and I paired it up with Metaprogramming Ruby 2: Program Like the Ruby Pros by Paolo Perrotta. I haven't been programming in Ruby too much lately, but I still love the language and think it's a great vehicle for exploring how to write beautiful code. Even if I don't use the things I learn from these books directly in my day-to-day work, plenty of the ideas behind good programming patterns will transfer to any language. The key is to recognize the rationale that makes a particular coding style work well—what are the underlying principles that make it better than the rudimentary way that code is normally written—and adapt that style to the language that you're using. So let's see what these books have to offer, whether you're a Rubyist or not.

Confident Ruby front coverVS.Metaprogramming Ruby 2 front cover

Confident Ruby

This is one of those special books that strikes the perfect balance between fun and informative. It was an immensely enjoyable read. Avdi Grimm has an excellent conversational style to his writing that made the material in the book so easy to digest, while giving great insights into how to program in Ruby with determination and purpose. It is a book primarily about patterns, but not so much patterns for architecting object-oriented programs as it is about patterns for how to write clear, understandable methods. They're more like micro-patterns for writing good code, or as in the author's words:
In this book, we'll take a look at many examples of the kind of code that unnecessarily obscures the storyline of a method. We'll also explore a number of techniques for minimizing distractions and writing methods that straightforwardly convey their intent.
The book starts out with some introductory explanations of what we're aiming for when writing confident code, and then gets into the patterns for accomplishing those goals. Each pattern is nicely contained with clear explanations and example code that really makes sense within the context of the pattern. The end of the book completes the picture with some extended examples of how to refactor parts of some open source projects to make the code more confident.

At just under 250 pages, and because it is so well-written, the book is a quick read, yet it is packed full of useful wisdom on how to write better methods. Much of this wisdom is just as applicable to other languages, such as this advice about returning nil:
nil is the worst possible representation of a failure: it carries no meaning but can still break things. An exception is more meaningful, but some failure cases aren't really exceptional. When a return value is used but non-essential, a workable but semantically blank object—such as an empty string—may be the most appropriate result.
This type of recommendation could apply to almost any language, and the reason why you should do things a different way becomes obvious when it's broken down like this. Often, the reasoning behind writing code a certain way is the most important catalyst for becoming a better programmer. Once it becomes clear that code written with stronger intent becomes easier to understand and debug, and why it becomes easier to understand and debug, then you have much more motivation to write code that way. Maybe that means you actually will start writing code that way. The book wraps up with more great insight on this idea:
No one sets out to write timid, incoherent code. It arises organically out of dozens of small decisions, decisions we make quickly while trying to make tests pass and deliver functionality. Often we make these decisions because there is no immediately obvious alternative. Over time, the provisions for uncertainty begin to overshadow the original intent of the code, and reading through a method we wrote a week ago starts to feel like trekking through thick underbrush.
With the patterns covered in this book, we now have the tools to clear away that thick underbrush and see the path to cleaner, more confident code. Even better, those tools were learned through an enjoyable, approachable book. I highly recommend checking it out.

Metaprogramming Ruby 2

After the experience with the last book, I had high hopes for Metaprogramming Ruby 2, being another concise book about Ruby patterns, this time focusing on one of Ruby's main features: metaprogramming. Alas, my hopes were not to be satisfied, and I found this book somewhat frustrating.

Paolo Parrotta attempts to put the reader into a setting of a pair-programming work environment with a more senior programmer called Bill. It was a nice try for spicing up what could potentially be a dry listing of metaprogramming patterns, but the narrative around Bill and the reader ends up feeling rather hokey and strained.

On top of that, the patterns are not called patterns; they're called spells. That choice may have worked if the author had committed more fully to the theme and put the reader into a fantasy environment where they had to come up with the right metaprogramming spells to succeed in making more flexible code for an adventure game. I don't know, something like that. It would have taken a lot more work, and it may still have been difficult to pull off well, but having patterns called spells for no other reason than to not call them patterns just didn't work for me. It ended up feeling more awkward and disjointed than anything:
If you buy into this notion—that a Ruby class definition is actually regular code that runs—you'll be able to cast some powerful spells. Two such spells that you'll learn about in this chapter are Class Macros (Class Macro) (methods that modify classes) and Around Aliases (Around Alias) (methods that wrap additional code around other methods).
Meh, it just seems unnatural and forced. Another issue arose with the code examples. Most of the examples presented with the explanations of the patterns were silly and useless. It felt like hardly any thought was put into them as long as the code demonstrated the mechanical aspect of the metaprogramming feature under discussion. More useful examples would be more difficult to come up with, to be sure, but that care in constructing examples makes the difference between a decent book and a great book. The more fleshed-out example code within the pair-programming narrative were better in this respect, but then the contrast between that code and the smaller random examples in the explanations was all the more jarring.

One last issue I had with the book was that Parrotta could sometimes be a little condescending and patronizing:
(Be aware that the next few pages contain advanced material that might take a while for you to digest. If you want, you can skip straight to Method Wrappers, on your first read through and come back to this section later.)
Maybe he was just trying to be helpful, but it didn't come off that way. These kinds of comments in a book that's supposed to be geared more towards experienced programmers seem out of place. It's fine for the author to say that they found it difficult, but leave the reader's assessment up to themselves. A good teacher will find a way to explain complex material so that it is easy to understand, not effectively say, "Well, it's not surprising that you don't understand this, yet. Just come back to it later, and you might get it then." I didn't have a problem understanding the patterns, but I did have a problem with this repetitive belittling in certain sections of the book.

Despite these criticisms, the book was not all bad. If you can look past the muddled narrative and other shortcomings, the actual patterns are fairly well described and useful to learn. The last section of the book on extended examples of metaprogramming in Ruby on Rails was quite well done and an interesting read. The lessons highlighted from studying how metaprogramming is done in Rails were especially thought-provoking, such as this one:
Here is the most important guideline I learned from Active Record's design: design techniques are relative, and they depend on the language you're using. In Ruby, you use idioms that are different from those of other languages you might be used to. It's not that the good design rules of old suddenly grew obsolete. On the contrary, the basic tenets of design (decoupling, simplicity, no duplication) hold true in Ruby as much as they do in any other language. In Ruby, though, the techniques you wield to achieve those design goals can be surprisingly different.
It's certainly true that languages each have their own styles and idioms, but this idea extends it to the actual program design techniques being language dependent. That's certainly true to some extent, as the same program can have significantly different structures and implementations in different languages. However, I also think design techniques are transferable from one language to another. Obviously you can't do metaprogramming in a language that doesn't support it, but learning that certain techniques are common and elegant in one language can transfer over to other languages. Developing new techniques that work well in Ruby will influence what you try in other languages, and that cross-pollination is extremely beneficial.

One final great piece of advice that applies to learning any new programming feature is:
[R]esist the temptation to be too clever in your code. Ask yourself whether there is a simpler way to reach your goal than metaprogramming. If the answer is no, then go forth and metaprogram the heck out of your problem. In many cases, however, you'll find that a more straightforward OOP approach does the job just as well.
Any cool, powerful feature taken to an extreme will make an incomprehensible mess of a program. Part of gaining proficiency in a language is knowing when not to use its most powerful features, and using the simple fundamentals of the language well.

Taking into account the rough spots of the book with the awkward setting, random examples, and irritating forewarnings, as well as the positives of decent pattern descriptions and a strong few chapters at the end, I can't say I'd recommend this book. Nearly all of the material is available in other books with better presentation, like Eloquent Ruby. This one is not worth the frustration, so skip it and enjoy Confident Ruby instead.

No comments:

Post a Comment