Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Docs: feature flag refresh #41522

Merged
merged 3 commits into from
Jun 5, 2022
Merged
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 27 additions & 23 deletions docs/how-to-guides/feature-flags.md
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
# Feature Flags

Often in the Gutenberg project, there's a need to control whether the code we write is shipped to WordPress core, or whether certain more experimental features are only active in the plugin.

Often this is handled using a 'feature flag'.
'Feature flags' are variables that allow you to prevent specific code in the Gutenberg project from being shipped to WordPress core, and to run certain experimental features only in the plugin.

## Introducing `process.env.IS_GUTENBERG_PLUGIN`

The `process.env.IS_GUTENBERG_PLUGIN` is an environment variable that represents whether code is currently executing within the plugin. When the codebase is built for the plugin, this variable will be set to `true`. When building for core, it will be set to `false` or `undefined`.
The `process.env.IS_GUTENBERG_PLUGIN` is an environment variable whose value 'flags' whether code is running within the Gutenberg plugin.

When the codebase is built for the plugin, this variable will be set to `true`. When building for WordPress core, it will be set to `false` or `undefined`.

## Basic Use

A plugin only function or constant should be exported using the following ternary syntax:
### Exporting features

A plugin-only function or constant should be exported using the following ternary syntax:

```js
function myPluginOnlyFeature() {
Expand All @@ -21,9 +23,11 @@ export const pluginOnlyFeature =
process.env.IS_GUTENBERG_PLUGIN ? myPluginOnlyFeature : undefined;
```

In non-plugin environments the `phaseTwoFeature` export will be `undefined`.
In the above example, the `pluginOnlyFeature` export will be `undefined` in non-plugin environments such as WordPress core.

If you're attempting to import and call a plugin only feature, be sure to wrap the call to the function in an if statement to avoid an error:
### Importing features

If you're attempting to import and call a plugin-only feature, be sure to wrap the function call in an `if` statement to avoid an error:

```js
import { pluginOnlyFeature } from '@wordpress/foo';
Expand All @@ -33,68 +37,68 @@ if ( process.env.IS_GUTENBERG_PLUGIN ) {
}
```

### How it works
## How it works

During the webpack build, any instances of `process.env.IS_GUTENBERG_PLUGIN` will be replaced using webpack's define plugin (https://webpack.js.org/plugins/define-plugin/).
During the webpack build, instances of `process.env.IS_GUTENBERG_PLUGIN` will be replaced using webpack's [define plugin](https://webpack.js.org/plugins/define-plugin/).

If you write the following code:
For example, in the following code

```js
if ( process.env.IS_GUTENBERG_PLUGIN ) {
pluginOnlyFeature();
}
```

When building the codebase for the plugin the variable will be replaced with the boolean `true`:
the variable `process.env.IS_GUTENBERG_PLUGIN` will be replaced with the boolean `true` during the plugin-only build:

```js
if ( true ) {
if ( true ) { // Wepack has replaced `process.env.IS_GUTENBERG_PLUGIN` with `true`
pluginOnlyFeature();
}
```

Any code within the body of the if statement will be executed because of this truthyness.
This ensures that code within the body of the `if` statement will always be executed.

For core, the `process.env.IS_GUTENBERG_PLUGIN` variable is replaced with `undefined`, so the built code will look like:
In WordPress core, the `process.env.IS_GUTENBERG_PLUGIN` variable is replaced with `undefined`. The built code looks like this:

```js
if ( undefined ) {
if ( undefined ) { // Wepack has replaced `process.env.IS_GUTENBERG_PLUGIN` with `undefined`
pluginOnlyFeature();
}
```

`undefined` evaluates to `false` so the plugin only feature will not be executed within core.
`undefined` evaluates to `false` so the plugin-only feature will not be executed.

### Dead Code Elimination

When building code for production, webpack 'minifies' code (https://en.wikipedia.org/wiki/Minification_(programming)), removing the amount of unnecessary JavaScript as much as possible. One of the steps involves something known as 'dead code elimination'.
For production builds, webpack ['minifies'](https://en.wikipedia.org/wiki/Minification_(programming)) the code, removing as much unnecessary JavaScript as it can.

When the following code is encountered, webpack determines that the surrounding `if`statement is unnecessary:
One of the steps involves something known as 'dead code elimination'. For example, when the following code is encountered, webpack determines that the surrounding `if`statement is unnecessary:
ramonjd marked this conversation as resolved.
Show resolved Hide resolved

```js
if ( true ) {
pluginOnlyFeature();
}
```

The condition will always evaluates to `true`, so can be removed leaving just the code in the body:
The condition will always evaluate to `true`, so webpack removes it, leaving behind the code that was in the body:

```js
pluginOnlyFeature();
pluginOnlyFeature(); // The `if` condition block has been removed. Only the body remains.
```

Similarly when building for core, the condition in the following `if` statement always resolves to false:
Similarly, when building for WordPress core, the condition in the following `if` statement always resolves to false:

```js
if ( undefined ) {
pluginOnlyFeature();
}
```

The minification process will remove the entire `if` statement including the body, ensuring plugin only code is not included in the built JavaScript intended for core.
In this case, the minification process will remove the entire `if` statement including the body, ensuring plugin-only code is not included in WordPress core build.

## FAQ

#### Why shouldn't I assign the result of an expression involving `IS_GUTENBERG_PLUGIN` to a variable, e.g. `const isMyFeatureActive = process.env.IS_GUTENBERG_PLUGIN === 2`?

The aim here is to avoid introducing any complexity that could result in webpack's minifier not being able to eliminate dead code. See the [Dead Code Elimination](#dead-code-elimination) section for further details.
Introducing complexity may prevent webpack's minifier from identifying and therefore eliminating dead code. Therefore it is recommended to use the examples in this document to ensure your feature flag functions as intended. For further details, see the [Dead Code Elimination](#dead-code-elimination) section.