With rails, we build up our application using the chunks of functionality that the framework and it’s libraries provide. This is different to web platforms like Drupal, where we might reasonable expect to get a basically working application (for the default use cases) out of the box, and be able to configure it to change it slightly.
The problem with configuring a monolithic platform is of course that we’ll never get it to work exactly the way we want. That approach works well if our requirements are close to it’s area of core functionality, but for custom software, we want to build up, rather than configure downward.
Building our apps up has been a key philosophy in rails from the beginning. In fact, it’s been so important that the core team has strongly resisted any changes to rails which looked too much like they encouraged monolithic, configurable software.
So, if we think of a rails app as a structure built up from lots of little blocks, which all do different things and are often interchangeable, then we have a good working metaphor for the ideal rails development process. Rails 3 (due to the Merb influence) has done a lot of work to ensure that some of the fundamental building blocks of rails are easily swappable. This blocks, like which ORM to use, form the foundations of the awesome block fortress that we want to build.
With each layer of blocks that we build up, the blocks tend to be somewhat dependent on the interface of the blocks below. For example, we might build a login system that requires a user model that basically behaves like an ActiveModel object, although we don’t care if it’s implemented using ActiveRecord, DataMapper, or something else.
Once the tower of blocks rises above the foundation of the Rails framework itself, then adding more blocks becomes dependent on some sort of agreement on those interfaces. This tends to happen when gems or plugins become so commonly used that supporting them, or something that looks a bit like them, is a natural approach for further libraries.
For example, there are a number of libraries supporting user authentication, and most of them expose a current_user method which returns some sort of user object. Although this is far from being a clearly defined interface, it’s almost all you need for other libraries to do things conditional on being logged in.
As we reach a consensus on certain libraries being good ones, even the ones that follow them tend to implement these core bits of interface. For example, you’ll find that changing an app from using Authlogic to using Devise is fairly simple.
This process made it feel a bit like the blocks were slowly getting bigger, and we could build bigger and bigger towers out of them without needing to write our own code from scratch. Certain parts of our app are good candidates for giving a lot of thought and custom code, but other parts are simply the bolt-ons that we need to get there, and it’s great to be able to use a generic implementation without having to re-invent the wheel. We don’t rewrite paperclip when we want to store images, and most people tend to use an authentication system these days.
So, why did the progression of these bigger and bigger blocks stop? Where are the rest?
If we want to be able to build up, rather than configure down, from any level of detail, then we need blocks at each level of detail. We need something to store files (paperclip), something to upload and store files (which uses the previous), something to provide an image gallery (which uses the previous), etc. It’s up to us which areas of our app use large blocks, and which ones use small ones.
When we start to talk about blocks that are large chunks of our application, they start needing to include user interface. This tends to be where we get scared and run away. The tricky bit here is the lack of defined interface, and that’s why you tend to see entire open source rails apps that do useful things like run a blog, forum, image gallery, social profiles, but not so many rails plugins that do these, and even less rails plugins that do it by building the functionality up from even smaller blocks which you can swap out.
In my next post, I’ll lay out some examples of what these “slightly larger that presently available” blocks of code might be, and take a stab at figuring out how we might start to define interfaces for them, while keeping a decentralized community process.