# Feature Flags: Deploying on Friday Without Fear
By Govind Bajaj · 2025

Tags: feature-flags, deployment, methodology, devops, software-engineering
I deployed a major checkout refactor on a Friday afternoon. It went to 5% of users. Rolled back 20 minutes later. Zero downtime. No panic. Here's how feature flags make this possible.
I deployed a major checkout refactor on a Friday afternoon.

It went to five percent of users. I monitored error rates for twenty minutes. The error rate ticked up slightly. I rolled it back with one click. Zero downtime. No panic. No weekend work.

This is what feature flags make possible. They separate deployment from release. Code ships to production but stays invisible. You turn it on for a small percentage of users. You monitor. You increase the percentage or you turn it off. All without a new deployment.

## The Simplest Feature Flag System

You do not need LaunchDarkly or Unleash to start. A database table and an API endpoint are enough.

```ts
// models/FeatureFlag.ts
import mongoose from "mongoose"

const FeatureFlagSchema = new mongoose.Schema({
  name: { type: String, required: true, unique: true },
  enabled: { type: Boolean, default: false },
  rolloutPercentage: { type: Number, default: 0 }, // 0-100
  targetUsers: [{ type: String }], // Specific user IDs
})

export const FeatureFlag = mongoose.model("FeatureFlag", FeatureFlagSchema)
```

## Checking Flags in Code

```ts
// lib/features.ts
import { FeatureFlag } from "@/models/FeatureFlag"

export async function isEnabled(
  flagName: string,
  userId?: string
): Promise<boolean> {
  const flag = await FeatureFlag.findOne({ name: flagName })
  if (!flag) return false
  
  // Fully enabled
  if (flag.enabled && flag.rolloutPercentage === 100) return true
  
  // Percentage rollout
  if (flag.enabled && flag.rolloutPercentage > 0) {
    const hash = hashUserId(userId || "anonymous")
    return hash % 100 < flag.rolloutPercentage
  }
  
  // Targeted users
  if (flag.targetUsers?.includes(userId)) return true
  
  return false
}
```

## Using in Components

```tsx
// app/checkout/page.tsx
import { isEnabled } from "@/lib/features"

export default async function CheckoutPage() {
  const useNewCheckout = await isEnabled("new-checkout-flow", session?.user?.id)
  
  return useNewCheckout ? <NewCheckout /> : <OldCheckout />
}
```

## The Friday Deployment Workflow

Step 1: Deploy the code with the feature flag off. The new code is in production but inactive.

Step 2: Enable the flag for 5% of internal users. Test manually.

Step 3: Enable for 5% of real users. Monitor error rates, latency, and business metrics for 30 minutes.

Step 4: If metrics look good, increase to 25%, then 50%, then 100% over several hours or days.

Step 5: If anything goes wrong, disable the flag. Instant rollback without a deployment.

Step 6: After the feature is stable for a week, remove the flag and old code.

## When to Use Feature Flags

Use for: large refactors, new payment flows, UI redesigns, experimental features, and performance optimizations.

Do not use for: security patches (deploy immediately), bug fixes (deploy immediately), or small changes that are low risk.

## Flag Cleanup Discipline

Temporary flags become permanent flags if you do not clean them up. Set a reminder to remove flags after a week of stability. Code review checklists should include: "Is this flag still needed?"

## Takeaways

- Feature flags separate deployment from release — code ships but stays invisible
- Start with a simple database table, not a paid service
- Percentage rollouts let you test with real users at low risk
- One-click rollback without deployment is the safety net that enables Friday deploys
- Monitor error rates, latency, and business metrics during rollout
- Clean up flags after the feature is stable — temporary becomes permanent without discipline
- The ability to turn off a broken feature in 10 seconds is worth more than any deployment process