Feature flags and authorization abstract the same concept
ntietz.com
ntietz.com
The concept behind feature flags is, "this person will have access to this feature some day, and I (am / am not) ready for them to see it yet." They're shortlived, tend to be sprinkled throughout the codebase, and need to be aggressively removed to prevent a mess. The consequences for failure are usually minor.
The concept behind authorization is, "this person (does / does not) have this capability, and they absolutely must not be given capabilities they don't have." They're long lived, have extraordinary consequences for failure, and need careful design to make sure they're centralized and easy to reason about.
These are vastly different needs that demand different design approaches.
The concepts are similar if not the same if you ask me (access control), just as the article points out, but they generally have different uses and patterns of use, again, just as the article points out.
That's authorization.
Feature flags were invented to enable continuous integration and trunk-based development. Ultimately, they're an alternative to feature branches. If you wouldn't create a git branch for it, you shouldn't create a feature flag for it.
Additionally, you roll out feature flags incrementally and hand control of them to your product team to use to gather user feedback. Beta users, alpha users, individual users or companies. It’s on a case by case basis as feedback is gathered to refine the feature before it goes to general availability.
Another option that is getting popular is to randomly grant access to a certain percentage of users over a series of days to track the rollout and watch for errors/user complaints.
Often, you end up capturing statistics for usage on wrapped features as well.
These are all very different than authorization.
Commonly, you will put a feature behind a feature flag, and roll it out locale by locale, or never.
You build a new feature that only works for a certain place, and you turn it on via a feature flag, and then you expand it to new places, also using a feature flag
I guess the question is when do you grant authorization? Before or after the feature has launched in a region?
A feature flag grants or denies access to a feature in exactly the same way as authorization - you could have 'feature' attributes in an ABAC system for example, and in fact I'm sure many systems use exactly this to provide feature tiers.
It's just that feature flags are generally used for a different purpose, ie targeted roll-out of new functionality, ie different policy concerns.
So, different uses, different policies, but the same abstraction/concept.
Some flags add a function or widget, while other flags add restrictions or change cronjob behaviour. Is this an authorisation or a feature flag system, or neither?
A complete feature flag system would also need a business-friendly front-end that product managers could use to roll out features and track how many people were using each one. Possibly an A/B testing component as well. And a way of alerting when a flag has been 100% active for X days so it could be removed from the code. Etc.
A complete authorization system would need a front-end focused on understanding who had which capabilities, assigning capabilities to users and categories of users, and probably some sort of capability grouping and inheritance mechanism. Possibly an impersonation ability for the support team. Etc.
From the perspective of someone implementing the frontend or backend? It has all the characteristics of a feature flag.
But what if Widget X shows something we are not allowed to sell to that person (say, per a specialized contract with their organization, or regional/legal limitations)? Then it's long lived, has extraordinary consequences for failure, and needs careful design to be able to model the person's membership in different (often overlapping) labels or organizations.
We implement a `resolve_config` function used across our codebase, where different entities in our data model have a well-defined precedence order when they can override the value iff they match part of the hierarchical context. It's designed to be a near-zero-cost abstraction (cached locally so lookups can be done synchronously). That way, doing something that will roll out to some partners over time, and never to other partners, is as simple as using this function instead of a constant or literal. Edits at any level are audit-logged and can, when necessary, have approval procedures applied. It's an authorization system, at its core, but with arbitrarily typed values rather than boolean authorized-or-not flags. And I can't imagine building a B2B or B2B2C business without it.
Full disclosure, I think about this topic regularly because I am a maintainer of SpiceDB[1], an open source authorization database.
It's a little silly, but lots of folks aim for the moon when it comes to performance for authorization, but then go on and sprinkle a dozen feature-flag RPCs each adding more and more latency. You should be able to keep feature-flags performant, too: that's why some SpiceDB users collapse checking for authorization and feature-flags into a single round-trip by defining a permission also requires a feature-flag (e.g. permission = admin & has_beta_feature).
It's really encouraging to be working in this space because there are so many epiphanies like the one in the article, but there just hasn't been tooling to make it as obvious until now. We're seeing folks adopt SpiceDB for update graphs and dependency graphs because the resolution of permissions (finding a path through a graph) is similar to solving these problems as well!
To the articles main point: eventually consistent and strongly consistent are not similar.
(thing: T, reducer: Reducer<T>): boolean =>
criterion(reducer(thing))
It’s good to recognize the similarity in these two cases, but why stop there?Edit: there’s a little bit of novelty in recognizing that global predicates implicitly take a global context argument, but that can be similarly reduced to “all context-sensitive processes are implicit functions over their context and any additional parameters”.
There are a lot more differences in the requirements for these use cases though, e.g., feature flags products want for more analytics, controlled rollout, etc.; authorization, not so much. Authorization requires access to lots of shared application data/state; feature flags, not so much.
Another area of overlap I see is entitlements/billing, e.g., can this person access this feature based on the plan they've signed up for? I've seen a lot of companies use the same underlying systems for feature flags, authorization, and entitlements (esp at earlier-stage companies and ones with a heavy DIY bent). Is this consistent with what others see/do?
Granted, our use is pretty straightforward, you either have access to product $Foo or you don't.
I think with usage based billing, like first 100 messages free, or tier one gets 5MB uploads, tier 2 gets 50MB uploads it becomes harder to have a universal API, you end up having to do some manual checks.
Still haven't really thought this all out but it's an interesting problem.
Edit: also roles can provide entitlements / enable feature flags. So Admin could get every feature, while intern might get only get the analytics_read feature.
Hm, that gives me a fun idea.
Feature flags - roll this out to 1% of users, randomly selected; roll this out to users with X hardware in Y region, etc.
Authorization - only users of the admin role can edit other users
As a concrete example, you might authorize some cohort of users as admins. Within that cohort you want to vary the UI for your admin app by using a feature flag controlling whether to use the old React app or a new Angular rewrite you are rolling out in tranches.
Conflating these concepts might work in some cases, but doesnt scale. Its like saying pods, services, deployments in k8s are all the same thing just because you haven’t yet seen a use case where the difference matters. Its not hard to imagine a use case where rolling out a feature necessitates tediously duplicating your ACL rules if you’re using ACL to gate rollout of features.
More properly due to frontend caching issues an HTTP request should include a header with the active features, separately from the header containing the auth token. And that gives a first semantic difference: feature flags take effect when the app is updated/reloaded, auth changes take effect immediately.
They are absolutely similar concepts, just as different levels.
If a customer wants to buy Analytics for example, it is controlled via a feature flag to enable it for that entire customer. By default all of thier user accounts can then access Analytics.
Then if we want to restrict certain categories of their users from seeing Analytics we can do that at the Authorization level.
Is it possible we could control all of this with one Authorization layer? Yes, probably.
But this lets us have a bit more fine grained control over things.
It used to be that authz was just roles and permissions assigned to users, or feature flags & entitlements just booleans, but sophisticated systems allow for all kinds of permutations and rules based on attributes, relationships and environment such that the lines between them are blurred and implementations are likely similar.
As others have said, the differences still come down to a handful of factors like correctness, tolerance for error and performance.
(Disclaimer: I'm a founder in this space and spend a lot of time thinking about it at Warrant - https://warrant.dev/ )
There are important subtleties that mean the same tech isn’t usually a good choice for all of these. But I’m really interested in building something that understands that as a developer I just want to know if X can do Y.
If anyone wants to jam on this hmu. We’re thinking a bunch about this at Prefab.cloud[0]
[0]: https://prefab.cloud
But for many applications, the semantic difference between the two drives different patterns in the calling code, and when that is the case, having an easily visible difference between the two predicates makes the code more readable and prompts people to write it more correctly.
Example: What happens when the answer is "no?" For many bog-standard applications, it's a NOOP. Let's say there's a feature: You can edit a message that has already been sent.
If you are wrapping that in a flag for gradual rollout or early access purposes, you wrap both the client-side edit control and the server-side API endpoint in the "can X do Edit" test, and if the answer is no, you don't show the control in the client, and you 404 the endpoint on the server.
Whereas, if editing messages that have already been sent is a feature only available to a special class of users—let's say they are paying $8 a month for an orange check-mark on Hacker News—then on the client we still hide the control if our user is not authorized to edit messages.
But on the server side, we typically don't want to 404 the endpoint, we want to give some semantically appropriate response such as a 401 "unauthorized" status.
If you use one construct for asking both FF and auth questions, it's hard to see at a glance whether the code is doing an appropriate thing with the answer.
This is grossly oversimplified, but the thesis here is that yes, if you use both FFs and auth in exactly the same way, it's reasonable to consider using one way to ask the question. But the definition of "using them the same way" should also include what you do with the answer.
In your case, you very likely always do exactly the same thing with the answer regardless of whether the answer is derived from FFs or auth. But for those who do different things depending on which question they're answering, I suggest having different ways of asking the questions provides better affordances for writing and maintaining the code.
Granted, the reason I want this is more that I've spent too much time try to help customer support figure out why person X can't see Y, which can devolve into checking 3 different systems too often.
Take any SaaS, where a single account can have multiple users. Feature flag is usually an account level concept. Authorization is a user level concept. Just because they both implement a conditional doesn't yet mean they are the same concept.
Was it ideal? IDGAF, I got promoted anyway