Creating a Drupal Feature is easy. Creating a Drupal Feature that everyone can use is really hard.
Features, as in the Features module, allows you to very easily export something that you have created in a Drupal website in order to use it in another. This is an extremely powerful tool that is the basis for how many Drupal websites are built. Not only can you share functionality from one site to the next you can use it to start new websites. Over time as the functionality changes, or matures, the Feature can be updated with new items and then re-deployed to the sites that currently using the Feature so that they get the new changes and are not left behind.
Drupal by nature is a complex mess of data and configuration that could either be in code or in a database. Drupal 8 is making significant leaps and bounds to wrangle this mess but it won't solve all of our problems. In Drupal 7 it is possible to have parts of a Feature in code and parts of a Feature in the database. Modern day site building strategies have determined that having all site configuration in code is the best way to build and maintain a website. Many agencies, teams, and shops, use this strategy, very successfully, to maintain their organization's website or websites. They are able to make changes to their Drupal site Features on a local, or development, environment and use Features to export, or re-export, the code to deploy to a production environment. This works well when you have a developer, or team of developers, working to maintain a handful of similar websites. This does not work well if you let site builders make changes through the UI on the live site.
Unleash the UI!
At Stanford we heavily use Features to create our websites. At the time of this article we have over 1600 websites in production and we have several products (distributions) that we maintain. Our products share many of our Features and use them in slightly different ways. When we deploy a website for a department or group, at Stanford, we hand over the keys to the UI to their team. The other departments team is able to create content, Views, Contexts, View Modes, and more. Any seasoned Drupal builder should be shouting "But think of the Features!" right about now. Yes, with handing over the UI, site owners can override Features extremely quickly.
As a developer, overridden Features are a big red flag of unpredictability. They say "Don't touch me. I am a special snowflake now." They are no longer the Feature we once knew and we don't know what they have become. This is a problem because now some of the configuration is in the database and some of the configuration might still be in the code.
Finding the common ground
Finding a common set of configuration for anything with a large set of players is difficult, if not impossible. When you whittle down the feature requests, needs, and wants, then take away the "specific to one" functions, you are left with a very small, and most often, not very useful set of features. Do you still try to find this common set? Is it worth the effort? Who does it benefit? Yes, but finding the common ground is not as important. What is important is that the feature either can support the special or unique parts through configuration or accelerates the development of such.
How do you build the feature(s)?
We have four scenarios that we need to account for when building a Feature.
- For use in any Drupal website (public)
- For use in our (Stanford Web Services) distributions
- For use in a client specific distribution or custom website
- KIT Compliance (sorta)
This means that not only does the feature have to work and look good and the Drupal contributed module level but also have the special sauce required for specific distributions. We need to support things like namespaces, encapsulation, configurability, and upgradability.
Naming things is hard. Naming something so that it doesn't collide with other similarly named things is even more difficult. With Features in Drupal 7 this means prefixing everything with a namespace. From your content types to your fields to your views, it is important that you prefix the machine names with your namespace.
Example for a page content type:
content type label: Page content type machine name: stanford_page fields: field_s_page_fieldname views: stanford_page_view context machine name: stanford_page context group: Stanford Page
Feature encapsulation is extremely important. De-coupling the feature from themes, other features, and site specific features is key to the ability to use it anywhere. Even though you can use a Feature anywhere, there still is the expectation that it will do something and look good out of the box. Providing sensible defaults is a way to support the out of the box issue but as the word default implies, there needs to be a way to change or override them. Some things that change often are:
- Display settings, theme options, view modes, or page layouts
- Front end and administration views
Keeping Feature modules de-coupled is important. A good example for when it would be a good idea to break something out in to its own module would be when a content type has a entity reference field to an entity type that is also in a Feature module. This relationship field would create a direct relationship and dependency on the other feature module. If possible, it would be a good idea to break out the entity reference field into its own module so that the core content type and fields could be used as stand alone.
Allowing for site specific configuration within a Feature module is important for its success and adoption. Allowing a feature to have options can be as simple as breaking out choices in to their own separate Feature module. This would allow site administrators, or installation profiles, to choose which options they want to turn on.
Scenario #1: I want one of the bundled options
For example, if you created a page feature that could have sidebar blocks on the left or the right columns, you could separate the two options in to feature modules of their own and allow individual sites to enable one or the other.
Scenario #2: I don't want any of the bundled options
For example, if you created a page feature with a separate Feature module for the node display, as a site builder I could choose to disable, or not enable, the Feature module with the node display in it. This would allow the site builder to then build any layout for the page feature without overriding the Feature.
Scenario #3: I want what is bundled but slightly different
Creating separate Feature modules for choices is not always a fit as it is an on or off strategy. Sometimes you want more options and for that you will need to have a developer create a configuration page/form/place for the additional choices. Adding modules and custom code to hook into existing features to make the changes will prevent a feature from being overridden.
It is important that as a Feature changes over time that older versions of the Feature do not get forgotten. Newer versions of a feature should not break older versions. For example, if a field called field_s_page_type gets renamed to field_s_page_category, an update hook will need to be written to handle this change. This will include moving all of the data in the previous field name in to the new one.
The general rule: New versions cannot break older versions.
E. General practice
- Stay as close to KIT compliant as possible.
- Never put content in a Feature. There are better methods for creating content.
- Content includes but is not limited to:
- Taxonomy terms
- Menu items
- Block classes
- Never use a strongarm variable that is used outside of the feature it is intended for.
- For example, Display suite exports a variable for field to block that stores information for all content types. This breaks the encapsulation rule and cannot be used.
Working with shared features
The above article details how to create and maintain Features in Drupal. Please read part two of this topic by navigating to and reading "Tips on how not to bork your features". The second part is geared towards site builders working with a site built with shared features.