The first Alpha version of the Features module for Drupal 8 is now available! “But wait!” you say. “I thought we didn’t need Features anymore now that we have the Configuration Management Initiative (CMI) in Drupal 8?”
This article will explain why some sites will still need Features, how Features works with CMI, and how Drupal 8 Features differs from the Drupal 7 version.
Features and CMI
Built in Drupal 8 core, CMI cleanly and consistently separates “content” (articles, blogs, etc) from “configuration” (views, content types, fields, etc). Rather than forcing each module to create its own import and export format, CMI uses YAML (yml) files to store configuration data. CMI also provides mechanisms for deploying configuration between sites, including development, test, and production environments.
Drupal 7 didn’t have CMI, and because the Features module could export and import configuration data as code modules, Features was often used for configuration management and deployment. But Features wasn’t designed to be a full configuration management system. Features was initially written to “bundle reusable functionality”. The classic example was creating a Photo Gallery which was a bundle of a content type, some fields, a view, and an image style.
To create a Photo Gallery module in Drupal 8, you need to use the “single export” option in CMI to export your content type, then export your fields (and field storage), then export your view, then export your image style, and then hope that you haven’t missed any other important settings. This very manual process is subject to errors. Luckily, the Features module in Drupal 8 allows you to pick and choose what configuration data you want to add to your custom module, simplifying the process.
Features in Drupal 8 uses CMI as a consistent storage mechanism, in addition to importing and exporting configuration. Both the user interface and drush command line support are similar to the Drupal 7 version many developers are accustomed to.
New Features in Features
Rather that rewriting Features from scratch for D8, we started with the excellent “config_packager” module from nedjo. His module was created to export “packages” of configuration in D8, which is really the intended use-case of Features. During the past few months I have worked closely with nedjo to merge his config_packager concepts into the new Features module, which gives users some very cool new functionality. It’s been a great example of how open source collaboration often produces better results than either individual effort.
One awesome idea from config_packager was plugins that can automatically assign existing site configuration into packages. When you first install Features and go to the listing page within the Configuration Management section of your site, you will see that it auto-detects your content types (article, page) and creates Features for exporting these content types, automatically including any fields, views, or other configuration assigned to that content type.
You can control which assignment plugins are enabled, and what order they run. Each plugin can have it’s own configuration. For example, the “Base” assignment plugin lets you choose which component should be the “base” for organizing configuration. By default, the base is “content type.” If you change the base to “views” then your site configuration will be organized into Features based upon each view on your site.
These assignment plugins solve the problem of organizing and modularizing your configuration. They also better modularize functionality within Features, such as auto-detecting dependencies, adding profile configuration, or excluding configuration that has already been exported to a module.
Assignment plugins are a great concept, but we wanted to support multiple plugin settings to enable easy switching between “show config organized by content type” and “show config organized by views.” In addition, we wanted to add “namespaces” to Features to better isolate configuration exported to different profiles. For example, “namespaces” make it easier to support sites running multiple distributions, such as Open Atrium running on Panopoly, where some Features are prefixed by oa_* and other Features are prefixed by panopoly_*.
In D8 Features we implemented “bundles” to specify the namespace (prefix) of Features modules, similar to the “package” tabs along the left side of the Features listing in D7. These bundles can be assigned to a specific profile, such as Panopoly or Open Atrium and will filter the Features listing to only show modules within the bundle’s namespace. Assignment plugin settings are stored within the bundle, allowing different bundles to organize configuration in different ways. You can also easily export configuration to a different bundle, making it finally easy to copy your Features from one namespace to another.
Features is for Developers
Drupal 7 didn’t have CMI — core support for configuration management was added via Features. If a custom module contained configuration exported by Features, you needed to have the Features module enabled (in most cases), even on Production, to use that module. In Drupal 8 we wanted to remove the dependency on the Features module. When you create a module using Features D8, the module will work on any other D8 site even without Features being installed!
In Drupal 8, configuration is owned by the site and not by modules. This is a very important design decision behind CMI and how your site configuration is managed and deployed. Configuration provided by a module is imported into the site only when the module is enabled. Once the configuration is imported, any changes made to the config files in the module are ignored by the site. The only way to re-import the configuration is to uninstall the module and then reinstall it.
Drupal 8 also does not allow you to enable a module that contains configuration that already exists on the site. This can cause problems with updating configuration provided by a module. For example, if the Photo Gallery view within our Gallery module needs to be changed, you normally need to uninstall the module and then reinstall it. However, some configuration is not removed when you uninstall a module. Thus, you can have a situation where you uninstall the module and then cannot reinstall it because the configuration already exists. You must manually delete the view provided by the module before you can re-enable the module.
These hurdles and restrictions can make developing modules that contain configuration difficult. As a developer, Features provides functionality to help with these issues. When the Features module is active, it allows you to enable a module that contains configuration already on your site. Once a module is enabled, Features allows you to Import changes from that module into your site without needing to first uninstall the module. This prevents the situation of having a module that cannot be re-enabled, or a module that cannot be uninstalled because of dependencies and makes it much easier to update the configuration stored in the module during development.
You only need to install the Features module in your development environment. Once you have updated your feature module and imported the configuration changes into your dev environment, you then use normal Drupal 8 CMI to deploy configuration to staging and production. If you still want to keep the Features module enabled on staging or production for doing config imports, you can disable the Features UI submodule and only use the drush interface.
If you change configuration in Drupal 8 that was imported from a module, Features will detect and display this change. If you import the new module configuration, it will overwrite the active site configuration. If you export the changes you will update the config data stored in your module. CMI controls the granularity of this configuration. For example, an entire View is a single configuration entity. Thus, there is no way to only import a simple change, such as the title of the view. Importing the view will overwrite the entire view, replacing any other changes that might have been made.
So far there isn’t any way to manage partial configuration changes in Drupal 8. CMI was specifically designed not to handle partial configuration. Currently a module that needs to update partial configuration needs to implement an update hook. However, the code to update a partial configuration object is very specific to the configuration being changed. There is no overall consistent way to “merge” configuration between the module and active site. The config_synchronizer module is the beginning of some good ideas on monitoring site config changes to determine if it is safe to import new config from a module and might be the start of a better Features Override module in the future.
Rewriting Features for D8 was a very interesting project for me. I started with little knowledge of the guts of Drupal 8. I’m actually amazed by the amount of work that has gone into Drupal 8 to make it look and feel like Drupal (Views, Content Types, etc) and yet have a completely different architecture based on Symfony.
Working on Features for Drupal 8 was actually a joy.
Once I got the hang of some of the new concepts such as services and routing and plugins, it was actually fun to create Features. Sure, there are still some rough edges and some core config formats changed even during the Drupal Beta that caused some things in Features to break.
Ultimately I challenge developers to give Drupal 8 a chance and start “poking the tires” to give it a spin. In only four weeks I was able to learn D8 and produce a useable module for something as complex as Features (while also learning config_packager and doing a lot of refactoring). It’s not nearly as onerous to develop in D8 as I had been led to believe by some bloggers. I’m very excited for this new chapter of the Drupal story and also happy that Features will be playing a much smaller part in that story than ever before.
If you want to learn more about Features and see a demo, come to my session at DrupalCon LA next month: Features for Drupal 8. If you want to help, go to the Features project page on drupal.org and download the latest 3.x version and start playing with it. Post your bugs and suggestions to the issue queue. We could really use some help writing automated tests in order to release a Beta version. I am looking forward to our bright future in Drupal 8 where we no longer need to curse about Features and can focus back on building reusable functionality.