Flags guides
12 · Flag cleanup

Remove flags you’re done with.

Every release flag is temporary. A short routine keeps your code and your config free of switches nobody flips.


1

Temporary or permanent

Give each setting a lifecycle. temporary is for release flags and experiments that should go once they are at 100% or 0%. permanent is for kill switches, plan entitlements and remote settings that stay. Set an owner too, so someone gets asked. Filter the flag list by either.


2

Find the ones that are done

A temporary flag is done when it serves one plain value in every environment, with no rules or split, and nobody has touched it for a while. The flag list shows every environment's state at a glance; this script lists the candidates:

stale-flags.ts
import { Flags } from "@productcraft/flags";
const flags = new Flags(); // PRODUCTCRAFT_API_KEY + PRODUCTCRAFT_WORKSPACE_ID

declare const configId: string;
declare const productionId: string;

// Temporary flags that serve one plain value in every environment, with
// no rules, unchanged for 30 days: candidates to remove from the code.
const cutoff = Date.now() - 30 * 24 * 3600 * 1000;
for await (const row of flags.configs.environments.values.list(configId, productionId, { include: "states" })) {
  const states = Object.values(row.states ?? {}); // the compact state in every environment
  const first = states[0];
  const settled = first !== undefined && states.every((st) => !st.has_rules && st.value === first.value);
  if (row.setting.lifecycle === "temporary" && settled && Date.parse(row.value.updated_at) < cutoff) {
    console.log(`${row.setting.key}: serves ${String(first.value)} everywhere since ${row.value.updated_at}`);
  }
}

3

Code first, then Flags

  1. Remove every read of the flag from your code, keeping the branch it serves, and deploy.
  2. Delete the flag in Flags. Do it in this order: an app still reading a deleted flag gets its default (reason ERROR, FLAG_NOT_FOUND), which may be the old behaviour.

Prerequisites and segments that point at the flag block the delete (409 SETTING_IN_USE); the flag page lists them. The delete is kept in History with your note.

delete.ts
import { Flags } from "@productcraft/flags";
const flags = new Flags(); // PRODUCTCRAFT_API_KEY + PRODUCTCRAFT_WORKSPACE_ID

declare const settingId: string;

const setting = await flags.settings.retrieve(settingId);
// Prerequisites and segments that point at it block the delete (409).
console.log(setting.dependents);
await flags.settings.delete(settingId, { note: "new_checkout shipped to 100%; code path removed in #812" });