Skip to content

Commit

Permalink
Updated features doc. (#281)
Browse files Browse the repository at this point in the history
  • Loading branch information
danepowell authored and grasmash committed Aug 12, 2016
1 parent 1a2b1dc commit f05eaf9
Showing 1 changed file with 15 additions and 22 deletions.
37 changes: 15 additions & 22 deletions readme/features-workflow.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,28 +18,21 @@ Generally speaking, a configuration change follows this lifecycle:

As you can see, adding or updating configuration *should* be as easy as exporting the configuration via the Features UI and letting continuous integration processes handle the rest. Unfortunately, due to Features' immaturity in D8, there are a few limitations and best practices to be aware of.

## Gotchas and best practices
## Best practices

### Using bundles
Features lets you define custom "bundles" that essentially let you train Features to support your project's individual workflow. At the most basic level, they are a way to namespace your features, so you'd want to choose a bundle name based on your project name (an "Acme" bundle would prefix all of your feature machine names with "acme_").

Bundles can also do a lot more to make your life easier. For instance, Features automatically suggests features based around content types and taxonomies. If you'd also like to automatically create features for, say, custom block types, you can configure that preference in your custom bundle. You can also choose to always exclude certain types of configuration (such as permissions--see below), or always group certain types of configuration (such as field storage) into a "core" bundle, which is super helpful for breaking circular dependencies (see below).

Unfortunately, for now it seems that bundles are only stored in the database, so your project bundle needs to be created in a system-of-record environment (i.e. production) and then copied down by each developer.

### Managing roles and permissions
Sorry, you [can't do it with Features](https://www.drupal.org/node/2383439). Don't even try, you will be sorely disappointed (as will your database's existing roles and permissions, which will be deleted). For this reason, it's recommended that you use Bundles (see above) to exclude roles from features.

Instead, you'll want to use Drupal's core configuration management system for this by storing role exports in the `config/default` directory and using `drush config-import --partial`to import them on deploys.

### Testing features
You must, must, must test your features. Did I mention you _must_ do this? Seriously, you will regret not doing this.
It’s important to ensure via automated testing that features can be installed on a new site as well as enabled on existing sites.

There are many reasons that features can fail to install or import properly. The most frequent cause is circular dependencies. For instance, imagine that feature A depends on a field exported in feature B, and feature B depends on a field exported in feature B. Neither feature can be enabled first, and site installs will break. This may not be a big deal if you only have a single-site installation, but if you are building a multi-site platform this is something you want to catch early.

Features can also fail on update for a number of reasons (see below). Most frequently, a feature stays "overridden" after it is imported, due to another module overriding the provided config. For instance, workbench adds a special field to content types when it is enabled. If this field isn't exported to the feature containing a content type, the feature will be perpetually overridden.

For these reasons, it's important to have developers test importing their own features, and to have automated tests that install sites from scratch using all available features. You can use the following code snippet in your profile's install file to enable all features in a given bundle:
You can use the following code snippet in your profile's install file to enable all features in a given bundle:

<?php
$available_modules = system_rebuild_module_data();
Expand All @@ -51,8 +44,15 @@ For these reasons, it's important to have developers test importing their own fe
\Drupal::service('module_installer')->install($dependencies);
}

### Features doesn't do that, a.k.a. workarounds
There are some configuration changes that Features doesn’t handle well, or doesn’t handle at all, including:
## Gotchas and workarounds

### Managing roles and permissions
You can manage roles with Features, but not individual permissions ([reference](https://www.drupal.org/node/2383439)). For this reason, it's recommended that you use Bundles (see above) to exclude roles from features.

Instead, you'll want to use Drupal's core configuration management system for this by storing role exports in the `config/default` directory and using `drush config-import --partial`to import them on deploys.

### Updating fields and schema
There are some configuration changes that Features (and the core config system) doesn’t handle well, including:

* Updating field storage (e.g. changing a single-value field to an unlimited-value field)
* Adding a [new custom block type](https://www.drupal.org/node/2702659) to an existing feature (sadly, you have to create a new feature for every block type)
Expand Down Expand Up @@ -104,23 +104,16 @@ This depends on a helper function like this, which I suggest adding to your cust
return $storage[$module]->read($id);
}

Additionally, there are some inherent limitations with the Drupal 8 configuration management system:

* You can't "split up" an individual configuration file. For instance, the Acquia Connector module exports all of its settings, including its secret API key, into a single settings file (sigh). There's no way to prevent that key from being exported, so you generally wouldn't want to export this file at all.
* You can't export a config file that is provided by a contributed module. For instance, if the Pathauto module provides default configuration settings, you can't export your own Pathauto settings with Features (or rather you can, but if you try to install the feature from scratch you will get a fatal error).

In both of these cases, the only workaround for now is to manage the configuration via update hooks instead of config files (this is the approach used by Lightning). Fortunately, configuration is easy to manage in code:
Additionally, an inherent limitation of the Drupal 8 configuration system is that you can't "split up" an individual configuration file. For instance, the Acquia Connector module exports all of its settings, including its secret API key, into a single settings file (sigh). There's no way to prevent that key from being exported, so you generally wouldn't want to export this file at all. You can work around this by managing the configuration via update hooks instead of config files. For instance, Lightning uses this snippet to set the active theme:

$theme_settings = \Drupal::configFactory()->getEditable('system.theme');
$theme_settings->set('default', 'my_theme')->save(TRUE);

Unfortunately, you lose the ability to track these changes and your active configuration is at risk of "drift". Hey Drupal community, we'd love to see a solution to this! (a [recent Features patch](https://www.drupal.org/node/2704181) may or may not help this situation).
Finally, you have to be careful when updating core and contributed modules. If those updates make changes to a module’s configuration schema, you must make sure to also update your exported features definitions. Otherwise, the next time you run features-import it will import a stale configuration schema and cause unexpected behavior. We need to find a better way of preventing this than manually monitoring module updates. Find more information in [this discussion](https://www.drupal.org/node/2745685).

### Other gotchas

Be aware that since Features is a ground-up rewrite in Drupal 8 and still quite young, there are still a lot of bugs to work out. For instance, when you export a feature the UI will frequently try to automatically include [unrelated configuration](https://www.drupal.org/node/2720167) or dependencies (or worse, it will [forget to include](https://www.drupal.org/node/2666836) very necessary dependencies, such as the field storage for field instances!) Developers just need to keep a close eye on this when exporting features, and TA need to carefully review features in PRs for the gotchas and best practices listed above.

You also have to be careful when updating core and contributed modules. If those updates make changes to a module’s configuration schema, you must make sure to also update your exported features definitions. Otherwise, the next time you run features-import it will import a stale configuration schema and cause unexpected behavior. We need to find a better way of preventing this than manually monitoring module updates. Find more information in [this discussion](https://www.drupal.org/node/2745685).
Features is a ground-up rewrite in Drupal 8 and is maturing quickly, but may still have some traps. Developers should keep a close eye on exported features, and TA need to carefully review features in PRs for the gotchas and best practices listed above.

## Getting set up on Acquia Cloud
When setting up a project on Acquia Cloud, it's recommended to add Cloud Hooks for post-code-deploy, post-code-update, and post-db-copy that will automatically perform the following steps:
Expand Down

0 comments on commit f05eaf9

Please sign in to comment.