The feature flag decision record: why the lifecycle model you chose determines your configuration surface and your dead-code accumulation rate
Feature flag decisions are made in a sprint planning session or a founding infrastructure session when the team needs to gate a risky release, enable a gradual rollout, or run an A/B experiment without deploying new code. The AI session that produces the feature flag decision is practical and tightly scoped: it evaluates LaunchDarkly against Unleash against a homegrown toggle system, notes that the existing CI/CD pipeline already has a deployment slot for environment variables, and concludes in favor of whatever is fastest to wire up and start using. The session also produces the first flag: a boolean that controls whether the new checkout redesign is visible to users, set to false in production until the QA cycle is complete. The decision is immediate, correct, and efficient.
What the AI session does not produce is the feature flag system's second half. The session answers "which platform?" and creates the first flag. It does not ask: what is the maximum lifetime of a temporary flag before it must be retired? Who owns a flag when the engineer who created it leaves the company or the team that created it is reorganized? What is the difference between a temporary release flag and a permanent entitlement flag, and does that distinction affect how the flag's targeting rules are maintained? What is the performance budget for flag evaluations per request, and which code paths are prohibited from evaluating flags because their throughput makes even microsecond overhead visible in the p99? What is the procedure for retiring a flag — not just setting it to a permanent value, but removing the evaluation call from the code, deleting the dead code path on the inactive side, and removing the flag from the system? Each of these questions has an answer that is not derivable from "we use LaunchDarkly" or "flags are in our infrastructure repository." Each answer determines the rate at which the flag system accumulates configuration debt, the surface area of flag behavior that is undocumented in any system an engineer can consult, and the probability that deleting what looks like a stale flag causes an outage for a customer cohort whose dependency on that flag exists only in a closed Slack thread. The answers exist in the AI sessions. They are the operational commitments behind the platform choice. They are almost never written down.
Two ways feature flag lifecycle decisions produce the wrong outcome
The zombie flag story
A forty-person SaaS company adopts LaunchDarkly in their second year to support progressive feature rollouts. The initial use case is clear and the benefit is immediate: instead of deploying a new feature to all users simultaneously and rolling back on the first sign of problems, the team gates new features behind flags, routes five percent of users to the new path, monitors error rates and conversion metrics for forty-eight hours, and increases the percentage incrementally. Over the following three years the practice becomes standard. Every significant feature ships behind a flag. The flag dashboard grows to two hundred and thirty-seven flags. The team grows from twelve engineers to forty. Three engineering managers and two engineering directors have cycled through. Twenty-one of the original engineers have left the company.
A new senior engineer joins and spends their first two weeks doing a codebase orientation. They open the LaunchDarkly dashboard and sort flags by last-modified date. They find forty-three flags that have not been modified in more than six months. They find a flag called legacy_checkout_flow that has been set to false in production for eleven months and appears in a code path that is clearly the pre-redesign checkout flow. The code behind the true branch is the old three-step checkout with sequential form submission. The code behind the false branch is the redesigned single-page checkout with async order submission that shipped eleven months ago. To the new engineer, legacy_checkout_flow looks like a completed rollout: the old code is behind the true branch, the new code is the false branch, production has been on false for eleven months, and the old code is dead. They file a cleanup ticket, remove the flag evaluation call from the code, delete the old checkout code path, and remove the flag from LaunchDarkly in a PR that gets reviewed and approved by two engineers who also read legacy_checkout_flow: false for 11 months as "completed rollout, safe to delete."
The change deploys on a Tuesday morning. By 9:47am, the customer success team has received eleven support tickets from enterprise customers reporting that their checkout integrations are failing. By 10:15am, twenty-three enterprise accounts are confirmed affected. By 10:30am, the incident commander has identified the change and the rollback is in progress, but the rollback cannot restore the deleted code path — the PR deleted it. The fix requires manually reverting the code deletion from git history, a process that takes until 12:44pm. Total impact: three hours and fourteen minutes of broken checkout for twenty-three enterprise accounts, totaling approximately $140,000 in daily GMV across those accounts during the affected window.
The root cause is not the engineer's mistake. The root cause is a flag that was created as a release flag but was being used as a compliance kill switch, documented only in a Slack thread from the original configuration date. The full context: four enterprise accounts had contractual commitments to the legacy checkout flow because their procurement teams had completed a SOC 2 audit against the legacy flow's data handling model. The new checkout redesign changed the session token scope in a way that the four accounts' compliance teams were not ready to re-audit. The engineering team and the customer success team reached an agreement to keep these four accounts on the legacy flow for up to eighteen months while the compliance teams worked through the re-audit. The mechanism they chose was legacy_checkout_flow: true for a named user segment targeting those four accounts. The flag was then set to false for all other users as the general rollout completed, giving the dashboard the appearance of a completed rollout at the population level while four account-level overrides kept the flag functionally active for their compliance use case. The LaunchDarkly targeting rules showed this clearly if you opened the flag's targeting panel rather than just looking at the default value — but the default value was false, and the cleanup assessment that led to the deletion did not involve opening the targeting panel, because there was no documented procedure that required reviewing targeting rules before flag deletion.
The incoming technical leader who reviews this incident in retrospect will find a flag that looked like a completed rollout from the outside and was actively serving a compliance function that four enterprise accounts depended on. They will find no document that distinguished compliance kill switches from release flags. They will find no procedure that required examining targeting rules before deleting a flag. They will find a Slack thread from eleven months ago in which a customer success manager and an engineering manager agreed on the compliance arrangement — a thread that is not linked from the LaunchDarkly flag, not present in any code comment near the flag evaluation call, and not surfaced by any search that starts from "is it safe to delete this flag?"
The flag proliferation latency story
A developer tools startup with sixteen engineers adopts feature flags during their Series A growth phase. The decision is made in an AI session focused on reducing deployment risk: the team is shipping multiple times per week and a bad deploy has twice caused multi-hour outages that churned paying customers. Feature flags are the standard answer to this problem, and the session produces a clear decision: adopt LaunchDarkly for progressive rollouts, start with five flags for the five features currently in development, evaluate in ninety days. The five flags work correctly. The team expands the practice. Eighteen months later the flag catalog contains one hundred and twelve flags. The team has grown from sixteen to thirty-one engineers.
A performance audit triggered by a customer complaint about API response times produces a breakdown of where their 340ms average p99 is coming from. The breakdown reveals 22ms attributed to flag evaluation overhead across the hot request path. The engineering team's first reaction is confusion — LaunchDarkly evaluates flags against a local rules store, not via a remote API call, and local evaluation should complete in microseconds. The investigation reveals the source: the LaunchDarkly SDK is initialized correctly and does use local evaluation, but forty-seven flags are evaluated on every single API request in the main request handler, in a sequence accumulated incrementally as engineers added new flag evaluations to the top of the handler without any review gate on the handler's total flag evaluation count. At the SDK level, each individual evaluation takes approximately 0.2 to 0.4 microseconds. Forty-seven evaluations per request at 0.3 microseconds each is 14 microseconds — negligible. The actual 22 milliseconds is coming from something else: eighteen of the forty-seven flags evaluated in the handler are evaluating against user context attributes that require a database read to populate. The user's plan tier, the user's account age, and the user's cohort assignment are all stored in the database, and the request handler was populating these context attributes with a synchronous database read at the top of every request to provide context for flag evaluation, regardless of whether the specific request's code path would encounter any of the eighteen context-dependent flags. The database read was introduced incrementally, one context attribute at a time, each introduced by an engineer adding a new context-dependent flag to the handler and adding the database read that flag required. No single addition was significant. The aggregate was a synchronous database read on every request that existed solely to populate feature flag evaluation context for flags that were only relevant to a subset of requests.
The performance audit also produces two secondary findings. First, forty-seven of the one hundred and twelve flags in the catalog are effectively permanent: set to true for all users with no targeting rules, not modified in more than ninety days, and gating code paths that have been in production for over a year. These flags are inline constants implemented as feature flag evaluations — they add evaluation overhead and codebase complexity without providing any operational flexibility, because no one would ever set them to false without a major code change to restore the old behavior. Second, twelve of the one hundred and twelve flags gate code paths where the inactive side is dead code: the false branch of a temporary flag that shipped as permanently true nine months ago has never been deleted, because the flag still exists and the dead code path is still reachable via a flag evaluation that always returns true. The dead code paths range from twelve lines to two hundred and forty lines. They are tested by the test suite through flag evaluation mocks that force the false branch, adding test surface area for code that will never run in production. Three of the twelve dead code paths contain references to deprecated library functions that generate deprecation warnings in the test output, suppressed with warning filters rather than fixed because the deprecation fix would require understanding whether the dead code path is safe to delete — a question no one has confidently answered because the flag lifecycle policy does not distinguish between "permanently true and safe to delete" and "temporarily true with a future rollback plan." The fix for the latency regression requires three weeks of engineering time: audit all forty-seven handler flags against the request types that actually need them and move context-dependent flag evaluations to the specific code paths that require them, retire the forty-seven effectively-permanent flags by inlining their values as constants, and delete the twelve dead code paths after confirming the flags are permanently true.
The technical debt accumulation model that produced this outcome is structurally identical to the one that produces other categories of invisible technical debt: each individual addition was reasonable under the information available at the time; the aggregate, visible only in the performance audit eighteen months later, represents a significant architectural liability. The difference between feature flag debt and other categories of technical debt is that feature flag debt is actively generated by a system the team is using correctly for its intended purpose — progressive rollouts — and the debt accumulates as a side effect of correct use rather than as a result of deliberately deferred work.
Three structural properties that feature flag lifecycle decisions determine
The flag lifecycle model and the dead-code accumulation rate
A feature flag system without a lifecycle policy is a flag creation system, not a feature management system. Flags are created at a steady rate determined by the team's feature velocity; they are retired at a rate determined by the explicit allocation of cleanup effort, which in the absence of a policy defaults to zero. The accumulation rate is linear in the flag creation rate and the retirement rate is effectively zero, producing a flag catalog that grows without bound until the catalog becomes unmaintainable or a deletion incident surfaces the latent risk. The lifecycle policy is the mechanism that drives the retirement rate from zero to a rate that matches or exceeds the creation rate at steady state.
The lifecycle model must distinguish between flag types because different flag types have fundamentally different lifecycle expectations. A temporary release flag is created to gate a specific feature during a rollout period; its lifecycle ends when the rollout is complete, and the cleanup — removing the evaluation call, deleting the dead code path, removing the flag from the system — is the natural completion of the original release work. A permanent feature entitlement flag controls which subscription tier or account type has access to a feature; its lifecycle ends only when the entitlement model changes and the flag is superseded by a different access control mechanism. A kill switch is created to enable emergency disabling of a feature that poses an operational or compliance risk; its lifecycle is indefinite and its targeting rules require active maintenance as the operational risk profile evolves. An experiment flag is created for an A/B test with a defined hypothesis and a defined conclusion date; its lifecycle ends when the experiment produces a decision, at which point one branch is selected as permanent, the other branch's code is deleted, and the flag is retired. Without documented type taxonomy, all flags look the same in the dashboard, and engineers making cleanup decisions cannot distinguish between a release flag whose deletion is safe and a kill switch whose deletion would disable a safety mechanism.
The dead-code accumulation rate is the downstream consequence of the retirement rate. A temporary release flag that is permanently set to true after a successful rollout but never retired leaves behind the code path on the false side. That code path is compiled, deployed, and tested on every release cycle. It may contain references to dependencies maintained solely to support the dead path. It adds cognitive load to engineers reading the code, who must reason about what happens when the flag evaluates to a value that in practice it never evaluates to. It generates warnings or errors in static analysis tools that are suppressed rather than fixed because fixing them would require touching code that "we're not sure is safe to delete." The dead code path is invisible debt until a static analysis pass, a dependency upgrade that breaks the dead path without anyone noticing, or a performance-motivated cleanup surfaces it. The retirement policy that drives dead code deletion is the same policy that drives flag retirement: a documented procedure that requires removing the evaluation call and deleting the inactive code path as part of flag retirement, not as a separate cleanup task that is deprioritized indefinitely. The ADR lifecycle for feature flags is a supersession event: when a temporary flag is retired, the feature flag ADR should be updated to record the outcome — which branch became permanent, when the cleanup was completed, and what the permanent state of the feature is — so that engineers who encounter code that previously lived behind the flag can understand its history without reading git blame or Slack threads.
The targeting model and the configuration surface complexity
A feature flag's targeting model is the set of rules that determine which users or contexts evaluate to which flag value. The simplest targeting model is a global boolean: the flag is true for everyone or false for everyone. More complex models include percentage rollouts (a random sample of users evaluates to true), user attribute targeting (users whose plan tier is enterprise evaluate to true), segment targeting (users who are members of a named segment evaluate to true), and multi-variate rules that combine these. The targeting model complexity grows monotonically as rules are added to existing flags without a corresponding removal of superseded rules. A flag that started as a global boolean and has accumulated targeting rules over eighteen months may have: a default value of false, an override rule for a named beta segment, a percentage rollout rule for the general population set to 100%, three individual user overrides for customer success team members who need the feature for support purposes, and two account-level overrides for enterprise accounts with compliance constraints. Each rule was added for a valid reason at the time. The aggregate produces a flag whose effective behavior requires reading five separate rule sets to understand, and whose deletion risk cannot be assessed by looking at the default value alone.
The configuration surface is the set of flag rules that an engineer must understand to answer the question "what behavior will a given user see?" For a flag catalog with complex targeting, the configuration surface is not the number of flags but the number of rules across all flags. The configuration surface complexity determines: how long it takes a new engineer to understand the system's behavior for a given user context; how confident anyone can be that a proposed change to a flag's rules does not break a user segment they did not consider; and how likely a flag deletion is to have unintended consequences for user segments whose dependency on the flag is not visible from the default value. The configuration surface grows with flag count, with targeting rule accumulation per flag, and with the age of the flag catalog — older flags have had more time to accumulate rules added for reasons that are now partially or fully forgotten.
The targeting model governance policy must specify: what targeting complexity is allowed for each flag type (a temporary release flag should never have more than a percentage rollout rule and an emergency kill override; complex account-level targeting belongs in permanent entitlement flags); the procedure for reviewing and pruning targeting rules that are no longer serving their original purpose; and the documentation requirement for each non-default targeting rule — every override segment, every account-level exception, and every user-level override must have a documented rationale stored in the flag metadata, not in a Slack thread. The legacy_checkout_flow incident is a targeting complexity incident: the default value (false) accurately described the global configuration, but the four-account targeting override in the rule set — the configuration surface element that mattered for the deletion decision — was undocumented in any system visible to the engineer making the cleanup decision. The platform engineering decision record for the flag system must specify the documentation requirement for targeting rules as a platform contract, because flag targeting rules are a form of configuration-as-code that must be as readable and auditable as the code they control.
The flag evaluation overhead and the performance ceiling
The performance ceiling of a feature flag system is determined by three decisions made at flag system adoption time and modified by flag accumulation over the system's lifetime: the evaluation model (local rules store versus remote API call), the evaluation placement (where in the request lifecycle flags are evaluated), and the context population strategy (how the user or request context required for targeting rules is assembled). These three decisions interact to produce the system's per-request flag overhead, and the overhead grows as flags accumulate and context requirements expand unless the governance policy constrains flag placement and context population explicitly.
The evaluation model is the foundation. Server-side SDKs for commercial flag systems (LaunchDarkly, Unleash, Flagsmith) use a local rules store: the full flag rule set is downloaded at SDK initialization time and kept current via a streaming connection. Individual evaluations against the local store complete in tens of microseconds at most, with no network call. This evaluation model does not add meaningful per-request overhead regardless of the number of flags evaluated, provided the flags being evaluated do not require context attributes that must be fetched from a remote source. The performance ceiling under local evaluation is effectively unlimited for flag count alone. The ceiling emerges when evaluation context requires database reads, external API calls, or other synchronous operations that have meaningful latency. A flag that evaluates true or false based solely on a user attribute present in the session token or JWT claims (user ID, account tier, region) adds zero network overhead per evaluation. A flag that evaluates based on an attribute not present in the session — account-level configuration, subscription entitlement state, user cohort assignment — requires that attribute to be fetched from somewhere, and the fetch cost is the evaluation overhead, not the rule traversal cost.
The evaluation placement policy determines where in the request lifecycle flag evaluations occur. Evaluating all flags at the entry point of the main request handler — the pattern that accumulated in the latency regression story — concentrates the evaluation overhead at the most performance-sensitive location in the system and makes that overhead unconditional: every request pays the evaluation cost regardless of whether the specific request's code path will encounter any of the evaluated flags. The correct placement model is lazy evaluation: flags are evaluated at the point in the code where the evaluation result is needed, not at a centralized initialization point. Lazy evaluation ensures that a request that never reaches a flagged code path pays zero flag evaluation overhead. The constraint on lazy evaluation is context: if a flag requires context attributes that are expensive to assemble, and the flag is evaluated multiple times in a single request's code path, the context assembly cost is paid multiple times without caching. The policy for context-dependent flag evaluation in a hot code path must require: the context to be assembled once and cached for the request duration, the flag evaluation to be memoized if the same flag with the same context is evaluated more than once per request, and the context assembly cost to be assessed against the request's performance budget before the flag is placed in a high-throughput code path. The deployment strategy decision that uses feature flags for progressive rollout is directly coupled to the flag evaluation overhead: a flag used to route five percent of traffic to a new code path at deployment time is evaluated on every request, and its context assembly cost multiplies by the full request volume of the system, not just the five percent of requests that will take the new path.
The hot-path flag policy is the operational expression of the performance budget: a list of code paths classified as high-throughput (above a defined requests-per-second threshold) where flag evaluation is prohibited without explicit review and approval. The policy prevents incremental flag accumulation in hot paths by requiring a conscious decision at the point where the flag would be placed, rather than discovering the accumulated overhead in a performance audit eighteen months later. The performance budget must specify: the maximum number of flags evaluated per request in the main request handler; the maximum context assembly latency permitted for flags evaluated in high-throughput paths; and the monitoring signal that detects when a new flag's placement has crossed the latency budget — an alert, not a post-hoc audit.
Three AI session types that embed feature flag lifecycle decisions without documenting them
The release gating session is where the flag system is chosen and the first flag is created. The session evaluates flag platforms against the team's deployment risk profile, selects a platform, and configures the first flag for the feature currently in development. The session produces a clear answer to "which flag system?" and delivers an immediately usable release safety mechanism. What the session does not produce is the lifecycle policy: what is the maximum lifetime of this flag and the flags that will follow it? What happens when the rollout is complete — who is responsible for executing the cleanup, and what does the cleanup procedure require? How will a flag created today be evaluated for safety to delete by an engineer who joins the company in two years and has no context about why the flag was created? These questions are not part of "the feature flag system" in the way teams usually frame the decision; they are the operational commitments that determine whether the flag system is a release velocity tool or a configuration debt accumulator. The release gating session is where the debt accumulation begins, not because anything goes wrong in the session, but because the session answers the immediate question — "how do we ship this feature safely?" — and stops before the questions whose answers determine the long-term configuration surface. The open-source extractor surfaces these founding release gating sessions from AI chat history, recovering the platform selection rationale and the first flag's design — context that makes visible which operational commitments were implicit in the original decision and which were never addressed.
The A/B testing session is where targeting rules first become complex. The team needs to route a random sample of users to a variant, measure the variant's effect on a conversion metric, and roll back to the control if the variant underperforms. The AI session that designs the first experiment produces a percentage rollout rule, a holdout segment definition, and a metric collection approach. The session may also produce a defined experiment duration — "we'll run this for two weeks and make a decision." What the session does not produce is the experiment retirement policy: what is the procedure when the experiment concludes and a winner is selected? Who is responsible for promoting the winning variant to permanent, retiring the losing variant's code path, and removing the flag? In practice, the cleanup procedure for an experiment is conceptually simpler than creating the experiment but practically more likely to be deferred: the experiment concluded, the team made a decision, the winning variant shipped to everyone, and the flag retirement is a backlog item that competes with the next sprint's new features. The experiment that concluded nine months ago with a clear winner is now a flag that is permanently true with a complex targeting rule reflecting the experiment's holdout structure still intact, serving no operational purpose except as configuration debt. The decisions never written down problem is most acute for experiment flags: the experiment produced a decision — which variant won, what the measured effect was, what the confidence interval was — and that decision exists in the analytics dashboard from the experiment period, in the Slack thread where the team reviewed the results, and possibly in a PR description mentioning the experiment conclusion. It does not exist in the flag metadata as a structured outcome record that connects the flag's permanent state to the experiment that produced it.
The kill switch design session is where the first operational flag is created — a flag intended not for gradual rollout but for emergency disabling of a feature that poses a risk. Kill switches are architecturally correct for managing operational risk, but they require a governance model distinct from release flags and experiment flags. The AI session that designs the first kill switch produces a flag that is true (feature enabled) by default with a documented procedure for setting it to false in an incident. What the session does not produce is the kill switch lifecycle policy: how does the kill switch relate to the feature it controls — is it permanent infrastructure or a temporary safety measure for a feature in early production? What is the ownership model — who has permission to change it, under what circumstances, and through what approval process? What is the review cadence — how often does the team verify that the kill switch's targeting rules are still accurate and that the feature it controls still requires a kill switch? Kill switches designed without this governance accumulate silently over the system's lifetime as each new risky feature addition creates a new kill switch and existing ones are never reviewed for relevance. The kill switch review cadence — quarterly for high-stakes kill switches, annually for lower-stakes ones — is the policy that prevents kill switch accumulation and ensures that kill switches whose purpose has been superseded by architectural improvements are retired before their targeting complexity accumulates to the level where their behavior is undocumented and their deletion risk is unknown. The documentation strategy decision record for kill switches is a targeting rule documentation policy: every non-default targeting rule in a kill switch must have a documented rationale stored in the flag metadata, not in a Slack thread, for the same reason that operational runbooks cannot live in closed Slack threads — because the engineer who needs the information in an incident or a cleanup review will not find a Slack thread from eleven months ago.
The five sections of a feature flag decision record
The first section documents the flag system selection and the flag type taxonomy. The platform selection rationale must be specific and evaluatable: "LaunchDarkly was selected over Unleash self-hosted because the team of fourteen did not have the operational capacity to maintain a self-hosted flag service, and the additional cost of LaunchDarkly's hosted model at the team's flag volume was within the infrastructure budget" is evaluatable against "has the team grown to the scale where Unleash self-hosting is operationally feasible and cost-justified?" — the re-evaluation trigger that should accompany the platform selection. The flag type taxonomy must define at minimum four types: temporary release flags (created to gate a specific feature during rollout, maximum lifetime defined, cleanup procedure required); permanent entitlement flags (control feature access by subscription tier or account type, lifecycle tied to the entitlement model, active maintenance required); kill switches (emergency disable mechanism for operational or compliance risk, permanent or indefinite lifetime, quarterly review required); and experiment flags (created for a defined experiment with a documented hypothesis and conclusion date, retired when the experiment concludes). Each flag type must specify the creation requirements, the maximum lifetime or review cadence, the retirement trigger, and the retirement procedure. The section must also specify the metadata fields required at flag creation time: flag type, owner team, creation date, related ticket or PR, expected retirement date (for temporary and experiment flags), and retirement criteria. Without required metadata, the flag catalog becomes a list of names with no context for assessing whether individual flags are safe to retire.
The second section documents the flag lifecycle policy and retirement criteria. The lifecycle policy converts the flag type taxonomy into operational commitments. For temporary release flags: the maximum lifetime is specified in days or weeks (a common range is thirty to ninety days, depending on the team's rollout cadence); at the end of the rollout period, the flag owner is responsible for executing the retirement procedure or explicitly extending the flag's lifetime with a documented reason; the retirement procedure requires removing the flag evaluation call from the code, deleting the code path on the inactive side of the flag, removing the flag from the flag system, and updating any documentation or ADRs that referenced the flag. For experiment flags: the conclusion procedure specifies who is responsible for reading the experiment results, what criteria constitute a winner selection, and what the timeline is for promoting the winner to permanent after the experiment concludes; the code deletion for the losing variant must be scheduled as a concrete task at the time the winner is selected, not as a vague "eventually clean this up" intention. The lifecycle policy must also specify the zombie flag detection mechanism: the process that identifies flags that have exceeded their documented lifetime without being retired. For commercial flag systems, this can be implemented as a periodic job that reads flag creation dates and types from the flag system API and creates tickets for flags overdue for retirement. For teams without API access to flag system metadata, the zombie flag review is a quarterly manual audit of the flag catalog against the type and lifetime fields in the metadata. The audit produces a prioritized list of flags for retirement review, and each flag on the list is assessed individually before retirement — with the targeting rules examined rather than only the default value, because the legacy_checkout_flow incident is the canonical demonstration that default value alone is insufficient for safety assessment.
The third section documents the targeting model and the configuration surface definition. The targeting model policy specifies which targeting rule types are permitted for each flag type. Temporary release flags: permitted targeting rules are a percentage rollout and a single emergency kill override for the flag owner team; complex user-level or account-level targeting in a temporary flag is a signal that the flag's type classification is wrong — the behavior it controls belongs in a permanent entitlement flag, not a temporary release flag. Permanent entitlement flags: permitted targeting rules include subscription tier matching, account attribute targeting, and named segment assignment; each non-default rule must have a documented rationale stored in the flag metadata and a documented review date. Kill switches: targeting rules should be minimal — the operational use case is all-or-nothing, and complex targeting in a kill switch makes the kill switch less reliable as an emergency mechanism because the configuration surface to verify increases; account-level kill switch overrides are permitted but require documented approval and a time-bounded review. The section must also specify the configuration surface audit cadence: quarterly for flags with complex targeting, annually for simple boolean flags. The audit reviews each targeting rule to confirm it is still serving its documented purpose and has not been superseded by a system change that makes the rule obsolete. The quarterly targeting review of kill switches and permanent entitlement flags is the mechanism that would have confirmed the four-account compliance overrides in legacy_checkout_flow were still active and documented — making the deletion risk visible to any engineer who consulted the flag's documentation rather than only its default value.
The fourth section documents the evaluation performance budget and the hot-path flag policy. The performance budget must specify the maximum number of flags evaluated per request in the primary request handler, and the maximum context assembly latency permitted for flags in high-throughput code paths. Concrete values are better than principles: "a maximum of ten flags may be evaluated in the main request handler; each flag evaluated in the handler must use only context attributes available in the session JWT with no additional database or API reads; any flag requiring external context for evaluation must be placed at the specific code path that uses it, not in the handler" is a policy that a code reviewer can enforce. The hot-path flag policy must specify the code paths classified as high-throughput and the review gate required before a flag can be placed in one of them: a design review that documents the flag's evaluation context requirements, the expected evaluation frequency, and the performance impact assessment. The section must also document the SDK configuration requirements: the SDK version governance (minimum SDK version, upgrade cadence, policy for patches versus minor upgrades), the streaming connection configuration (reconnect behavior, fallback to last-known state on connection loss, alert configuration for extended disconnection), and the initialization behavior (flag system unavailability at process startup should fall back to safe defaults, not block startup). The performance budget is a living document — it must be updated when the system's traffic profile changes significantly and when the flag system SDK's evaluation model changes in a way that affects per-request overhead. The context assembly cost is the variable most likely to change over time: as the team adds new context-dependent flags, the attributes required for evaluation expand, and without a documented budget against which new attributes are assessed, the context assembly cost grows incrementally until a performance audit finds the 22-millisecond regression.
The fifth section documents the flag ownership model, the audit cadence, and the dead-flag detection mechanism. Flag ownership must be assigned to a team, not an individual — the owning team is responsible for the flag's lifecycle from creation to retirement, and the ownership must survive individual engineer departures and team restructurings. The ownership assignment must be stored in the flag system's metadata, not only in a spreadsheet or wiki page, so that the ownership is queryable from the flag system itself. The ownership transfer protocol specifies the procedure when the owning team is reorganized or the feature the flag controls is transferred to a different team: the outgoing team documents the flag's purpose, targeting model, expected behavior on both sides, and any account-level exceptions with a rationale; the incoming team reviews this documentation, confirms understanding, and accepts ownership by updating the metadata. The dead-flag detection mechanism uses automated signals where the flag system's API allows: a job that queries the flag system's evaluation log for flags with zero evaluations in the past thirty days; a job that diffs the flag catalog against the codebase's flag evaluation calls to identify flags no longer referenced in any deployed code; and a job that identifies flags whose default value has not changed in more than ninety days and whose targeting rules have no active modifications — the combination of signals that identifies a permanently static flag that belongs as a constant rather than a flag evaluation. Each job produces a list of candidates for retirement review, not a list of flags to delete immediately — the review step is required because absence of evaluation traffic may indicate dead code rather than a safely-retirable flag, and the distinction requires examining the codebase rather than only the flag system data. The quarterly flag audit reviews the outputs of all three detection mechanisms and produces a retirement plan for the current quarter: flags to retire, flags to extend with updated lifetime documentation, and flags to reclassify from temporary to permanent based on evidence that their original temporary classification was wrong.
The release gating session, the A/B testing session, and the kill switch design session each produce feature flag lifecycle decisions whose long-term cost — three hours of broken checkout for twenty-three enterprise accounts caused by a flag whose targeting rules documented a compliance arrangement that existed only in a closed Slack thread, or three weeks of engineering time to unwind forty-seven zombie flags and twelve dead code paths whose accumulation added 22 milliseconds to an API's p99 — exceeds what a lifecycle policy and a retirement procedure would have cost at the time the decision was made. The decisions are in the AI chat history: the platform selection rationale for LaunchDarkly over Unleash, the performance model that informed the first flag's placement in the request handler, the experiment design that produced the first complex targeting rules. WhyChose's open-source extractor surfaces these founding feature flag sessions as structured records before the next zombie flag deletion incident, the next performance audit, or the next compliance escalation makes the undocumented lifecycle assumptions visible as operational failures. The new CTO onboarding problem in the context of a feature flag system is a targeting complexity problem: an incoming technical leader will find a flag catalog with two hundred flags, no type taxonomy, no documented retirement criteria, and no way to assess from the flag dashboard alone which flags are safe to retire and which are actively serving a compliance, entitlement, or operational safety function that exists only in the targeting rules and in the institutional memory of engineers who may or may not still be at the company.
Further reading
- Decisions never written down — feature flag lifecycle decisions as founding choices whose consequences compound silently until a zombie flag deletion incident or a performance audit surfaces the undocumented assumptions eighteen months after the founding session
- The new CTO onboarding problem — an incoming technical leader finds a flag catalog with no type taxonomy, flags whose default value does not reflect the effective configuration for key customer segments, and no document explaining which flags are safe to retire versus which are actively serving compliance or operational safety functions
- The technical debt decision record — zombie flags that gate dead code paths are a specific category of technical debt with their own discovery and repayment model; the debt registry for flag debt must include the dead code path size and the test surface area maintaining coverage for code that will never run in production
- The deployment strategy decision record — feature flags decouple deployment from release and are a core mechanism of progressive delivery; the flag lifecycle policy and the deployment strategy are coupled because the deployment strategy determines which flag types are needed and at what evaluation frequency
- The platform engineering decision record — the feature flag system is a platform capability; the platform team that owns the flag system is responsible for the SDK governance, the performance budget policy, the metadata schema enforcement, and the audit mechanism that detects zombie flags across all product teams' flag catalogs
- ADR lifecycle: superseding and deprecating decision records — retiring a temporary feature flag is a supersession event: the feature flag ADR should record the outcome (which branch became permanent, when the cleanup was completed, what the permanent state of the feature is) so that engineers who encounter the code can understand its history without reading git blame or Slack threads
- The documentation strategy decision record — flag targeting rules are a form of configuration documentation subject to the same staleness and undiscoverability failure modes as runbooks and API documentation; the targeting rule documentation policy is the documentation strategy for the flag system's configuration surface
- WhyChose extractor — surfaces the founding release gating session, the A/B testing design session, and the kill switch architecture session from AI chat history as structured decision records, recovering the platform selection rationale and the original lifecycle commitments that were implicit in the founding team's shared understanding of the flag system