Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 87 additions & 0 deletions docs/contributing/feature-flags.md
Original file line number Diff line number Diff line change
Expand Up @@ -327,3 +327,90 @@ follows:

Local and development self-hosted installations may choose to configure alternative
[data sources](#flag-data-sources) to more quickly adopt a feature.

## Removing a feature flag

Once the flag has been enabled in all environments and the feature is verified to be functioning as
expected, the final steps are to remove the flagged conditional logic from our codebase, then the
flag itself. When defining the tasks for feature-flagged code, we also include cleanup tasks that
capture each step in the process of removing the feature flag.

Due to the complexity of the different client deployments and how we expose feature flags through
our API, it is important that each feature flag be removed in the appropriate sequence, with the
appropriate timing considerations.

### Step 1: Remove business logic and client references

:::tip Timing

**Step 1** can take place immediately after the feature is released.

:::

In this step, teams should remove all business logic that relies on the flag from both client and
server code. This includes all references in the client codebase, and also any business logic on the
server that checks the flag value.

:exclamation: This does **not** include removing the flag from the FeatureFlagKeys on the server --
we must leave this here for backward compatibility, so that existing clients who have not updated
continue to be served the correct "on" value when querying for the flag, even after the new server
release.

This code should then be deployed to all clients and to the server.

### Step 2: Remove flag from server

:::tip Timing

**Step 2** can take place either:

- Three major releases after the feature flag was removed from the clients in **Step 1**, if the
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

โš ๏ธ This was mentioned to me recently (I did not know it was documented this way) and I didn't think was how anyone operated. As I read it this is to protect older clients from regressing when using a newer server, and I don't think we should care about this use case -- why not expect the client from the first phase release to be in place for when the next server release deploys?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't we need that in order to meet our backward compatibility guarantees? If I release a feature in version N, and then remove the feature flag from clients and server in version N+1, when server version N+1 is deployed, the version N client will no longer receive the feature flag value in the /api/config response and default to false, disabling the feature that we just rolled out.

Is there some protection in place that I'm not aware of that would prevent that?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's a subtlety to feature flag usage, and I think literally having "feature" in the term can be confusing, but the intention is for them to live and die fast for the purposes of deployment and release resiliency first and foremost; gating functionality is indeed a benefit but it wasn't quite the point when I introduced them.

We do of course have a support policy, but that's for client-server interaction and my stance is that feature flags are not actually related to it. If you're going to use and benefit from something in N, then when N+1 comes you must also stay up to date if you expect to continue using whatever was flagged. Our support policy is about maintaining an operable state and not about getting anything and everything available.

Put simply, I feel the best way to operate is to have a client release that removes the use of the flag, is released and therefore available to users, and then the server can cease emitting that flag.

Copy link
Member

@eliykat eliykat Oct 2, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not opposed to this change, but I can just about guarantee that most devs have the same understanding as @trmartin4, so please make sure this is widely shared in Slack.

It also means (as I understand it) that we may not necessarily remove old code with the feature flag like we do today. e.g. a feature flag would be removed the N+1 release, but you may have to keep an old endpoint until N+3 if it's a breaking change. Whereas today we would keep both until N+3 and remove them together. That's not a problem, maybe that's even a better way to think about it, but something we'll have to think about differently.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Has anything changed with this position since our discussions?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, not meaningfully. Teams are still operating under the documentation as it is captured today internally. I made another commit to this changeset after some discussion amongst EMs, to add some guardrails around the flag removal process based on some feedback on recent flag archiving that has had unanticipated downstream effects.

I wanted to make sure it was captured here so it was included if/when this was ready for discussion again.

flag is used in non-web clients, or
- At the same time as the feature flag was removed from the clients in **Step 1**, if the flag is
only used on the web client.

:::

Once we have satisfied the backward compatibility
[requirements](https://bitwarden.com/help/bitwarden-software-release-support/) for our clients, we
can completely remove the feature flag from the server codebase. This can be done by removing the
flag value from the `FeatureFlagKeys`.

:::info Determining when it's safe to remove

To assess when a flag was removed from the TypeScript clients, you can check the
[history](https://github.com/bitwarden/clients/commits/main/libs/common/src/enums/feature-flag.enum.ts)
of the `FeatureFlagKeys` enum.

:::

### Step 3: Archive flag in LaunchDarkly

:::tip Timing

**Step 3** can take place immediately after the changes have been deployed the cloud servers for
Step 2. Appropriate time should be taken to ensure a rollback of the release will not be required.

:::

Once the server codebase has been deployed to all environments without any references to the flag,
the flag can be archived in LaunchDarkly.

Before the flag is archived, it should first be **disabled** in the Development and QA environments
and verified that there are no unintended side-effects.

:::warning "Ready to Archive" recommendation

Feature flags not accessed for a long period of time will automatically move to an "inactive" state
that can also help with identifying technical debt to clean up.

Because we do not use the LaunchDarkly client-side SDK, LaunchDarkly does **not** know if we are
evaluating a flag on the client. We retrieve all flags via the `/api/config` endpoint on the
Bitwarden API and serve those up to the client. The retrieval of all flags through the server-side
LaunchDarkly SDK
[does not report as evaluation of the flags](https://launchdarkly.com/docs/sdk/features/all-flags#about-the-all-flags-feature),
which means that that flag may be falsely reported as "inactive" and available to archive.

Flags that are evaluated on the server only will be reliably reported as being available to archive.

:::