What's an actor

Many feature flag systems are solely based on users and that's a big limitation. That's why we use the concept of actors. An actor can be anything in your application. It can be a user, but it can also be a project, a team, a workspace, a device, etc.

Let's put an example. Let's say you are developing a project management tool and you are implementing a new "snooze" feature for issues. There's a team that has requested that feature and you want to test it with them. Instead of flagging every team member with the feature flag, you should just flag the whole team. There's many reasons to do it this way:

  • It isolates the feature. What if a user that is flagged starts using the feature in other teams? Other teammates in those teams would be confused. Maybe they don't see snoozed issues and think they are gone or something is broken.
  • What if new users are added to the team, or removed from it?
  • How do you implement an API endpoint that is authenticated via an API key and not as a user?

Flagging the right entity is better and easier:

// If this is the snooze API endpoint:
async function handle(request, response) {
  const teamId = getTeamId(request);
  const canSnooze = await client.isFeatureFlagEnabled("issue-snooze", {
    actors: [`team:${teamId}`],
  });
  if (!canSnooze) throw new NotImplementedError();

  // Rest of the implementation
  // ...
}

We recommend prefixing actor ids with their entity type such as user:1234 or team:4321 if their id is not globally unique.

How to support multiple environments

Other feature flag systems have the concept of environments (staging, production, preview environments,...). We think that this adds too much complexity because it makes it harder to trace and sync feature flags. Additionally the number of actors you'll have in a non production environment will be tiny compared to the production one.

Our recommendation is use a prefix for non-production environments. This way a user with id 1234 in staging will be staging:user:1234 whereas a user with the same id in production will just be user:1234.

You could create a simple utility function in your code to make things more readable:

const prefix = `${process.env.NODE_ENV}:`.replace("production:", "");

export function actorId(entity: string, id: string) {
  return `${prefix}${entity}:${id}`;
}

// Example
const id = actorId("user", currentUser.id);