The Features module is a module that creates modules called features.
You're still here? Wasn't that crystal clear for you? Ok, I suppose I'll go ahead and elaborate.
I've been waist deep in Features module working on a new video series for Drupalize.me and have been thinking it would be valuable to provide a bit of a retrospective on the tool, what it was created for, and how the Drupal community has been using it.
The Back Story
It is March 2009, and I am waiting for a session to begin at DrupalCon DC, prepared to slip out if it turns out to be uninteresting. It's entitled A Paradigm For Reusable Drupal Features, and it's being led by a bunch of guys with these orange stickers on their MacBooks. I'm already skeptical because I'm not sure why anyone would willingly place a sticker on something they like. Apparently a lot of people think this is a good idea, so I decide to give them a chance to redeem themselves. They did.
Drupal had made giant leaps in terms of being able to administer a very complex and intricate system right from the comfort (ahem) of your favorite browser. It used to be that if you wanted a new node type, you had to write a module to do that. Want to add a field to that node type? Time to brush up on your SQL chops, because you're going to have to do that all by hand in code. Want to display a customized listing of that content? Well, don't set down that SQL handbook just yet, you'll need to write all those queries yourself, and write the markup to go with it. All of that had changed with the awesome work that was being done with the Views and CCK modules, and it seemed to just get better every day.
This was all great for the recipients of these sites, the people that work on the 'Content Management' part of a CMS. But what about the developers? What about those souls in the Drupal trenches day after day? Create node type. Add CCK fields. Create view. Rinse. Repeat.
Everyone had been all "Yay CCK!" and "Yay Views!" for a good time now, but for those of us actually using these tools every day, it frankly was getting to be the developer equivalent of data entry. Oh, this client wants a blog too? Ok, click here, type here, click here, type here, click here, type here. Isn't this awesome? You can do it all in your browser! Boo.
Most of us had already been working on ways out. If you are familiar with Views module, you've probably seen how it has the ability to export a view to PHP code. You can then import that view on another site. This ability began a movement in the Drupal community to start making things 'exportable', or in other words, creating the ability for configuration to live in code, not just in the database. It wasn't long before all sorts of Drupal Stuff™ was exportable: CCK Fields, Blocks, Contexts, Panels, Variables... The list goes on and on.
I learned quickly in this session that Development Seed has some super smart people. The awesome thing about super smart people is that they often do super smart stuff, and there was no exception here. Development Seed began to show in detail their solution to some of these problems. They had a good workflow for making Drupal development faster, and Drupal deployment possible. Surprise surprise, it was in part by going back to the old fashioned way of doing things. Want a new node type? Do it in code. Want CCK fields on that node type? Code. Want a View? Export it to code. Like I said, lots of us had already been doing that. We had these monolithic modules that contained bits and pieces of what made our website tick, and filled in the gaps with update hooks and even (gasp) SQL queries to do iterative deployments. The difference was, Development Seed had created a pattern for building these modules, and a big distinguishment was that their modules were focused on accomplishing very specific, very concrete tasks.
Did You Just Say Concrete?
Yes, yes I did. So often in the Drupal development world we're thinking about how to make things more abstract, moving away from the specific to the generic. Because of this, we have loads of modules that sit like tools in our tool boxes. You go to the module administration page and start clicking things, hit submit, and what have you done? Nothing! All you've done is load up your toolbox to the point that it's probably pretty heavy (read slow). You've got enough tools to make Norm Abram jealous, but haven't actually built anything worth showing your friends.
Development Seed had begun changing that by spear-heading a movement to create modules to accomplish specific things. Need a blog? Don't just start clicking things willy nilly till you get what you want. Create a module for that 'feature' that you want, and start adding things to it. Create a blog content type, add a Subtitle field to it, add that blog view, and get it all in code into a module. All that work pays off very quickly when you want a blog on another site. Click the checkbox next to your new module, hit submit, and that's it. Really. None of that clicky-type-clicky-type stuff.
I remember coming out of the session like it was January 1 and I had just bought a treadmill, ready to take on the world with fresh legs. At the time I had been working daily on a Drupal platform called WebGear that was trying to be all things Drupal wasn't: Pretty, Easy to Use, Simple. Stuff like that. I left DrupalCon re-thinking the entire architecture of what we were building. I was picturing nice little boxes in my head, each containing just the code specific to accomplishing the task associated with it. A Blog module. A Gallery module. A FAQ module.
Enter the Features Module
What did this module do? Well, it actually wrote modules for you, just like the ones they had described in their session. Using the example of the Blog again, you create your node type, add fields, create a view, etc. Then you go to this "Features" interface, and just by clicking checkboxes, you can the turn all that clicky-type-clicky-type work into a nice pretty Drupal module that you can turn on and off at will.
I wet myself.
And then I converted all of my site-specific modules into these new 'feature' modules.
It wasn't long after this that the Features module really started taking off in the Drupal community. If they weren't using it, Drupal developers were at least talking about it. It made deployment of new functionality super fast. It made maintaining that functionality easier. It made it so that you could have version control on every little tweak to your functionality (I know you are all pretending like you haven't spent 2 hours tweaking a new display on a view to have lost all of your work by accidentally deleting that display moments later), which in turn made consistent debugging possible (git-bisect anyone?).
Using Features as a Deployment Tool?
We started using Features on every project we did after that, and I have to say it made so many things so much easier. Sure, it had its share of problems, but for the most part, it did what we needed it to do. If there wasn't a feature for something we needed, we created it. I wish I could say, "and then everyone lived happily ever after," but I can't.
Features was created with a lot of assumptions. I had an advantage being a part of the discussion early on, so I knew why and how Features made these assumptions, but I quickly realized that others coming in didn't have this background. It didn't come as naturally to them to think of things in terms of "use cases", so you'd see features being created like "All Variables" or "All Contexts"—solitary modules that contained all the variables that were being Strongarmed, or all the Contexts, or all the Views.
Why were people doing this? The reason is that they were using the Features module as a Deployment tool. They wanted to get their configuration into code, and then wanted a way to deploy that configuration to a live site. They also wanted a shortcut from having to do that work by hand, from having to write that PHP code into a module. This seems natural enough, right? But if we start using a tool before we understand what it was built for, and maybe even a bit of history behind it, we will surely be in for some frustration.
Features was built by a team of developers (you know, those guys with orange stickers on their MacBooks) working on the Drupal distribution called Open Atrium. As the authors put it, it exists to help you write modules that will "satisfy...certain use-case[s]." That's it. It wasn't built as a deployment tool, even though it is often used for that. It wasn't built as a productivity tool, although it can make you more productive. It was built to help you create little self-contained, packaged modules, oh, and by the way, for-the-love-of-Pete-please-don't-let-the-modules-touch-each-other.
Excuse Me, Your Feature is Touching My Feature
As soon as you create an "All Contexts" feature, you have just begun down the road toward Dependency Hell. Follow up that feature with a feature of all your views, and you may end up with a circular dependency, where the "All Contexts" feature depends on the "All Views" feature which depends on the "All Contexts" feature. Whee! This is a lot less likely to happen in newer versions of the Features module, but I make no promises.
Even if you are creating Features by the book, you've probably run into similar problems with dependencies. Features module starts with the assumption that there can somehow be this atomic (stand-alone) sort of module that just does X and that's all it does and any configuration or functionality it provides will not—and therefore cannot—be touched by any other module. Unfortunately, software development doesn't work that way, as Victor Kane so eloquently affirms. In the real world, things can't be isolated into tidy boxes that never touch. We've created a Blog feature, assuming it is an atomic piece of functionality. Then the software requirements change and we find we need to display some biographical information about the author of a blog post in the sidebar. Great, except the field that stores that data happens to exist in a completely separate feature. Whoops.
The problem is twofold. 1) Dependencies exist between modules only, and 2) exported configuration exists within modules only. We export a view, and put it in a module using Features. Anything that depends on that view must depend on the module that contains the view, not the view itself. For instance, going back to the Blog feature, when we need to display the author's bio in the sidebar we might configure the blog Context to display the view of the author bio in the sidebar. Instead of the Context getting a new dependency on the existence of that view, the Blog feature now has a dependency on the User Profile feature.
Use, Don't Abuse
The path of least resistance in using Features module is to work with it: realize it is a tool created for a very specific task, and use it in that context. The Drupal community by and large (myself included) has been using Features module to try to fill the need for a deployment tool. It feels a lot like pounding in a screw with a hammer. It works, but it's certainly not ideal, and since we don't have a screw driver, we don't have an easy way to get the screw out now that we've pounded it in.
I'm not saying don't use Features. I use it every day. I still love it. But I try to remind myself often that I am holding a hammer in my hand, not a screw driver. I've got my share of bruised thumbs and broken screws, but it sure beats screwing these in with my fingers. Understanding the history behind it and the use case it is primarily trying to solve will go a long way in helping you experience the least amount of pain in working with Features.
Links and Resources
If you're interested in learning more about Features, check out these links:
- The Features module project page, which contains lots of helpful information
- 4-hour Drupalize.me Video series on Drupal Deployment with Features & Drush
- The Kit Specification, which describes best practices for creating features
- MustardSeed Media Video on creating a quick Feature in Drupal 6
- Features Plumber for resolving nasty conflicts
- Features Override module for altering pre-existing features
- DrupalCon presentation on creating a Drupal distribution with Features by Dmitri Gasken
- Debut, a set of baseline features
- Modules on Drupal.org that are features or integrate with Features module somehow