Feature
Toggle
Driven
Development
Igal Steklov

CEO & FED
Cheers
🍻
Good Morning
☕










Deploy to Production?
How Often Do You
How stable is it?
TDD
Test Driven Development
CI/CD
Continuous Integration & Deployment
How do you control when code is exposed to users?

Feature Toggles
you need...

Feature Toggles?
What are


Feature Toggles
(or Feature Flags)
import React, { Component } from 'react';
import featureToggles from 'services/featureToggles';
class ExampleComp extends Component {
render () {
return featureToggles.isEnabled('my_new_feature')
? <a href="...">Click me for new feature</a>
: <p>New feature is coming soon</p>
}
}
services/featureToggles.js
import bulletTrain from 'bullet-train-client';
featureToggles.isEnabled('my_feature') // true | false
import bulletTrain from 'bullet-train-client';
const config = {
environmentID: process.env.BULLET_TRAIN_ENV_ID
};
class FeatureToggles {
// Called once app loaded and user data retrieved
init(user) {
bulletTrain.init(config);
bulletTrain.identify(`${user.id}_${user.fullName}`);
}
// Called on logout from our app
destroy() {
bulletTrain.logout();
}
isEnabled(featureToggle) {
return bulletTrain.hasFeature(featureToggle);
}
}
export default new FeatureToggles();
[
{
"id":3000,
"feature":{
"id":818,
"name":"notes_list",
"created_date":"2019-05-25T15:22:26.058231Z",
"initial_value":null,
"description":null,
"default_enabled":true,
"type":"FLAG",
"project":325
},
"feature_state_value":null,
"enabled":true,
"environment":814,
"identity":null
},
{ ... },
{ ... }
]
~ curl 'https://api.bullet-train.io/api/v1/flags/' -H 'X-Environment-Key: BgwB3E6'
It's just a simple JSON
<body data-ft-new_design>
...
</body>
.button {
font-size: 12px;
}
body[data-ft-new_design] .button {
font-size: 18px;
}
Also in your CSS
.button {
font-size: 12px;
@include when-feature-enabled("new_design") {
// Compiles to body[data-ft-new_design] .button
font-size: 18px;
}
}
Or SASS...
New Dev
Workflow
Driven by Feature Toggles
1.
Start with creating a FEATURE TOGGLE
2.
Block entry points with conditions
import React, { Component } from 'react';
import { NavLink } from 'react-router-dom';
import featureToggles from 'services/featureToggles';
class Navigation extends Component {
render () {
return (
<nav>
<ul>
<li><NavLink to="/">Homepage</NavLink></li>
<li><NavLink to="/reports">Reports</NavLink></li>
{featureToggles.isEnabled('notes_list') &&
<li>
<NavLink to="/notes">
Notes
</NavLink>
</li>
}
</ul>
</nav>
);
}
}
import React, { Component } from 'react';
import history from 'utils/history';
import featureToggles from 'services/featureToggles';
class NotesList extends Component {
//UNSAFE_componentWillMount
constructor(props) {
super(props);
if (!featureToggles.isEnabled('notes_list')) {
history.push('/error-page/404');
return;
}
}
render () {
return null;
}
}
3.
Work on your feature,
deploy to Production
import React, { Component } from 'react';
import history from 'utils/history';
import featureToggles from 'services/featureToggles';
class NotesList extends Component {
//UNSAFE_componentWillMount
constructor(props) {
super(props);
if (!featureToggles.isEnabled('notes_list')) {
history.push('/error-page/404');
return;
}
}
render () {
return (
<div>
Notes list component is under development,
so no user reach current component, yay 🎉.
</div>
);
}
}

4.
Share work progress with PMs & QA

5.
Controlled Release of Features,
Bug Fixes
or Infra Refactoring
AB Test or just Release
6.
Monitor Production: Biz & Tech KPIs





It's OK to...

What's so Great About FTs?
-
Better collaboration with PMs, QA and other stakeholders.
-
Ability to gradually rollout sensitive changes.
- Quickly resolve Production issues by closing FT (aka Kill Switch).
- Verify code works on Production, daily.
- No need for staging if you have tests – auto-deploy to Prod when CI passes.
- Better code – everything is "Production Code"
Tips
1.
Feature Toggle
Any Non-trivial
Code Change
- Tiny feature? YES!
- Bug fix? YES!
- Internal infra change? YES!
2.
It's OK to FT
Entire Components
import React, { Component, Fragment } from 'react';
import { Switch, Route } from 'react-router-dom';
import featureToggles from 'services/featureToggles';
import DashboardPage from 'containers/DashboardPage';
import ListPage from 'containers/ListPage';
import ListPageV2 from 'containers/ListPageV2';
export default class App extends Component {
render() {
return (
<main>
<Switch>
<Route path="/" component={DashboardPage}/>
<Route path="/list" render={() => featureToggles.isEnabled('list_page_v2')
? <ListPageV2/>
: <ListPage/>
}/>
</Switch>
</main>
);
}
}
3.
Remember the Backend APIs
import axios from 'axios';
import featureToggles from 'services/featureToggles';
export function listNotes() {
const apiVersion = featureToggles.isEnabled('notes_api_v2')
? 'v2'
: 'v1';
return axios.get(`api/${apiVersion}/notes/`);
}
4.
Plan Feature Toggle so that they can be turned off if needed
class NotesList extends Component {
render () {
const { notes = [] } = this.props;
if (featureToggles.isEnabled('notes_list')) {
return (
<ul>
{notes.map(note =>
<li key={note.id}>{note.text}</li>
)}
</ul>
);
}
return (<p>Notes are coming soon</p>);
}
}
class NotesList extends Component {
render () {
const { notes = [] } = this.props;
const isNotesExist = notes.length > 0;
if (isNotesExist || featureToggles.isEnabled('notes_list')) {
return (
<ul>
{notes.map(note =>
<li key={note.id}>{note.text}</li>
)}
</ul>
);
}
return (<p>Notes are coming soon</p>);
}
}
5.
Cleanup your Feature Toggles after release
class NotesList extends Component {
render () {
const { notes = [] } = this.props;
const isNotesExist = notes.length > 0;
if (isNotesExist || featureToggles.isEnabled('notes_list')) {
return (
<ul>
{notes.map(note =>
<li key={note.id}>{note.text}</li>
)}
</ul>
);
}
return (<p>Notes are coming soon</p>);
}
}
class NotesList extends Component {
render () {
const { notes = [] } = this.props;
return (
<ul>
{notes.map(note =>
<li key={note.id}>{note.text}</li>
)}
</ul>
);
}
}
Step
1
Step
2
Step
3
-
Easy to Start
-
Safer Deploys
-
Stable Production
-
PMs & QA ❤️ You
-
Infra for A/B Tests
Thank You :)
Igal Steklov
igal@webiya.co.il
+972-54-6490123

Slides at: tiny.cc/FTDD
Questions?

FTDD - Feature Toggle Driven Development
By Igal Steklov