As promised, here's the first installment of my Fractal Design series. I'd like to kick things off with what might seem to be an odd choice for Rubyists: the interface segregation principle.
A Brief Introduction to the ISP
Interface Segregation is one of the SOLID design principles, and states:
Clients should not be forced to depend upon interfaces that they do not use. (source)
The idea is that when a class implements an interface, it should only get methods that it needs. When followed, you get higher cohesion and less coupling in your code.
In Ruby, we don't have explicit interface constructs -- but we do have plenty of violations of this principle.
Utility Modules
At RailsConf a few months ago, DHH justified the asset pipeline by referring to the javascripts and stylesheets folders in Rails as junk drawers, full of bric-a-brac and this-and-that. Modules make a much better target for that description, though.
I've seen (and/or written) any number of medium- or larger-sized Ruby applications where there's a utility module, filled with all sorts of methods used here or there in the application. Such modules are created with the best of intentions -- people just want to stay DRY, after all, so they extract methods that appear in multiple classes or views, but they end up dropping those into an ever-expanding, unfocused module that then has to be included all over the place even when only a fraction of it is used in any particular context.
Sure, many classes that include Toolbox might use the log method -- but how many of them need to format credit card numbers, or select a random element from an array? Here's a better approach:
Granted, there's more code now, but this ISP-following refactoring allows us to include just the pieces of Toolbox that we need where we need them. The result is a cleaner, more comprehensible, and more testable system.
ActiveSupport
For a very long while, the biggest ISP offender in Ruby was ActiveSupport. It was the utility module pattern writ large -- including it anywhere brought in a huge amount of code, the vast majority of which was completely irrelevant to the reason you included it somewhere.
In Rails 3, ActiveSupport was refactored and modularized so that you could include just the pieces you need as you needed them:
And millions of gems, non-Rails applications, and more breathed a sigh of relief.
Rails Helpers
Sticking with Rails, there's one violation of ISP still present in the current releases by default: automatic controller-based helper generation:
I can count on one hand the number of controllers I've seen that use the same helper methods across even a majority of their actions, much less all of them. It's a clear violation -- controller-scoped helpers are automatically included in all of that controller's views, regardless of whether they are needed or not.
Luckily, this is easy to fix. In your config/application.rb, add a config.generators block:
And voila, the offending helpers are no longer created:
Of course, many of you have probably been ignoring the auto-generated helpers for years, creating smaller, more focused, more ISP-compliant ones the whole time. To you, I say: Good show!
JavaScript and CSS
In my introductory post, I promised that we'd be looking at these principles at various levels of code. In the above examples, we've mainly stuck to the class and library levels (or to reasonable facsimiles thereof) of complexity -- but there's another spectrum on which we can look at code. In addition to the server-side examples we've already seen, we can get significant violations of the ISP on the client-side, with JavaScript and CSS.
Go to your app and take a look at the JavaScript and CSS you're serving up on each page. Chances are, there's a ton of stuff in there that isn't relevant to any given page -- even if you're using the new Rails asset pipeline, asset_packager, or something similar, it takes a tremendous and constant effort to make sure you're only sending what you need down the pipe at any given time. I've seen thousands of lines of JavaScript and CSS come down on pages that don't use any of it, and I'm sure you have, too.
Following the ISP is inherently harder when you get code from a third-party. As much work as has been done to keep JavaScript libraries like jQuery slim, there's still always going to be code in there that you don't need. jQuery UI does a nice job of providing a custom script generator, allowing you to choose only the effects that you want -- but that probably isn't a universal solution.
With any design principle, there's a tradeoff. With the ISP, it's between convenience and precision. It may be absolutely more correct to have a unique, customized JavaScript and CSS load for each page on your site, but the organizational work to reach that goal -- and the cognitive overhead of figuring out which features are shared between multiple pages and unique to individual ones, so that you can find the right file to edit -- could easily become overwhelming.
Libraries and Applications
When you move from classes and modules to the larger scales of libraries and applications, the ISP doesn't seem to map that closely to the code -- we move from discussing implementing interfaces to providing and consuming them. Nevertheless, I think there's still some good to come out of thinking this way.
And ISP-friendly gem, for instance, should provide a single chunk of related functionality, without a ton of extraneous stuff thrown in (hey, old ActiveSupport -- there you are again!). An ISP-friendly system of applications would pass around APIs where each application did something relatively specific... but at a certain point this approach transitions from being an example of interface segregation to illustrating the Single Responsibility Principle (which we'll be getting to soon enough).
I'm on my way back home after a great Ruby Hoedown in Nashville, where I gave a talk called "Fractal Design." This is the second(ish) time I've given this talk, having previewed it in a very conversational format at the Charlotte Ruby Users' Group a month or so ago -- but I'm starting to think it's better suited for a blog post (or series of, at least), so: this.
The idea behind the talk is that, just as fractals are the immensely complex, impressive, and emergent results of the repeated application of a simple set of rules at different levels of magnification, so to might we produce complex and impressive software by repeatedly applying a simple set of principles and practices at different levels of code.
The Sierpinski Triangle
So most people have probably seen the Sierpinski Triangle at some point. Heck, I bet more of them have been idly doodled in middle school math notebooks than were ever generated by practicing mathematicians.
The basic idea is that you start with an equilateral triangle.
You then remove the triangle formed by connecting the midpoints of each side, leaving you with three new triangles:
Next, you do the same thing to each of these three triangles -- remove smaller triangles from the center of each:
And on and on, as many times as you like.
What you end up with is impossibly intricate, and can be manipulated in surprising ways. When conceived of in three dimensions, you end up with a structure that has an infinite surface area and no volume at all! (a pyramidal Menger sponge)
Software
OK, so software isn't exactly analogous to pure geometric space. Nevertheless, we do have distinct levels of magnification:
methods → classes → libraries → applications → ecosystems
We also have a large array of software design principles and practices, each of which is usually couched in the language of one of these levels (or at most, two adjacent levels). The SOLID principles, XP's practices, the law of Demeter, design patterns -- what would happen if we tried applying them across our entire body of software, instead of just when designing a library's API, a user interface, or a single method?
So that's the question. In future posts I'll tackle this question with specific principles, and hopefully we'll find something interesting.
Oh! I should mention -- this is the fourth, fifth, or twenty-third incarnation of my blog, all told. It's running on Posterous, which provides an import feature that I used to bring over all of my old posts. Unfortunately, the import isn't perfect, meaning that a lot of old posts have lost some or most of their formatting, and in many cases links don't work. Sorry! I'm hopeful that future value will make up for past pain, but you all will be the judge of that....
Back in May, I gave a talk at Ignite RailsConf on being awesome. The presentation revolved around three (well, two and a half) rules, of which the first was:
Start stuff
The justification for this rule is simple: if you don't start things, you'll never succeed. To go cliched, you'll never reach any destination unless you take a first step. Beyond that, though, the more things you start, the more chances you have to be awesome. No one in history ever became great by only doing one thing.
To prove my point, here is a sweet and not-at-all made-up chart showing the relationship between the number of things someone starts and how awesome they are (all other things being equal, of course)
OK, it's easy enough to say that you should start lots of stuff, but it's a lot harder to actually do it. As I see it, there are two main problems around following the rule.
What to start
First, you have to figure out what things to start. There's an entire cottage industry around people who think they don't have enough ideas, and I don't have much to add to that -- I'll just tell you what seems to work for me. The more reliably I track my ideas, the more (and better) ideas I seem to have. The medium doesn't seem to matter very much; I've had bursts of creativity with pen and paper, voice recorders, and tools like Evernote alike. I will say, however, that some choices make reviewing things much easier than others, and now I primarily use Evernote for tracking ideas.
Overcoming intertia
The other major challenge around starting things is inertia. We're all busy, and it takes a real effort to start new projects regularly. For me, the best way to deal with that is to start smaller and smaller projects. When I'm reviewing ideas for something new to work on, things that are too big (writing the Great American Novel, building a new piece of hardware to do some task around the house, etc.) are massively intimidating; the fear that I'll never be able to finish them makes it that much harder to start them. Small projects, on the other hand, make it much easier to convince myself that I'll be able to bang them out -- and that's a huge step.
Next up
There's a lot more to say on starting, of course, but I don't want to ignore the other rule-and-a-half, so I'll be talking about finishing and quitting next.
I've been very happy to see that, for the most part, the evangelism around Haml has died down over the past year or so -- it felt like you couldn't go anywhere in 2010 without getting slapped in the face with significant whitespace. It still crops up every once in a while, though, so I thought I'd add my ever-welcome $0.02 and explain why I don't use it.
- HTML isn't broken. Sure, it takes more keystrokes, but there are no faults in HTML that are fixed by Haml (unlike Sass, which actually does fix problems in CSS -- and can I just cheer the separation of Sass into its own gem?).
- Significant horizontal whitespace becomes a gigantic problem when you have more than one screen of content. Even relatively simple web pages can run to multiple pages of markup, invalidating the "indentation as visible structure" argument.
- Haml implicitly promotes a
<div>-heavy markup structure -- it's just too easy to go <div>-crazy with Haml, instead of the slimmer, more semantic markup style that I prefer.
Of course, your mileage may vary. Do what you want, but please stop accosting those of us who choose something else.
Edited: Haml isn't an acronym, so all of my "HAML"s were incorrect. Sorry!
As I'm just now coming out of the caffeine- and sleep-deprivation-induced haze that was my weekend, I thought it might be interesting to reflect on what exactly happened. After forgoing competition last year in favor of judging others as an expert panelist, I decided to compete in the Rails Rumble once again this time around. As I expected, I had a metric ton of fun building my app (Airport Atlas - see its full glory on the RDU Terminal 2 map) all by my lonesome. From 8pm Friday to 8pm Sunday, I pounded code, tweaked pixels in Photoshop, and drank Cherry Coke Zero like there was no tomorrow. My work is far from over, though -- for the next few days, the entries are being evaluated by the new crop of experts, after which some of them will go on to public voting. Wish me luck (and vote for me, if you get the chance!) Some (possibly) interesting stats
- 107 commits
- 251 lines of application code written
- 214 lines of testing code written (1:0.9 code/test ratio)
- 67.2% test coverage (abysmal! It was much higher before Sunday :)
- 20ish cans of Cherry Coke Zero drunk (don't judge me)
I'm really looking forward to continuing to work on Airport Atlas, so feel free to leave comments and suggestions here or on my team page.
Yes, yes, I know I've been gone for a while. What can I say? I've been really busy. That said, I'm rejiggering priorities, and pretty high on the list was getting this site updated -- particularly the Speaking and Upcoming Events pages. If you're interested in knowing where I've been and where I'll be, take a look! (And expect new content here shortly!)
It's been a few days since we wrapped up Developer Day Austin, and I'm only now able to catch my breath – a few snowmageddon- and planning-related incidents combined to keep me pretty busy over the weekend and this week. In a lot of ways, Austin reminded me of the very first Developer Day, back in March of last year. Some of the lessons in logisitics that we learned at that first event, in particular, were significantly reinforced by our experiences in Austin. Despite a few hiccups, however, the event as a whole went over well. I think we were able to bring together at least a few people who don't often meet up – front-end developers and Rubyists, with a few Pythonists and others in the mix. Our lineup of talks was front-end heavy this go-round, and that was interesting; I thought Kyle Simpson and Alex Sexton did a great job bringing the JavaScript thunder, for instance, and I'm encouraged to bring in more front-end talks at future events (especially because JavaScript is almost a lingua franca for web developers at this point). Unfortunately, I had to leave midway through the day (the aforementioned snowmageddon incident), so I missed Aaron Bedra, Eitan Suez, and Bruce Tate, each of whom I was excited to see. And a few words about Austin... first, Austin is clearly Gowalla territory. Stepping off the plane, I was struck by how many featured spots there were, and it made bopping from place to place a lot of fun. Second, it was much colder than I expected. If I thought I underdressed for Boulder last year, it was nothing compared to the mismatch of expectations that Austin (and Dallas, on the way back) triggered. Anyways, I think Austin provided a good start for the 2010 Developer Day season, and I'm really looking forward to the rest of the series – speaking of which, the next event is February 27th in Durham, NC, so if you're anywhere nearby register today!
Raise your hand if you have a job description - a paragraph or eight that describe your role and responsibilities in your current position.Now, raise your hand if you have a specific set of goals for what you are supposed to accomplish today, this week, this month, and this quarter. I'm betting that there's little, if any, overlap between those two groups. I used to be in the job description camp, and I was pretty effective. I built stuff, went to meetings, talked to people, got stuff done... I think I did a good job of matching the description. For the last eight months or so, however, I've been in the second camp. In lieu of a formal job description, I have specific, measurable goals to accomplish in any given time period. As a result, I can tell you exactly how I'm doing at any given point, and where I need to focus my efforts to get more done. For freelancers, of course, this is probably an obvious change – when you work for (and substantially by) yourself, your job description is "get everything done," which means you have to fall back on goals. For employees, however, changing your mindset from a vague prose passage to a set of concrete goals can be a massive shift. It can dramatically improve your effectiveness at your current job – or it could show that you should've been doing something different all along.
I had a thought the other day... I wonder if the demise of correspondence via letters has resulted in a reduction in significant thought. Here's the idea: When long-form letter writing was the predominant means of long-distance communication, you had some astounding exchanges (Descartes' correspondence with pretty much everyone, for instance). Many great thinkers first detailed their theories in these long letters, and I wonder if the form itself made that more likely. Think about it - when you're corresponding via letter, longer messages are more efficient (whereas the opposite is true today, with email, IM, and tweets). Longer messages mean more time writing, and more time writing means more time thinking through what you want to say. As a result, then, writing long letters may have helped people think through their ideas more fully before making them public. If that's the case, then it very well might be the case that today's preponderance of short-form communication makes it much less likely that anyone will release a complex idea fully-formed – but the greater frequency (and reach) of their interactions with other people may overcome that deficit. Could it be that letter-writing was waterfall, and email, IM, and Twitter are agile? (And might this post play into that precise analogy, being far from presenting a fully-formed theory itself?)
|