Adopting GitHub Flow with
Feature Flags and Flux
Using Next.js 14.1 and React
Challenges
of modern software development
Github Flow
Overview
Key concepts
* d3abcde (HEAD -> main, tag: 1.0.0) Merge PR Implement feature XYZ'
|\
| * 9a8b7c6 Implement feature XYZ
|/
* 1a2b3c4 Initial commit
PROS
* Rapid Integration
* Reduced branch complexity
* Prevent the accumulation of long-lived feature branches
CONS
* Can be challenging
* Potential risk of breaking changes
* Potential Incomplete features
Feature Flags
Gradual rollouts, experimentation, reduced risk
Flux CD
Versioning and Environment Variable Storage
spec:
values: DEV
app:
image:
repository: iccacr.azurecr.io/frontend
tag: 2.5.2-unstable
envs:
VERSION: 2.5.2-unstable
FLAG_ENABLE_DOCUMENTS_LIST: "true"
FLAG_ENABLE_PROMO_HERO: "true"
FLAG_ENABLE_BEST_BITRATE: "true"
spec:
values:
app:
image:
repository: iccacr.azurecr.io/frontend
tag: 2.5.1
envs:
VERSION: 2.5.1
FLAG_ENABLE_DOCUMENTS_LIST: "false"
FLAG_ENABLE_PROMO_HERO: "false"
FLAG_ENABLE_BEST_BITRATE: "false"
spec:
values: DEV
app:
image:
repository: iccacr.azurecr.io/frontend
tag: 2.5.2-unstable
envs:
VERSION: 2.5.2-unstable
FLAG_ENABLE_DOCUMENTS_LIST: "true"
FLAG_ENABLE_PROMO_HERO: "true"
FLAG_ENABLE_BEST_BITRATE: "true"
spec:
values: PRD
app:
image:
repository: iccacr.azurecr.io/frontend
tag: 2.5.1
envs:
VERSION: 2.5.1
FLAG_ENABLE_DOCUMENTS_LIST: "false"
FLAG_ENABLE_PROMO_HERO: "false"
FLAG_ENABLE_BEST_BITRATE: "false"
Next.js & React
Using Flags on Server/Client-side components
// ‘@/utilities/featureFlags.ts’
import { convertStringToBoolean } from
SSR
'@/helpers/dataVariableHelper’;
const { FLAG_ENABLE_ACCORDIONS } = process.env;
// expose only the ones you want
// ‘@/components/TextBlock/index.tsx’
const featureFlags: Record<string, boolean> = {
FLAG_ENABLE_ACCORDIONS: import { featureFlags } from
convertStringToBoolean(FLAG_ENABLE_ACCORDIONS||'false') '@/utilities/featureFlags.ts’;
};
export { featureFlags }; const TextBlock = async({ ...data }:ComponentProps) => {
const { FLAG_ENABLE_ACCORDIONS } = featureFlags;
const entity = await getEntity(‘text', slug);
return (
<>
<Markdown text={entity.body}/>
{ FLAG_ENABLE_ACCORDIONS &&
<Accordion entity={entity.parts}/>
}
</>
);
};
// ‘@/contexts/featureFlagsContext.tsx’
'use client';
CSR
import React, { createContext } from 'react';
type FeatureFlagsContextType = Record<string, boolean>;
export const FeatureFlagsContext =
createContext<FeatureFlagsContextType>({});
// ‘@/app/layout.tsx’
export const FeatureFlagsProvider = ({
featureFlags,
import { featureFlags } from '@/utilities/featureFlags’;
children,
}: {
const RootLayout = ({ children }: { children:
featureFlags: FeatureFlagsContextType;
React.ReactNode }) => {
children: React.ReactNode;
return (
}) => {
<html lang={process.env.LANGUAGE ?? 'en'}>
return
<head>
<FeatureFlagsContext.Provider value={featureFlags}>
<Favicon />
{children}
</head>
</FeatureFlagsContext.Provider>;
<body>
};
<FeatureFlagsProvider featureFlags={featureFlags}>
{children}
</FeatureFlagsProvider>
</body>
</html>
);
};
export default RootLayout;
// ‘@/hooks/useFeatureFlags.ts’ CSR
import { FeatureFlagsContext } from
'@/context/featureFlagsContext’;
import { useContext } from 'react';
export const useFeatureFlags = (): Record<string, boolean> =>
useContext(FeatureFlagsContext);
// ‘@/components/FavoriteTeam.tsx’
import { useFeatureFlags } from'@/hooks/useFeatureFlags';
const FavoriteTeam = () => {
const { FLAG_ENABLE_FAVORITE_TEAM } = useFeatureFlags();
return (
<>
{FLAG_ENABLE_FAVORITE_TEAM && (
<FavoriteTeamView />
)}
</>
);
};
export default FavoriteTeam;
Thank you!
Q&A time