New features are introduced to Coral in small increments. With small increments, it likely that a certain feature is not working 100% from the end user point of view. One approach to the problem would be to build features in a feature branch and merge the feature branch to main after in being complete. However, that approach will lead into the need of constant rebasing. Instead of using feature branches, we decided to implement feature flag functionality.
Feature flags enable Coral developers to merge incomplete pieces of a feature features to main without breaking the user experience of a production user.
The feature flags are controller in vite.config.ts
as build time environment variables. We can choose in which modes the flag should be toggled on. For example, when Coral is run within the Core (Springboot), we want to toggle the flag off. However, development we want to toggle the feature on.
FEATURE_FLAG_EXAMPLE_FLAG: ["development", "remote-api"].includes(mode).toString()
Introducing a new feature flag requires only a few steps.
Define an environment variable represents a flag in vite.config.ts. Prefix your name with FEATURE_FLAG_
.
Example:
"process.env": {
ROUTER_BASENAME: getRouterBasename(environment),
API_BASE_URL: getApiBaseUrl(environment),
FEATURE_FLAG_EXAMPLE_FLAG: ["development", "remote-api"]
.includes(mode)
.toString()
}
Add the environment variable to be part of FeatureFlag
enum defined in coral/src/services/feature-flags/types.ts like this:
// src/services/feature-flags/types.ts
enum FeatureFlag {
FEATURE_FLAG_EXAMPLE_FLAG = "FEATURE_FLAG_EXAMPLE_FLAG",
}
If you're developing a feature that required adding a new route, you can use our helper.
- Add your new route
- Add your route in router-utils.ts
enum Routes {
TOPICS = "/topics",
// ...
YOUR_NEW_ROUTE = "/your-new-route",
}
Then add your route and the page component (in this example <YourPageElement />
) to router.tsx, using our helper:
import { createRouteBehindFeatureFlag } from "src/services/feature-flags/route-utils";
const routes: Array<RouteObject> = [
{
path: Routes.TOPICS,
element: <Topics />,
},
// ...
createRouteBehindFeatureFlag({
path: Routes.YOUR_NEW_ROUTE,
element: <YourPageElement />,
featureFlag: FeatureFlag.FEATURE_FLAG_EXAMPLE_FLAG,
// Routes.TOPICS is an example, you can use any
// existing route that makes the most sense in
// your case
redirectRouteWithoutFeatureFlag: Routes.TOPICS,
}),
//...
];
You can access the feature flag in your components etc. like this.
// Component.tsx
const exampleFlagActive = useFeatureFlag(FeatureFlag.FEATURE_FLAG_EXAMPLE_FLAG);
If you want to test your new feature, you have to add the feature flag in the test environment. To avoid adding env variables in tests or in various code files, we provide the helper setupFeatureFlagMock
. This function will mock a specific feature flag and enable you to test the expected behavior.
You can mock use the helper in tests like this:
import { setupFeatureFlagMock } from "src/services/feature-flags/test-helper";
describe("your test description", () => {
beforeAll(() => {
setupFeatureFlagMock(FeatureFlag.FEATURE_FLAG_EXAMPLE_FLAG, true);
});
// ...
});
Note: You have to setup the feature flag mock in every render scope, e.g. in beforeAll, beforeEach or inside your it
. You can only set one feature flag active in one scope.