Let’s Do Some Engineering Pt. 1: Design Patterns
View part 2 of this series: Software Metrics.
We Rubyists like to call ourselves engineers. For the most part, there are a lot of great engineering practices being applied in the realm of testing. Continuous integration, acceptance testing and other automated testing are some pretty advanced techniques that are not so well-practiced in other communities. We should all be proud of ourselves in this respect.
But you can’t just do testing right and call yourself an all-around engineer. There are still plenty of engineering concepts that Rubyists ignore or completely reject but can prove pretty useful for a well structured project. Techniques like the use of design patterns, metrics, traceability and estimation are all important but seem to be completely ignored by most Rubyists I know. I’m going to cover these all in parts over the next few weeks starting with design patterns, because it’s the easiest to cover.
Note that I can’t teach you patterns. You probably know most of them anyway. If not, please visit Wikipedia or buy Design Patterns. I just want to point out that you should pay more attention to them. And here’s why:
Denial is a River
The issue here is that although many Rubyists make use of design patterns every day, some of them (who shall remain unnamed) refuse to accept that they are actually making use of patterns, citing that the names of these patterns imply a complexity that is not actually being expressed. The "complexity" here usually comes from the fact that these patterns are mostly applied in static languages (like Java) where design patterns are occasionally more difficult to implement due to all the extra moving parts, compilation and explicit typing. None of this makes these patterns any less effective or any more restricted to Java (or C++, or …). Patterns are still patterns whether you acknowledge them or not, and they are not defined by the LOC or number of classes they require in language X. I mean, come on, Rails used to have "MVC" and "ActiveRecord" plastered on the website to sell its new web development technique, two patterns ripped right out of P of EAA. Now MVC is a household name. Rails is the ultimate testament that a pattern can be simple to implement in a simple language. Refusing to admit that patterns are used hurts your development team and the community, because patterns (simply identifying them) serve a very important purpose.
Why Patterns Matter
Pat Maddox brought this up at his MWRC2010 talk a few weeks ago. I forget his exact wording, but the paraphrasing goes a little something like: "patterns convey meaning". This is entirely true. I think it was Pat who also brought up the idea of metaphors in Agile development at a MWRC lunch. Metaphors in Agile are the idea that the team decides on a metaphor that expresses some high level concept of the project, and they use this metaphor exclusively to convey this concept. This saves time because usually these metaphors are one or two words. It also keeps everyone on the same page, because they all know (or can look up) what the metaphor really means. The differences between two metaphors can be subtle but have large impact on the functionality of a system. For example, consider the metaphors "email" and "instant messaging". Although emails can be short and instant and instant messages can be long and very email-like, we already know (mostly from experience) that the functionality of these two systems involve some significantly different user interactions. If your client wanted "email" and got "instant messaging", they would notice the difference and probably complain.
Well, design patterns are just like these metaphors, but they are meant for code, not UI. Just like metaphors, they convey a very specific meaning in one or two words. This means they save everyone time. They can also be looked up, because there is a pretty good set of standard design patterns (GoF, Fowler) that people can look up and immediately understand. These patterns can have very subtle differences depending on the terminology used. For instance, consider the factory pattern versus the builder pattern. Both are concerned with creating objects, but a builder is also concerned with the assembling of the object. This is a relatively subtle but pretty noticeable difference. Telling someone you are using a "builder pattern" should immediately ring a bell in their heads saying, "oh, it’s not just a factory".
These patterns also come with another interesting benefit: authority. We know these patterns work, because they’ve been running in production code for decades. There is no argument about whether the factory pattern works, because it does. We know this. When someone proposes a design and tells you "I’m going to use X because it’s worked in hundreds of previous projects that have had this same problem," that’s real engineering.
A Tiny Case Study
But back to the topic of saving time: just the other week James Golick blogged about how he writes Rails apps. Now, James is a really smart guy, and he’s way ahead of the curve when it comes to good engineering. But really, his whole blog post could have been replaced with the statement: "Here’s how I do Rails apps: I don’t create domain objects directly in my controller. Instead I create all of my domain objects through the Command pattern. <example here>" End of blog post.
Now of course this is an exaggeration, there’s some explanation of how this relates to his tests, coupling and logging. But a lot of this discussion and assertion that it’s smart is also unnecessary when we describe the design through the command pattern. We know what a command pattern does, what problems it solves, and why it is used. We know that it’s meant to solve coupling issues, help implement SRP properly and comes with the implicit benefit of composability and dependency injection. More importantly, we know there’s nothing heretical about his methods at all; he’s using an authoritative design used by many others in this situation. No justification necessary, James.
Some to Get You Started
Now I know I said I wouldn’t teach you patterns, but here are a few you should definitely look into if you’re developing any kind of client-server architecture these days:
If you’ve never heard of any of these, look them up now. I use most of these daily, and you should be using them as well (you probably already are). Being aware of their existence can help you rethink some of your designs and find ways to make them much simpler by applying design patterns that have worked many times before. More importantly, when you review your current architecture (as any good engineer periodically does), having the ability to associate your design with existing, working, authoritative patterns should certainly give you some confidence in your system.