CI/CD·7 min read·

Feature Flags: Shipping Half-Built Features Without Breaking Trunk

Ship incomplete features safely by hiding them behind flags, so every merge stays green and the trunk is always deployable.

NB

Netbay Developer Relations

Netbay Engineering

On this page

Feature flags are the enabler that makes trunk-based development practical for teams building large features. Without them, a feature that takes six weeks forces you to either live on a long branch (and bury your pipeline in conflicts) or merge half-finished work that breaks users. With flags, you merge every day and the new code is simply dormant until you turn it on.

What a flag actually is

A feature flag is a runtime switch checked in code. When off, existing behavior runs; when on, the new behavior runs. The switch value can live in an environment variable, a config file, a database row, or a dedicated flag service. The key rule: the flag's effect must be isolated to new code paths, so flipping it changes behavior without redeploying.

Here is the simplest implementation, a flag backed by an environment variable:

python
import os

def checkout_flow(user):
    if os.getenv("NEW_CHECKOUT") == "on":
        return new_checkout_flow(user)
    return legacy_checkout_flow(user)

The trunk stays green because both paths exist and tests cover both. You can ship the new code, run it in shadow mode, and only switch the environment variable when you are ready.

Types of flags

  • Release flags gate unfinished features until they are safe to expose.
  • Experiment flags toggle variants for A/B testing.
  • Ops flags act as kill switches for risky subsystems in production.
  • Permission flags enable features for specific teams or beta users first.

Release flags are what make CI happy: they decouple deploy frequency from release frequency. You can deploy every merge to production and still control exactly what customers see. That is the whole point — deployment and release become independent concerns.

Flag hygiene and dead code

Unbounded flags rot. Once a feature is fully released, keep a short window, run weeding, and remove the old path. Every stale flag is dead code, extra test surface, and a place for bugs to hide. Adopt a simple lifecycle: flag added -> rolled out -> verified -> removed. Capture the removal as a ticket attached to the original flag so nobody forgets.

Some teams use a config schema and a rollout-aware helper so flags are discoverable and typed:

typescript
export interface Flags {
  newCheckout: boolean;
  v3Search: boolean;
  betaBilling: boolean;
}

const DEFAULTS: Flags = { newCheckout: false, v3Search: false, betaBilling: false };

export function loadFlags(source: Partial<Flags>): Flags {
  return Object.assign({}, DEFAULTS, source);
}

Typing flags catches typos at compile time and makes the set of switches explicit for any engineer joining the team.

new feature code shipped flag? runtime new path (on) old path (off) both branches live in the same deployed artifact flipping the switch never triggers a redeploy

Flags and your CI pipeline

The most important pipeline contribution of flags is this: every commit on trunk stays deployable. CI builds and tests the whole artifact, both feature states, so there is never a "mostly merged" build that works only in theory. In tests, exercise both flag states where practical — a helper that toggles the flag for a test is more valuable than testing only the default.

When flags are enough and when they are not

Flags solve release sequencing beautifully but are not for everything. Database schema changes that must run before the flag flips need separate planning (see the migrations post). Large refactors that touch shared code still need review discipline. And teams new to flags should start with ten or fewer active flags, not a zoo of thirty switches, so the system stays auditable.

Takeaway: feature flags decouple deploy from release, let you merge half-built features onto a green trunk daily, and shrink the risk of every release. You can exercise the whole cycle — build, flag-check, deploy — by running your pipeline and hosting on a Netbay VPS from Lucknow DC01, spinning one up in under 60 seconds at netbayhosts.in.

Keep reading

Follow along on a real VPS

Deploy Linux in under 60 seconds

These guides are written against Ubuntu, Debian, and RHEL-family images — the same ones on NetBay.

Deploy an instance