### Feature Flags

#### ows-features
Feature flags are used to control whether particular changes are presented to end-users.

They are defined in three different locations:

1. [ows-features](https://github.com/theorchard/ows-features) repository

    The canonical location for feature flags is in the ows-features project. Features are
    defined in a [config file](https://github.com/theorchard/ows-features/blob/master/features.yml)
    in a YML format, e.g.:
    
    ```yml
    - name: example_feature
      variants:
        enabled: 0
        control: 1
    
    - name: another_example_feature
      variants:
        enabled: 0
        control: 1
      force:
        enabled:
          user_ids:
            # vend_contact.id of vendor 7123's master contact:
            # SELECT id FROM vend_contact
            # WHERE master = 'Y'
            # AND vendor_id = 7123;
            - alw:16888
            # vend_contact.id of vendor 16055's master contact:
            # SELECT id FROM vend_contact
            # WHERE master = 'Y'
            # AND vendor_id = 16055;
            - alw:17163
    ```
    
    For a feature flag to be usable in QA or production, it must be defined in this file. Please visit
    the repo for more information.

1. In [render-layout.js](https://github.com/theorchard/frontend-distribution/blob/master/lib/util/render-layout.js)

    [user-feature-defaults.js](https://github.com/theorchard/frontend-distribution/blob/master/lib/util/user-feature-defaults.js) is consumed by [render-layout.js](https://github.com/theorchard/frontend-distribution/blob/master/lib/util/render-layout.js)
    which will be used when running a development server. These default features will be extended/overridden by the values returned by ows-features
    and then the resulting features will be extended/overridden by USER_FEATURES set in the .env file or set manually on the environment.
    
    ```javascript
    const userFeatureDefaults = {
        [USER_FEATURES.EXAMPLE_FEATURE]: USER_FEATURES.VARIANT_CONTROL
        ...
    };
    ```

1. In .env files (USER_FEATURES environment variable)

    Developers may want to selectively enable or disable feature flags outside of version control. To do
    this, add an environment variable to .env (or set it manually) with the following format. These will override individual
    values defined in `user-feature-defaults.js`
    
    ```js
    USER_FEATURES={"example_feature": "enabled"}
    ```

#### Example usage:

Suppose that you are starting work on a new story with a feature flag called 'awesome_thing'.

1. Add a value to the `USER_FEATURES` enum in `src/constants`:

    ```javascript
    export const USER_FEATURES = {
        ...,
        AWESOME_THING: 'awesome_thing'
    };
    
    ```

1. Add a disabled version of the feature flag to [user-feature-defaults.js](https://github.com/theorchard/frontend-distribution/blob/master/lib/util/user-feature-defaults.js).
This will help document the presence of the feature flag to other developers, without forcing them to use in-progress code prematurely:

    ```javascript
    export default {
        ...,
        [USER_FEATURES.AWESOME_THING]: USER_FEATURES.VARIANT_CONTROL
    };
    ```

1. For your development purposes, override the feature's enabled property in your .env file. This will
enable the feature when you run your development server:

    ```js
    USER_FEATURES={"awesome_thing": "enabled"}
    ```

See section below on using the feature flag in code.

1. Add a lookup method to `src/utils/user-features.js`, something like:

    ```javascript
    export const isAwesomeThingEnabled = () => isActiveFeatureVariant(
        USER_FEATURES.AWESOME_THING,
        USER_FEATURES.VARIANT_ENABLED
    );
    ```

1. Use the feature flag in code, e.g. in a component:

    ```javascript
    import PropTypes from 'prop-types';
    import React, { Component } from 'react';
    import { isAwesomeThingEnabled } from 'src/utils/user-features';
    
    class UserFeatureDemoClass extends Component {
        render() {
            if (isAwesomeThingEnabled())
                return <h1>Hi, {this.props.name}</h1>;
            return <h1>Hello, {this.props.name}</h1>;
        }
    }
    
    export default UserFeatureDemoClass;
    ```

1. Once the feature flag is done enough to let other developers use it, update [user-feature-defaults.js](https://github.com/theorchard/frontend-distribution/blob/master/lib/util/user-feature-defaults.js) to
set the feature's active variant to `USER_FEATURES.VARIANT_ENABLED`:

    ```javascript
    const userFeatureDefaults = {
        ...,
        [USER_FEATURES.AWESOME_THING]: USER_FEATURES.VARIANT_ENABLED
    };
    ```

1. When the story is ready for QA, add the feature to [ows-features](https://github.com/theorchard/ows-features/blob/master/features.yml). Typically
you will enable it for a couple of test users:

    ```yml
    - name: awesome_thing
      variants:
        enabled: 0
        control: 1
      force:
        enabled:
          user_ids:
            # vend_contact.id of vendor 7123's master contact:
            # SELECT id FROM vend_contact
            # WHERE master = 'Y'
            # AND vendor_id = 7123;
            - alw:16888
            # vend_contact.id of vendor 16055's master contact:
            # SELECT id FROM vend_contact
            # WHERE master = 'Y'
            # AND vendor_id = 16055;
            - alw:17163
    ```

7. Add a ticket to Jira to remove the feature flag once it's no longer necessary.


#### Using feature flags in code

Given that a feature flag has been declared in the appropriate spots listed above, how should you
use it in code?

##### How feature flags are injected in development

###### Summary for ows-features

1. .env values are loaded when the express server starts

1. These are combined with defaults in render-layout.js when web requests are received for the index file, and injected into the html source in a script block.

1. A request is performed to ows-features and the returned features are extended by the values from step 2 before the react app is first rendered, and are accessible in the client thereafter.


###### Details for ows-features

Note: If you're interested in one aspect of how the development server config works or are debugging a feature flag problem, feel free to walk through this, but it isn't necessary for using feature flags in development.

1. `$ yarn start` is used to run the development server.

1. This runs this in package.json: `env NODE_PATH=$PWD NODE_ENV=development node lib/dev-server`

1. `lib/dev-server/` runs an express server that uses webpack to package the app.

1. `webpack.config.js` includes a call to `require('dotenv').config({ silent: true });` that
loads the .env file, putting the config variables into the process.env keys. In particular, the
`USER_FEATURES` entry, if defined, becomes available as `process.env.USER_FEATURES`

1. `lib/dev-server/server.js` maps non-api requests using (`app.get('*', (_r, res) => renderIndexToString().then(layout => res.send(layout)));
`).

1. `renderIndexToString`, defined in `lib/util/render-layout.js`, uses pug to render
   `index.html.pug` with `__USER_FEATURES_OVERRIDES__` set to `process.env.USER_FEATURES`
   and `__USER_FEATURES_DEFAULTS__` set to `lib/util/user-feature-defaults.js`.

1. `index.html.pug` extends the generic `index.layout.html.pug` with a call to `initProjectManager`

1. `index.layout.html.pug` creates `window.getUserFeaturesDefaults()` and `window.getUserFeaturesOverrides()` that return
   `__USER_FEATURES_DEFAULTS__` and `__USER_FEATURES_OVERRIDES__` set by `lib/util/render-layout.js`.

1. `index.layout.html.pug` loads `src/load-user-features-before-scripts.js` and then calls `loadUserFeaturesBeforeScripts`
   with the bundle to load after user features are loaded from ows-features.
    
1. `loadUserFeaturesBeforeScripts` requests features for the current user from ows-features and creates the function
   `window.getUserFeaturesFromOwsFeatures()` to store them. This function is created for debugging purposes and is not used
   by flagged code.
    
1. `loadUserFeaturesBeforeScripts` creates the function `window.getUserFeatures` which returns the return value of
   `window.getUserFeaturesDefaults()` extended/overridden by the features returned from ows-features
   extended/overridden by `window.getUserFeaturesOverrides()`. Only the local dev/standalone index page creates
   `window.getUserFeaturesDefaults()` and `window.getUserFeaturesOverrides()` so when rendered in workstation `window.getUserFeatures`
   just returns the features returned from ows-features.

1. `loadUserFeaturesBeforeScripts` loads the main application code bundle and calls the bundle initialization function.

1. Feature flags are read from the window by `isActiveFeatureVariant` in `src/utils/user-features.js`.

1. Rather than calling `isActiveFeatureVariant` in different modules, the convention is to create a helper
    function in `src/utils/user-features.js` that wraps around the call to `isActiveFeatureVariant`, e.g.
    `isAwesomeFeatureEnabled()`. This makes testing a little easier.

#### Caveats in dev

1. When testing feature flagged code you'll want to test the code with all possible variants of the feature. Typically 'enabled' and 'control'.
   If you've feature flagged code that is executed immediately when the bundle loads like module exports or React component PropTypes,
   you won't be able to stub out the feature util before the code is executed meaning only the feature flag's 'control' state can be tested.
   This can be addressed by temporarily restructuring code to a format similar to that in: `examples/feature-flagging-module-exports`.

1. Changes to feature flag values in .env aren't reflected until the server is restarted.

#### How feature flags are loaded in QA

todo

#### How feature flags are loaded in Production

todo

#### Feature flag variants

Each feature flag comes with 2 variants (although these can be expanded to your needs) -
`enabled` and `control`. This should be thought of in terms of experimentation, where `control` is,
you guessed it - control. For most purposes, you should set control to false and set enabled to
true or false as needed.

