<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Stigg</title>
    <description>The latest articles on DEV Community by Stigg (@getstigg).</description>
    <link>https://dev.to/getstigg</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1001615%2F47b5c463-7a4f-4319-9ae8-b317557463ec.jpg</url>
      <title>DEV Community: Stigg</title>
      <link>https://dev.to/getstigg</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/getstigg"/>
    <language>en</language>
    <item>
      <title>A practical guide for working with entitlements</title>
      <dc:creator>Stigg</dc:creator>
      <pubDate>Sun, 18 Jun 2023 15:46:00 +0000</pubDate>
      <link>https://dev.to/getstigg/best-practices-every-developer-should-know-when-working-with-entitlements-d5a</link>
      <guid>https://dev.to/getstigg/best-practices-every-developer-should-know-when-working-with-entitlements-d5a</guid>
      <description>&lt;p&gt;&lt;em&gt;This is part 3 out of a 3-part series about entitlements, first published on the Stigg blog, &lt;a href="https://www.stigg.io/blog-posts/entitlements-untangled-the-modern-way-to-software-monetization"&gt;here&lt;/a&gt;. If you're new to the concept of entitlements, check out part 1 first: "&lt;strong&gt;Entitlements untangled. The modern way to software monetization&lt;/strong&gt;" yet, you can check it out &lt;a href="https://dev.to/getstigg/entitlements-untangled-the-modern-way-to-software-monetization-3fd6-temp-slug-2180622?preview=79f39159b6af843d3ac30edd4cf190b58cbeaff6cb338ade9ad93d6a16b4fda06f8f1fa29580ee3a3c69be3d1b09182ac26472b0ec944fb754bcc60b"&gt;here&lt;/a&gt;. And if you're already thinking of alternative ways to gate feature access in your application, check out part 2: "&lt;a href="https://dev.to/getstigg/how-to-gate-end-user-access-to-features-shortcomings-of-plan-identifiers-authorization-feature-flags-2pa5-temp-slug-881198?preview=26b778047e99f2317bcb2e1c0f8b9682ff9fee28519853ffde8d17fe578b58b0ef337f9d32f1fb92684edf5285b58e64845ee77a37c3071ed73e7da4"&gt;The shortcomings of gating feature access using feature flags, authorization, or plan identifiers&lt;/a&gt;". Hope you enjoy reading it!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Entitlements are a crucial aspect of any SaaS pricing &amp;amp; packaging. We’ve summarized a few tips we found crucial when working with entitlements.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Don’t nest entitlement checks
&lt;/h3&gt;

&lt;p&gt;As you work with entitlements, one key principle to follow is to avoid nesting entitlement checks within your codebase. Nesting checks can lead to complex logic that is hard to follow, manage, and debug. Moreover, it can create interdependencies, where changes in one entitlement can unintentionally affect others.&lt;/p&gt;

&lt;p&gt;Instead of nested checks, we recommend introducing a new entitlement, if possible, instead of depending on many entitlements. If that's not an option, at least you can introduce an abstraction that wraps those entitlement checks as a single function to better deal with this kind of coupling, and make it easier to change in the future. A singular check streamlines your code and makes it easier to understand and maintain.&lt;/p&gt;

&lt;p&gt;Example of alternatives to nested entitlement checks:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ILEJT1CC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vld4u76bjuptnwe8qu4l.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ILEJT1CC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vld4u76bjuptnwe8qu4l.png" alt="Image description" width="800" height="549"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://carbon.now.sh/?bg=rgba%2874%2C144%2C226%2C1%29&amp;amp;t=one-dark&amp;amp;wt=bw&amp;amp;l=javascript&amp;amp;width=760&amp;amp;ds=false&amp;amp;dsyoff=20px&amp;amp;dsblur=68px&amp;amp;wc=false&amp;amp;wa=false&amp;amp;pv=0px&amp;amp;ph=0px&amp;amp;ln=false&amp;amp;fl=1&amp;amp;fm=Fira+Code&amp;amp;fs=14px&amp;amp;lh=152%25&amp;amp;si=false&amp;amp;es=4x&amp;amp;wm=false&amp;amp;code=%252F%252F%2520try%2520to%2520avoid%2520nested%2520entitlement%2520checks%250Aif%2520%28canAccessFeature%28tenant.id%252C%2520%27feature-dashboards%27%29%29%2520%257B%250A%2520%2520if%2520%28canAccessFeature%28tenant.id%252C%2520%27feature-revenue-management%27%29%29%2520%257B%250A%2520%2520%2520%2520%252F%252F%2520grant%2520access%250A%2520%2520%257D%250A%257D%250A%250A%252F%252F%2520introduce%2520a%2520single%2520entitlement%2520instead%250Aif%2520%28canAccessFeature%28%27feature-revenue-dashboard%27%29%29%2520%257B%250A%2520%2520%252F%252F%2520grant%2520access%250A%257D%250A%250A%252F%252F%2520if%2520not%2520possible%252C%2520wrap%2520both%2520checks%2520in%2520a%2520single%2520function%250Afunction%2520canAccessRevenueDashboard%28tenantId%29%2520%257B%250A%2520%2520return%2520%28%250A%2520%2520%2520%2520canAccessFeature%28tenantId%252C%2520%27feature-dashboards%27%29%2520%2526%2526%250A%2520%2520%2520%2520canAccessFeature%28tenantId%252C%2520%27feature-revenue-management%27%29%250A%2520%2520%29%250A%257D%250A%250Aif%2520%28canAccessRevenueDashboard%28tenant.id%29%29%2520%257B%250A%2520%2520%252F%252F%2520grant%2520access%250A%257D"&gt;Link to code&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Never assume new entitlements are ‘there’
&lt;/h3&gt;

&lt;p&gt;Due to the dynamic nature of entitlements, and the fact that they can be moved between plans, there is a chance an entitlement won't be present at all anymore.&lt;br&gt;
Never assume that there are “mandatory” entitlements to ensure the application can continue to work properly. This will make your application less fragile and less subject to errors due to miss-configuration of entitlements. &lt;/p&gt;

&lt;p&gt;Let’s say, you’ve added a new entitlement to a new version of your Free plan in your development environment. You can’t expect that this entitlement will be available until you’ll finish migrating all of your free customers to this new plan version. In the meantime, if the new entitlement check was already deployed - it must accommodate the fact that the entitlement is missing, or else the application will break.&lt;/p&gt;

&lt;h3&gt;
  
  
  3.  Embrace the “fail open” principle
&lt;/h3&gt;

&lt;p&gt;Entitlements are a critical component of any infrastructure. It's crucial to account for potential service disruptions. The "fail open" principle states that in the event of a failure, default access permissions should grant the user as much access as possible without breaking the application invariants. This ensures that users can continue to access necessary services, even when your entitlement service encounters an issue.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Create a centralized module in the code that evaluates the entitlements
&lt;/h3&gt;

&lt;p&gt;In general, it’s good practice to keep the entitlement checks at bay by introducing a shared module or a class that encapsulates the checks for different features. This type of  will make it much easier to track and change entitlement-related logic. It also helps define a more “business centric” terminology, when using entitlements in different areas of the application. &lt;/p&gt;

&lt;p&gt;Think about this scenario. You have an entitlement ‘seats’. Inside your application, there is a members page for your customers to invite more team members. The invite button is enabled if the number of members is below the entitlement limit. Introducing a &lt;code&gt;canInviteMembers(numOfNewMembers)&lt;/code&gt; function that checks for the seats entitlement against the current usage of the entitlement can help simplify the access check logic and re-use it throughout other places in the application.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--yzfp1mTB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/74v9vnzg2naa2nsip2oi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--yzfp1mTB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/74v9vnzg2naa2nsip2oi.png" alt="Image description" width="800" height="559"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://carbon.now.sh/?bg=rgba%2874%2C144%2C226%2C1%29&amp;amp;t=one-dark&amp;amp;wt=bw&amp;amp;l=javascript&amp;amp;width=1130&amp;amp;ds=false&amp;amp;dsyoff=20px&amp;amp;dsblur=68px&amp;amp;wc=false&amp;amp;wa=true&amp;amp;pv=0px&amp;amp;ph=0px&amp;amp;ln=false&amp;amp;fl=1&amp;amp;fm=Fira+Code&amp;amp;fs=14px&amp;amp;lh=152%25&amp;amp;si=false&amp;amp;es=4x&amp;amp;wm=false&amp;amp;code=%252F%252F%2520Example%2520of%2520a%2520React%2520hook%2520encapsulating%2520entitlmement%2520checks%2520through%2520out%2520the%2520app.%250A%252F%252F%2520Assuming%2520the%2520tenant%2520%2560entitlements%2560%2520were%2520already%2520fetched%2520and%2520are%2520passed%2520to%2520this%2520hook.%250Aimport%2520%257B%2520useMemo%2520%257D%2520from%2520%27react%27%250A%250Aexport%2520const%2520useEntitlements%2520%253D%2520%28entitlements%29%2520%253D%253E%2520%257B%250A%2520%2520return%2520useMemo%28%28%29%2520%253D%253E%2520%257B%250A%2520%2520%2520%2520return%2520%257B%250A%2520%2520%2520%2520%2520%2520canAccessAdvancedDashboards%28%29%2520%257B%250A%2520%2520%2520%2520%2520%2520%2520%2520return%2520%21%21entitlements%255B%27advanced-dashboards%27%255D%250A%2520%2520%2520%2520%2520%2520%257D%252C%250A%2520%2520%2520%2520%2520%2520getDataRetentionDays%28%29%2520%257B%250A%2520%2520%2520%2520%2520%2520%2520%2520return%2520entitlements%255B%27data-retention-days%27%255D%253F.value%2520%257C%257C%25200%250A%2520%2520%2520%2520%2520%2520%257D%252C%250A%2520%2520%2520%2520%2520%2520canInviteMembers%28numOfNewMembers%29%2520%257B%250A%2520%2520%2520%2520%2520%2520%2520%2520const%2520entitlement%2520%253D%2520entitlements%255B%27seats%27%255D%250A%2520%2520%2520%2520%2520%2520%2520%2520if%2520%28%21entitlement%29%2520%257B%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520return%2520false%250A%2520%2520%2520%2520%2520%2520%2520%2520%257D%250A%2520%2520%2520%2520%2520%2520%2520%2520return%2520entitlement.usage%2520%252B%2520numOfNewMembers%2520%253C%253D%2520entitlement.limit%250A%2520%2520%2520%2520%2520%2520%257D%252C%250A%2520%2520%2520%2520%2520%2520%252F%252F%2520...%2520more%2520entitlement%2520related%2520logic%250A%2520%2520%2520%2520%257D%250A%2520%2520%257D%252C%2520%255Bentitlements%255D%29%253B%250A%257D"&gt;Link to code&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is just a recommendation. You’ll need to adjust it to your project’s coding standards.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Less is more
&lt;/h3&gt;

&lt;p&gt;This might sound obvious, but way too often, this gets out of hand… Just like feature flags, if you don’t keep your entitlements as clean and simple as possible, you’ll end up with messy code. If an entitlement does not exist anymore, first remove the entitlement checks in all the places from the codebase, and then the corresponding entitlement.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building an entitlement service:
&lt;/h2&gt;

&lt;p&gt;Once your product is mature enough you’ll probably need an entitlement service. &lt;/p&gt;

&lt;p&gt;Here are a few points that you should take into consideration before attempting to build one yourself:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Prepare for network latency and downtime
&lt;/h3&gt;

&lt;p&gt;Every network call adds latency to the overall system response time. Entitlements are a critical function in the service you provide. You don’t want your customers to face long response times when trying to access a feature inside your product. Always strive to optimize your entitlement service for low latency and scale, or at least make sure you have a good caching mechanism in-place.&lt;/p&gt;

&lt;h3&gt;
  
  
  ‍2. Communicate usage and limits
&lt;/h3&gt;

&lt;p&gt;Especially in self-service products, your customers need to be aware of their current usage and the limits of their subscription, before they are being denied access to functionalities or service. An effective entitlement service should be able to track and communicate this information clearly to users. That can be through dashboards, a self-service customer portal, or regular in-app / email notifications.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Determine your source of truth
&lt;/h3&gt;

&lt;p&gt;You’ll need a source of truth. Deciding between your billing provider and your entitlement service is a significant consideration. This decision will influence how you synchronize data between the two systems. If your entitlement service includes complex logic like complex trials, scheduled downgrades, or grandfathering, this will be more suitable as your source of truth. Conversely, if your billing system is more sophisticated, then it should be the primary data source. The key is to identify which system contains the most comprehensive and accurate representation of customer data and use it to drive your entitlement decisions.&lt;/p&gt;

&lt;h2&gt;
  
  
  To wrap up
&lt;/h2&gt;

&lt;p&gt;Operating in highly dynamic markets, SaaS companies have much less chances of success when compromising on  rigid pricing models  managed in the codebase. &lt;/p&gt;

&lt;p&gt;Entitlements are the base for a truly flexible taxonomy and speed in go to market, allowing developers to quickly build and update pricing and giving GTM teams the opportunity to experiment with pricing &amp;amp; packaging, without code.&lt;/p&gt;

&lt;p&gt;We made it our mission at &lt;a href="https://www.stigg.io"&gt;Stigg&lt;/a&gt; to solve entitlements for SaaS. Our API gives you a flexible and scalable infrastructure, including metering, built-in experiments, a bunch of snap-in widgets, and native billing integrations, out of the box, and provides the automation to orchestrate everything about your pricing &amp;amp; packaging. If you consider buying over building, check out our &lt;a href="http://app.stigg.io/signup"&gt;Free plan&lt;/a&gt;. You can use one of our sandboxes to see all functionalities of Stigg, even before integrating it.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>How to gate end-user access to features: Shortcomings of plan identifiers, authorization &amp; feature flags</title>
      <dc:creator>Stigg</dc:creator>
      <pubDate>Sun, 18 Jun 2023 15:45:58 +0000</pubDate>
      <link>https://dev.to/getstigg/how-to-gate-end-user-access-to-features-shortcomings-of-plan-identifiers-authorization-feature-flags-38dh</link>
      <guid>https://dev.to/getstigg/how-to-gate-end-user-access-to-features-shortcomings-of-plan-identifiers-authorization-feature-flags-38dh</guid>
      <description>&lt;p&gt;&lt;em&gt;This is part 2 out of a 3-part series about entitlements, first published on the Stigg blog, &lt;a href="https://www.stigg.io/blog-posts/entitlements-untangled-the-modern-way-to-software-monetization" rel="noopener noreferrer"&gt;here&lt;/a&gt;. If you're new to the concept of entitlements or haven't read part 1: "&lt;strong&gt;Entitlements untangled. The modern way to software monetization&lt;/strong&gt;" yet, you can check it out &lt;a href="https://dev.to/getstigg/entitlements-untangled-the-modern-way-to-software-monetization-3fd6-temp-slug-2180622?preview=79f39159b6af843d3ac30edd4cf190b58cbeaff6cb338ade9ad93d6a16b4fda06f8f1fa29580ee3a3c69be3d1b09182ac26472b0ec944fb754bcc60b"&gt;here&lt;/a&gt;. Hope you enjoy reading it!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Today, you might already use a number of concepts, which may seem like a proper solution to solve monetization challenges. Let’s discuss a few alternatives to entitlements, and the limitations and friction points you should be aware of. &lt;/p&gt;

&lt;h2&gt;
  
  
  Entitlements vs Plan identifiers
&lt;/h2&gt;

&lt;p&gt;A common approach, especially in early-stage SaaS startups, is to tie access control to plan identifiers, that is, checking the user's subscription plan to determine access to specific features. &lt;/p&gt;

&lt;p&gt;For example, a structure like this can be found in the early days of a company:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5w6p2ukeqh6xh7lvkio8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5w6p2ukeqh6xh7lvkio8.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://carbon.now.sh/?bg=rgba%2874%2C144%2C226%2C1%29&amp;amp;t=one-dark&amp;amp;wt=bw&amp;amp;l=application%2Ftypescript&amp;amp;width=554&amp;amp;ds=false&amp;amp;dsyoff=20px&amp;amp;dsblur=68px&amp;amp;wc=false&amp;amp;wa=false&amp;amp;pv=0px&amp;amp;ph=0px&amp;amp;ln=false&amp;amp;fl=1&amp;amp;fm=Fira+Code&amp;amp;fs=14px&amp;amp;lh=152%25&amp;amp;si=false&amp;amp;es=2x&amp;amp;wm=false&amp;amp;code=%252F%252F%2520list%2520of%2520available%2520plans%250Aenum%2520Plans%2520%257B%250A%2520%2520FREE%252C%250A%2520%2520PAID%252C%250A%2520%2520ENTERPRISE%250A%257D%250A%250A%252F%252F%2520customer%2520object%2520with%2520a%2520plan%2520identifier%250Aconst%2520customer%2520%253D%2520%257B%250A%2520%2520id%253A%2520%2522customer-123%2522%252C%250A%2520%2520currentPlan%253A%2520Plans.ENTERPRISE%250A%257D%250A%250Afunction%2520canAccessFeature%28customer%252C%2520minimumPlan%29%2520%257B%250A%2520%2520return%2520customer.currentPlan%2520%253E%253D%2520minimumPlan%253B%250A%257D%250A%250Aif%2520%28%21canAccessFeature%28customer%252C%2520Plans.PAID%29%29%257B%250A%2520%2520%252F%252F%2520no%2520access%250A%257D" rel="noopener noreferrer"&gt;Link to code&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;While this might seem like a straightforward solution, it has significant downsides that can lead to complexity and rigidity over time. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Brittleness of plan identifiers&lt;/strong&gt;&lt;br&gt;
Plan identifiers can make your code brittle. The major drawback lies in the tight coupling it creates between your pricing model and the application codebase. Any change in the pricing plan, from changing the name of a plan, to changing the price or modifying the packaging, implies changes to the product behavior, which in turn necessitates updates to the code to accommodate it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Code maintenance&lt;/strong&gt;&lt;br&gt;
Coupling the feature access logic with the pricing model will require changing the application code every time you want to adjust or change anything. That’s a lot of maintenance work you can save by decoupling plans and features. Your entitlement checks should only be concerned with whether a customer has access to a feature, not why they have access. This separation of concerns is vital for maintaining clean, maintainable, and flexible code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Changes and migrations&lt;/strong&gt;&lt;br&gt;
Another aspect to consider is customer migration between plans. Say, for example, your business introduces a new plan or updates an existing one, and you wish to move customers to this plan. With plan identifiers, you would need to sift through the entire codebase to update the plan reference, even if you only want to make a change in the entitlement configuration. A task prone to errors and bugs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Custom settings for enterprise plans&lt;/strong&gt;&lt;br&gt;
Enterprises often require custom settings that differ from standard plans, thus necessitating a unique plan. With plan identifiers, you'd need to create a new plan for each custom case, leading to a multitude of plans and identifiers that are difficult to manage and reason about.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Custom code for usage and limits&lt;/strong&gt;&lt;br&gt;
When you use plan identifiers, tracking feature usage or metering requires implementing usage limits in your codebase. This adds another layer of complexity, leading to a potential maintenance burden over time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Entitlements vs Authorization
&lt;/h2&gt;

&lt;p&gt;Authorization is a method of restricting system access based on certain rules, such as role-based (RBAC), policy-based (PBAC), or attribute-based (ABAC) access controls, of individual users within an application.&lt;/p&gt;

&lt;p&gt;While entitlements serve monetization, typically on account level, and authorization serve access control, typically on user level, it's possible to model entitlements using an authorization concept by mapping plans to roles, attributes or policies. Here is an example, the free and premium plans are mapped as roles, with each role having a set of entitlements modeled as permissions:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9yqgxae2rvbjv3zj631c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9yqgxae2rvbjv3zj631c.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://carbon.now.sh/?bg=rgba%2874%2C144%2C226%2C1%29&amp;amp;t=one-dark&amp;amp;wt=bw&amp;amp;l=application%2Ftypescript&amp;amp;width=670&amp;amp;ds=false&amp;amp;dsyoff=20px&amp;amp;dsblur=68px&amp;amp;wc=false&amp;amp;wa=false&amp;amp;pv=0px&amp;amp;ph=0px&amp;amp;ln=false&amp;amp;fl=1&amp;amp;fm=Fira+Code&amp;amp;fs=14px&amp;amp;lh=152%25&amp;amp;si=false&amp;amp;es=2x&amp;amp;wm=false&amp;amp;code=const%2520roles%2520%253D%2520%257B%250A%2520%2520free%253A%2520%257B%250A%2520%2520%2520%2520permissions%253A%2520%255B%2522basic-dashboard%2522%252C%2520%2522reports%2522%255D%250A%2520%2520%257D%252C%250A%2520%2520premium%253A%2520%257B%250A%2520%2520%2520%2520permissions%253A%2520%255B%2522basic-dashboard%2522%252C%2520%2522advanced-dashboard%2522%252C%2520%2522reports%2522%255D%250A%2520%2520%257D%250A%257D%253B%250A%250A%252F%252F%2520assuming%2520the%2520user.permissions%2520is%2520derived%2520from%2520the%2520user%2520role%250Aif%2520%28%21user.permissions.includes%28%2522advanced-dashboard%2522%29%29%2520%257B%250A%2520%2520%252F%252F%2520no%2520access%250A%257D" rel="noopener noreferrer"&gt;Link to code&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;While this approach may work for less complex pricing models for a while, here are a few downsides you should be aware of.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;User access vs account access&lt;/strong&gt;&lt;br&gt;
Authorization follows the principle of "least privilege," providing only the minimum access rights needed for a user to fulfill their role. Authorization isn't tied to the business model of a product, but rather to the organizational structure within which a product or system is used. For example, in a typical SaaS product, a user with the Admin role may have access to invite add or remove team members, while a user with the Member role may not.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;No support of usage limits&lt;/strong&gt;&lt;br&gt;
Usage limits are not natively supported in authorization. While some advanced authorization policy engines can support this use-case, it's not a first-class citizen in most authorization implementations.&lt;/p&gt;

&lt;p&gt;An example of how to implement entitlements using the Oso policy engine (&lt;a href="https://www.osohq.com/docs/guides/model-your-apps-authz#entitlements" rel="noopener noreferrer"&gt;example from Oso's docs&lt;/a&gt;):&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3967tdzcmpsa9craehi0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3967tdzcmpsa9craehi0.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This approach will only work for monolithic applications, where the policy is evaluated against a single database. In distributed applications or microservices, where each service owns a subset of the policy’s data, accessing the usage can become even more challenging. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpo0pgqcqm3bvwzjpcxuj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpo0pgqcqm3bvwzjpcxuj.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Entitlements and JWT&lt;/strong&gt;&lt;br&gt;
Some might consider storing customer entitlements on authentication tokens, like JWT, by writing custom claims or permissions as part of the token’s payload. Take into account that this method won’t be accurate enough for most monetization use cases. JWT tokens are stateless and are not typically updated until they're refreshed. &lt;/p&gt;

&lt;p&gt;This can be problematic for several reasons: &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Usage limits won’t be accurately updated. If a user purchased another seat - the entitlement limit should be increased immediately.&lt;/li&gt;
&lt;li&gt;If you enrich the JWT payload with current usage, it won’t be accurately tracked. For example, a user exceeds their limit, but it won’t be reflected or blocked in time. &lt;/li&gt;
&lt;li&gt;If a user upgrades or downgrades to a different plan, they won’t have instant access to the new entitlements. &lt;/li&gt;
&lt;li&gt;If the company wants to grant a promotional entitlement to an account or change a user’s plan, those changes won’t be reflected in the user’s token immediately. &lt;/li&gt;
&lt;li&gt;This can lead to inaccurate or outdated permissions being enforced.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Entitlements vs Feature Flags
&lt;/h2&gt;

&lt;p&gt;Feature flags, also known as feature toggles, are a technique used in software development to turn certain features on or off at runtime, without needing to change or redeploy code. They are originally intended to be short lived and removed from the codebase, when not in use anymore. &lt;/p&gt;

&lt;p&gt;While both feature flags and entitlements provide control over software functionality, entitlements offer more sophisticated controls such as usage limits and tiered access levels that are automatically updated when a plan changes.&lt;/p&gt;

&lt;p&gt;An example of an entitlement check can be performed using a LaunchDarkly SDK:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Felx5xngv6ew5an9mo18f.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Felx5xngv6ew5an9mo18f.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://carbon.now.sh/?bg=rgba%2874%2C144%2C226%2C1%29&amp;amp;t=one-dark&amp;amp;wt=bw&amp;amp;l=application%2Ftypescript&amp;amp;width=616&amp;amp;ds=false&amp;amp;dsyoff=20px&amp;amp;dsblur=68px&amp;amp;wc=false&amp;amp;wa=false&amp;amp;pv=0px&amp;amp;ph=0px&amp;amp;ln=false&amp;amp;fl=1&amp;amp;fm=Fira+Code&amp;amp;fs=14px&amp;amp;lh=152%25&amp;amp;si=false&amp;amp;es=2x&amp;amp;wm=false&amp;amp;code=const%2520canAccessFeature%2520%253D%2520await%2520ldClient.variation%28%250A%2520%2520%27premium-dashboard%27%252C%250A%2520%2520%257B%2520key%253A%2520user.id%252C%2520custom%253A%2520%257B%2520customerId%253A%2520user.tenantId%2520%257D%2520%257D%252C%250A%2520%2520false%250A%29%250A%250Aif%2520%28%21canAccessFeature%29%2520%257B%250A%2520%2520%252F%252F%2520no%2520access%250A%257D" rel="noopener noreferrer"&gt;Link to code&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The evolution of Permissioning Toggles&lt;/strong&gt;&lt;br&gt;
Feature flags allow developers to turn features on or off, providing a way to test and release features gradually, perform A/B testing, and more. When considering entitlements in comparison, we could view them as an evolution of &lt;a href="https://martinfowler.com/articles/feature-toggles.html#CategoriesOfToggles:~:text=Operations%20person%20happy.-,Permissioning%20Toggles,-turning%20on%20new" rel="noopener noreferrer"&gt;Permissioning Toggles&lt;/a&gt;. However, they specifically cater to marketable features sold under various subscription packages, extending beyond the binary on/off use-case of traditional feature flags.&lt;/p&gt;

&lt;p&gt;When compared to other types of toggles, the nature of entitlements is relatively high dynamism (i.e. when the toggle value changes) as they are tied to the pricing model and customer subscription level, and a longevity (i.e. how long the toggle exists in the codebase) that can span from at least a few months to a few years. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbyi39f4nrxgrzqjzmt9p.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbyi39f4nrxgrzqjzmt9p.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can read more about the different types of feature toggles in &lt;a href="https://martinfowler.com/articles/feature-toggles.html" rel="noopener noreferrer"&gt;this&lt;/a&gt; excellent article by Pete Hodgson that was published on Martin Fowler’s website.&lt;/p&gt;

&lt;p&gt;If you choose to use a feature flagging system to manage your entitlements, here are a few things you need to consider. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Deterministic changes in entitlements&lt;/strong&gt;&lt;br&gt;
Feature flags are not part of the customer's contract and don’t have any financial impact. These flags can be used to test new features or variations with a subset of users, hence, they can be altered on the fly. Entitlements, on the other hand, are charged for and are bound by contract terms with a customer. Unlike feature flags that can be altered on the fly, entitlements change whenever a customer’s subscription or contract changes. Changes to entitlements usually require advance notice, and the current entitlements are typically only revoked after a sufficient “grace period” has expired. They are deterministic and should not be assigned ad-hoc, except in explicitly intended circumstances like for enterprise customers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;‍Targeting rules&lt;/strong&gt;&lt;br&gt;
The core of a feature flagging system is targeting rules among users (or customers), segments (or subscription tiers), and features. This could mean building custom automation workflows to keep everything synchronized when a plan or subscription changes. Entitlements, on the other hand, are natively tied to the customer subscription, automatically updating any changes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ease of use&lt;/strong&gt;&lt;br&gt;
Feature flagging tools are typically more technical and primarily targeted at engineers and product managers. This means Customer Success and Sales teams often find them difficult to navigate and understand, reducing their overall effectiveness for those teams.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Again, custom code for usage and limits&lt;/strong&gt;&lt;br&gt;
Feature flags don't inherently account for feature usage or metering. As a result, to track feature limits, you might need to define those limits into your codebase and automate toggling of flags when those limits are reached.&lt;/p&gt;

&lt;h2&gt;
  
  
  Entitlements, Feature Flags, and Authorization: Stitching it all together
&lt;/h2&gt;

&lt;p&gt;While it’s possible to duct-tape a solution to support your pricing model with one of the alternatives listed above, none of them will be sufficient enough on their own. Authorization, RBAC, and Entitlements each serve different purposes, are operated by different departments, and play a part in significantly different processes. To decide which tools you’ll need in your own stack, here’s an overview.  &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Feature Flags&lt;/strong&gt;: Primarily managed by Engineering and Product teams. They form part of the software delivery and product management process. A good use case for feature flags is, if you have a user that you want to include into the release of a beta feature and you’re not yet ready to roll it out across your entire user base.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Entitlements&lt;/strong&gt;: Managed by Product, Sales, Marketing, and/or Growth teams. They form part of the pricing and packaging strategies, and customer contract management processes. A good use case is, if you have a feature you’re ready to monetize, and you want to roll it out to all existing and/or new customers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Permissions (Authz)&lt;/strong&gt;: Usually managed by the application administrators. They form part of the data security and compliance processes. A good use case is, if you want to enable customers to set different permissions for different users within a given account.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7b5bv3svw7dxncth9yf8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7b5bv3svw7dxncth9yf8.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A typical sequence of access checks in the code would start with a feature flag check at the user level. This determines whether the feature is enabled for this particular user or organization.&lt;/p&gt;

&lt;p&gt;If the feature is enabled, the system then checks the entitlements at the organization level to confirm that the customer's subscription includes access to this feature.&lt;/p&gt;

&lt;p&gt;Finally, the system checks the user's role-based permissions to ensure that the user is authorized to perform the requested operation within the feature.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fflpvs6fc2eoo45ui3gft.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fflpvs6fc2eoo45ui3gft.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If we’ll imagine this sequence in code within a React application, it will look similar to this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo9pxqd24tt90mqlbebx3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo9pxqd24tt90mqlbebx3.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://carbon.now.sh/?bg=rgba%2874%2C144%2C226%2C1%29&amp;amp;t=one-dark&amp;amp;wt=bw&amp;amp;l=application%2Ftypescript&amp;amp;width=554&amp;amp;ds=false&amp;amp;dsyoff=20px&amp;amp;dsblur=68px&amp;amp;wc=false&amp;amp;wa=false&amp;amp;pv=0px&amp;amp;ph=0px&amp;amp;ln=false&amp;amp;fl=1&amp;amp;fm=Fira+Code&amp;amp;fs=14px&amp;amp;lh=152%25&amp;amp;si=false&amp;amp;es=4x&amp;amp;wm=false&amp;amp;code=%252F%252F%2520check%2520if%2520the%2520user%2520is%2520logged%2520in%250Aif%2520%28%21isAuthenticated%28%29%29%2520%257B%250A%2520%2520return%2520%253CRedirect%2520to%253D%2522%252Flogin%2522%252F%253E%253B%250A%257D%250A%250A%252F%252F%2520check%2520if%2520the%2520user%2520can%2520see%2520the%2520new%2520members%2520page%250Aif%2520%28%21isFeatureFlagEnabled%28user%252C%2520%2522members-page-v2%2522%29%29%2520%257B%250A%2520%2520return%2520%253CRedirect%2520to%253D%2522%252Flegacy-members-page%2522%253E%253B%250A%257D%250A%250A%252F%252F%2520check%2520if%2520the%2520organization%2520is%2520paying%2520for%2520the%2520teams%2520feature%250Aif%2520%28%21isEntitledTo%28user.orgId%252C%2520%2522feature-teams%2522%29%29%2520%257B%250A%2520%2520return%2520%253CRedirect%2520to%253D%2522%252Fpricing%2522%253E%253B%250A%257D%250A%250A%252F%252F%2520check%2520if%2520the%2520user%2520has%2520permission%2520to%2520view%2520team%2520members%250Aif%2520%28%21hasPermission%28user%252C%2520%2522members%253Aview%2522%29%29%2520%257B%250A%2520%2520return%2520%253CRedirect%2520to%253D%2522%252Frequest-admin-access%2522%252F%253E%253B%250A%257D" rel="noopener noreferrer"&gt;Link to code&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Scatter/Gather&lt;/strong&gt;&lt;br&gt;
To stitch these three systems together and make sense of them, one practical approach is to create a centralized place for access checks, such as an API gateway. This gateway can take on the responsibility of authorizing operations using a "scatter-gather" pattern on the backend.&lt;br&gt;
It "scatters" the authorization checks across the feature flag, entitlement, and permission systems, then "gathers" the results to make an overall decision. It can also pass this context on the request to downstream services so they won't have to fetch this data again in case they need it for their internal logic.&lt;/p&gt;

&lt;p&gt;This approach encapsulates the complexity of the authorization checks and provides a unified interface to the rest of the application. It also allows for easier updates and changes as the underlying systems evolve.&lt;/p&gt;

&lt;p&gt;An example of a “stitched” user data including feature flags, permissions and entitlements:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F05klkqdl5vzm3d49sd9h.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F05klkqdl5vzm3d49sd9h.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://carbon.now.sh/?bg=rgba%2874%2C144%2C226%2C1%29&amp;amp;t=one-dark&amp;amp;wt=bw&amp;amp;l=application%2Ftypescript&amp;amp;width=554&amp;amp;ds=false&amp;amp;dsyoff=20px&amp;amp;dsblur=68px&amp;amp;wc=false&amp;amp;wa=false&amp;amp;pv=0px&amp;amp;ph=0px&amp;amp;ln=false&amp;amp;fl=1&amp;amp;fm=Fira+Code&amp;amp;fs=14px&amp;amp;lh=152%25&amp;amp;si=false&amp;amp;es=4x&amp;amp;wm=false&amp;amp;code=const%2520user%2520%253D%2520%257B%250A%2520%2520featureFlags%253A%2520%255B%27dashboards-early-access-2023%27%255D%252C%250A%2520%2520permissions%253A%2520%255B%250A%2520%2520%2520%2520%27boards.view%27%252C%250A%2520%2520%2520%2520%27boards.open%27%252C%250A%2520%2520%2520%2520%27boards.archive%27%252C%250A%2520%2520%2520%2520%27attachments.upload%27%252C%250A%2520%2520%255D%252C%250A%2520%2520currentTenant%253A%2520%257B%250A%2520%2520%2520%2520tenantId%253A%2520%27d74a2ae630b6%27%252C%250A%2520%2520%2520%2520name%253A%2520%27Acme%2520Inc%27%252C%250A%2520%2520%2520%2520entitlements%253A%2520%257B%250A%2520%2520%2520%2520%2520%2520%27advanced-dashboards%27%253A%2520%257B%257D%252C%250A%2520%2520%2520%2520%2520%2520%27data-retention-days%27%253A%2520%257B%250A%2520%2520%2520%2520%2520%2520%2520%2520value%253A%252014%252C%250A%2520%2520%2520%2520%2520%2520%257D%252C%250A%2520%2520%2520%2520%2520%2520%27file-storage-mb%27%253A%2520%257B%250A%2520%2520%2520%2520%2520%2520%2520%2520usage%253A%2520128%252C%250A%2520%2520%2520%2520%2520%2520%2520%2520limit%253A%25201024%252C%250A%2520%2520%2520%2520%2520%2520%257D%252C%250A%2520%2520%2520%2520%257D%252C%250A%2520%2520%257D%252C%250A%257D%253B" rel="noopener noreferrer"&gt;Link to code&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  In Part 3 of this technical deep dive
&lt;/h2&gt;

&lt;p&gt;...we're sharing a curated list of best practices when working with entitlements. We've made it our company's mission to solve this complex issue for engineering teams and have learned a lot building Stigg. Whether you decide to look for a solution or build a service yourself, this list will help you make informed decisions. Check out our best practices &lt;a href="https://dev.to/getstigg/best-practices-every-developer-should-know-when-working-with-entitlements-3m9c-temp-slug-6885361?preview=1f335e3b2ed59fd5c0c0a9cc36c5d10b957185a10ca9ff6b6ad6371604f5543080581d55ca6047311659670caa5f54e5310104cd6d2a461f75842e87"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  To wrap up
&lt;/h2&gt;

&lt;p&gt;Operating in highly dynamic markets, SaaS companies have much less chances of success when compromising on  rigid pricing models  managed in the codebase. &lt;/p&gt;

&lt;p&gt;Entitlements are the base for a truly flexible taxonomy and speed in go to market, allowing developers to quickly build and update pricing and giving GTM teams the opportunity to experiment with pricing &amp;amp; packaging, without code.&lt;/p&gt;

&lt;p&gt;We made it our mission at &lt;a href="https://www.stigg.io" rel="noopener noreferrer"&gt;Stigg&lt;/a&gt; to solve entitlements for SaaS. Our API gives you a flexible and scalable infrastructure, including metering, built-in experiments, a bunch of snap-in widgets, and native billing integrations, out of the box, and provides the automation to orchestrate everything about your pricing &amp;amp; packaging. If you consider buying over building, check out our &lt;a href="http://app.stigg.io/signup" rel="noopener noreferrer"&gt;Free&lt;/a&gt; plan. You can use one of our sandboxes to see all functionalities of Stigg, even before integrating it.&lt;/p&gt;

</description>
      <category>api</category>
      <category>programming</category>
      <category>python</category>
      <category>nextjs</category>
    </item>
    <item>
      <title>Entitlements untangled: The modern way to software monetization</title>
      <dc:creator>Stigg</dc:creator>
      <pubDate>Sun, 18 Jun 2023 15:45:54 +0000</pubDate>
      <link>https://dev.to/getstigg/entitlements-untangled-the-modern-way-to-software-monetization-2pi0</link>
      <guid>https://dev.to/getstigg/entitlements-untangled-the-modern-way-to-software-monetization-2pi0</guid>
      <description>&lt;p&gt;&lt;em&gt;This technical deep-dive was initially published on &lt;a href="https://www.stigg.io/blog-posts/entitlements-untangled-the-modern-way-to-software-monetization"&gt;the Stigg blog&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;What are entitlements? Why should engineering teams care? And how are they different from feature flags or authorization tools? Our CTO Anton takes you into a technical deep dive into entitlements for SaaS monetization.&lt;/p&gt;

&lt;p&gt;How many times did you change something in your pricing?&lt;/p&gt;

&lt;p&gt;If you’ve been around SaaS for a while, you know how dynamic this landscape is, forcing companies to continuously update their pricing and packaging to stay competitive. &lt;/p&gt;

&lt;p&gt;Yet, every change entails a corresponding change in the codebase to accommodate the new packaging behavior, inherently becoming more complex and time-consuming than any engineering team appreciates.  &lt;/p&gt;

&lt;p&gt;Tight coupling between pricing models and application logic can create a significant overhead which usually means the pricing model won’t be changed often… and it might sound ridiculous, but many organizations ignore this problem and continue to suffer from an outdated and anchored pricing model because of bad software design choices in the early days of the company.&lt;/p&gt;

&lt;p&gt;This article introduces entitlements, an elegant solution to a complex problem in software monetization, and what engineering teams need to know to utilize it effectively. &lt;/p&gt;

&lt;h2&gt;
  
  
  So, what are entitlements?
&lt;/h2&gt;

&lt;p&gt;By focusing on features instead of pricing concepts, we can decouple the price model from the application logic. The concept of Entitlements encapsulates the feature access settings under various product variants (aka Pricing Plans or Packages), bridging the gap between how a product is sold and how it behaves for a variant.&lt;/p&gt;

&lt;p&gt;In essence, entitlements are a set of permissions defining what a customer (paying or non-paying) can do with your software application. For example, an entitlement can grant the customer access to the “Audit logs” feature or limit their account to “5 seats” within a given plan. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;In essence, entitlements are a set of permissions defining what a customer (paying or non-paying) can do with your software application.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Entitlements provide a clean separation of concerns, effectively streamlining the process of updating product features in response to changes in the pricing model.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--7Iq3x8up--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/825iimxxajcxcnme472x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--7Iq3x8up--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/825iimxxajcxcnme472x.png" alt="Image description" width="800" height="367"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The 3 types of entitlements and how to control access using entitlement checks
&lt;/h2&gt;

&lt;p&gt;Entitlement checks are vital to accurately control and monitor the access and usage of features by customers. They ensure that customers are granted access to the features they’ve paid for and help prevent overuse or misuse of resources. &lt;/p&gt;

&lt;p&gt;To have some common ground on how are the checks are evaluated, lets present an example of a basic entitlements data structure granted to a customer:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--5eD73381--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3p88xfiho2xvxsrs2v38.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--5eD73381--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3p88xfiho2xvxsrs2v38.png" alt="Image description" width="800" height="699"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://carbon.now.sh/?bg=rgba%2874%2C144%2C226%2C1%29&amp;amp;t=one-dark&amp;amp;wt=bw&amp;amp;l=javascript&amp;amp;width=480&amp;amp;ds=false&amp;amp;dsyoff=20px&amp;amp;dsblur=68px&amp;amp;wc=false&amp;amp;wa=false&amp;amp;pv=0px&amp;amp;ph=0px&amp;amp;ln=false&amp;amp;fl=1&amp;amp;fm=Fira+Code&amp;amp;fs=14px&amp;amp;lh=152%25&amp;amp;si=false&amp;amp;es=2x&amp;amp;wm=false&amp;amp;code=const%2520entitlements%2520%253D%2520%257B%250A%2520%2520%252F%252F%2520feature%2520gate%2520entitlement%250A%2520%2520%27advanced-dashboards%27%253A%2520%257B%257D%252C%250A%250A%2520%2520%252F%252F%2520configuraiton%2520entitlement%250A%2520%2520%27data-retention-days%27%253A%2520%257B%250A%2520%2520%2520%2520value%253A%252014%252C%250A%2520%2520%257D%252C%250A%250A%2520%2520%252F%252F%2520usage%2520quota%252Flimit%2520entitlement%250A%2520%2520%27file-storage-mb%27%253A%2520%257B%250A%2520%2520%2520%2520usage%253A%2520128%252C%250A%2520%2520%2520%2520limit%253A%25201024%252C%250A%2520%2520%257D%252C%250A%257D%253B"&gt;Link to code&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;These are the 3 most common types of entitlements in SaaS and how entitlement checks work for each of them: &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Feature gates&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Sometimes referred to as binary flags, these types of entitlements control whether a customer has access to a specific feature or not. For instance, a customer on a free plan might not have access to advanced analytics, but upgrading to a paid plan will enable this feature. These binary flags simply gate the feature—either the customer has access, or they don't.&lt;/p&gt;

&lt;p&gt;Feature access is controlled by evaluating a condition at runtime based on the customer and feature identifiers. This check verifies whether the customer is entitled to access the particular feature or not.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s---myKV0kY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/r7vhtftfptazfvvenimm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s---myKV0kY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/r7vhtftfptazfvvenimm.png" alt="Image description" width="800" height="277"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://carbon.now.sh/?bg=rgba%2874%2C144%2C226%2C1%29&amp;amp;t=one-dark&amp;amp;wt=bw&amp;amp;l=javascript&amp;amp;width=572&amp;amp;ds=false&amp;amp;dsyoff=20px&amp;amp;dsblur=68px&amp;amp;wc=false&amp;amp;wa=true&amp;amp;pv=0px&amp;amp;ph=0px&amp;amp;ln=false&amp;amp;fl=1&amp;amp;fm=Fira+Code&amp;amp;fs=14px&amp;amp;lh=152%25&amp;amp;si=false&amp;amp;es=2x&amp;amp;wm=false&amp;amp;code=function%2520canAccessFeature%28entitlements%252C%2520featureId%29%2520%257B%250A%2520%2520return%2520%21%21entitlements%255BfeatureId%255D%253B%250A%257D%250A%250Aif%2520%28%21canAccessFeature%28entitlements%252C%2520%27advanced-dashboards%27%29%29%2520%257B%250A%2520%2520%252F%252F%2520no%2520access%250A%257D"&gt;Link to code&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Configuration&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This type of entitlement changes the behavior of a feature within the application, depending on the package or plan that a customer is on. For instance, a customer on a free plan of an observability tool might have the data retention policy set to 3 days, while a customer on a paid plan might have a longer retention of 14 days. The entitlement does not just control access, but also how the feature behaves within the application.&lt;/p&gt;

&lt;p&gt;For configuration entitlements, the check doesn't just involve whether or not the feature is present but also retrieves a configuration value that drives application behavior.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Wyc9Fb6E--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/49076hfzdzczex38wwk6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Wyc9Fb6E--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/49076hfzdzczex38wwk6.png" alt="Image description" width="800" height="233"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://carbon.now.sh/?bg=rgba%2874%2C144%2C226%2C1%29&amp;amp;t=one-dark&amp;amp;wt=bw&amp;amp;l=javascript&amp;amp;width=480&amp;amp;ds=false&amp;amp;dsyoff=20px&amp;amp;dsblur=68px&amp;amp;wc=false&amp;amp;wa=true&amp;amp;pv=0px&amp;amp;ph=0px&amp;amp;ln=false&amp;amp;fl=1&amp;amp;fm=Fira+Code&amp;amp;fs=14px&amp;amp;lh=152%25&amp;amp;si=false&amp;amp;es=4x&amp;amp;wm=false&amp;amp;code=function%2520getFeatureValue%28entitlements%252C%2520featureId%252C%2520defaultValue%29%2520%257B%250A%2520%2520return%2520entitlements%255BfeatureId%255D%253F.value%2520%253F%253F%2520defaultValue%253B%250A%257D%250A%250Aconst%2520dataRetentionDays%2520%253D%2520getFeatureValue%28entitlements%252C%2520%2522data-retention-days%2522%252C%25200%29%253B%250A%250Aif%2520%28dataRetentionDays%2520%253E%25200%29%257B%250A%2520%2520%252F%252F%2520set%2520TTL%2520on%2520incoming%2520data%250A%257D"&gt;Link to code&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Soft/Hard limits&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;These entitlements cap the metered usage of certain features. In case of hard limits, access is fully cut off, once the limit is reached. For example, a customer has access to 5 seats. Once they reach 5 seats, they can’t invite more users. In case of soft limits, customers are alerted or throttled once they reach a certain usage level. For example, a customer may be limited to 1000 API calls per day under a free plan. Once the customer crosses the limit a "Maximum capacity reached" email is automatically sent, and a significantly lower API rate limit (or a grace period) is applied before the API starts rejecting new data. &lt;/p&gt;

&lt;p&gt;In the case of metered entitlements that have soft or hard limits, these checks are performed against the expected usage (= current usage + new usage) in a per-request context. For hard limit checks, it’s crucial to prevent overages, so real-time data is essential for accuracy.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--cYfkg-x0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/57doekmnxqeci1d5ey9q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--cYfkg-x0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/57doekmnxqeci1d5ey9q.png" alt="Image description" width="800" height="311"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://carbon.now.sh/?bg=rgba%2874%2C144%2C226%2C1%29&amp;amp;t=one-dark&amp;amp;wt=bw&amp;amp;l=javascript&amp;amp;width=480&amp;amp;ds=false&amp;amp;dsyoff=20px&amp;amp;dsblur=68px&amp;amp;wc=false&amp;amp;wa=true&amp;amp;pv=0px&amp;amp;ph=0px&amp;amp;ln=false&amp;amp;fl=1&amp;amp;fm=Fira+Code&amp;amp;fs=14px&amp;amp;lh=152%25&amp;amp;si=false&amp;amp;es=4x&amp;amp;wm=false&amp;amp;code=function%2520checkUsageLimit%28entitlements%252C%2520featureId%252C%2520newUsage%2520%253D%25200%29%2520%257B%250A%2520%2520const%2520entitlement%2520%253D%2520entitlements%255BfeatureId%255D%253B%250A%2520%2520return%2520entitlement%2520%2526%2526%2520%28entitlement.usage%2520%252B%2520newUsage%2520%253C%253D%2520entitlement.limit%29%253B%250A%257D%250A%250Aconst%2520fileSizeMb%2520%253D%2520256%253B%250A%250Aconst%2520canUploadFile%2520%253D%2520checkUsageLimit%28entitlements%252C%2520%2522file-storage-mb%2522%252C%2520fileSizeMb%29%253B%250A%250Aif%2520%28canUploadFile%29%2520%257B%250A%2520%2520%252F%252F%2520allow%2520to%2520upload%2520file%250A%257D"&gt;Link to code&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For soft limit checks, where preventing an overage is less important, there’s some tolerance for eventual consistency. This means you can compare against cached or slightly stale usage, offering a balance between system performance and accurate usage tracking.&lt;/p&gt;

&lt;h2&gt;
  
  
  What entitlements are not
&lt;/h2&gt;

&lt;p&gt;To fully understand what entitlements are, let’s cover some misconceptions about them as well. &lt;/p&gt;

&lt;p&gt;Firstly, entitlements are not a customer setting. Entitlements are typically part of a contractual agreement between you and your customer, i.e. the plan the customer chose. Customer settings are individual configurations a customer can make within their account. For example, a customer pays for 1GB of log data per day. One of their applications in their development environment is spammy. The customer setting allows them to reduce the log data for this specific environment to 256mb, while the account is still entitled to 1GB in total. &lt;/p&gt;

&lt;p&gt;Secondly, entitlements are not synonymous with plans, e.g. "Basic", "Pro", and "Enterprise".  Entitlements are, at their core, marketable features. Different pricing plans may grant access to these features with various configurations, but an entitlement does not inherently represent a particular product level.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--TGrj63eC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jw4739qcjt5qvu07317c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--TGrj63eC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jw4739qcjt5qvu07317c.png" alt="Image description" width="800" height="413"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why should you care about entitlements?
&lt;/h2&gt;

&lt;p&gt;Managing pricing changes at the code level, without appropriate abstraction mechanisms, can quickly devolve into a tangled mess, resulting in more complexities than anticipated. &lt;/p&gt;

&lt;p&gt;With entitlements, you can cleanly decouple the pricing model from the application logic, enabling swifter and smoother changes to your offerings. Any packaging change will take a fraction of the time you’d spend with hardcoded checks.&lt;/p&gt;

&lt;p&gt;Besides taking much less of your time, you will make your organization that much more flexible in the way they go to market. You can introduce new features or tweak existing ones without having to overhaul the entire system.  You’ll be able to quickly adapt to changes in the market, customer needs, or business strategies. This agility not only gives you a competitive edge but also makes for a better user experience as you can tailor your offerings to better suit your customers' needs.&lt;/p&gt;

&lt;p&gt;On top of that, entitlements enable GTM teams to A/B test and experiment with pricing &amp;amp; packaging, without talking to developers at all. As outlined at the beginning of the article, SaaS pricing &amp;amp; packaging has to change regularly to keep up with the market dynamics. As an abstraction on top of your codebase, those changes will no longer require you to write code.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;As an abstraction on top of your codebase, those changes will no longer require you to write code.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Adopting entitlements can save you tons of development hours, fewer bugs, and increase your chances of success in a rapidly changing market environment. &lt;/p&gt;

&lt;h2&gt;
  
  
  Fusing entitlements and metering
&lt;/h2&gt;

&lt;p&gt;Metering becomes crucial, once your pricing model includes configuration or soft/hard limit entitlements. Say you limit your customers by the number of users they can add to their team, the amount of storage space they get access to, or the number of API calls they can make within a certain timeframe. Without measuring a customer’s usage of an entitlement, you won’t be able to enforce limits in the entitlement service, and the enforcement will have to leak into the application code. For example, a customer’s plan includes 5 seats. Without metering, you won’t know how many seats they actually use. Your customer could invite 30 other users, without being limited - or ever having a reason to upgrade.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Without measuring a customer’s usage of an entitlement, you won’t be able to enforce limits in the entitlement service, and the enforcement will have to leak into the application code.&lt;/code&gt;&lt;br&gt;
‍&lt;br&gt;
Metering software is designed to calculate feature usage, based on raw measurements that are often stored in an append-only system. This way, it is possible to maintain a historical record and allows querying for historical usage between different timeframes.&lt;/p&gt;

&lt;p&gt;If you don't have a metering solution in place, you'll need to calculate the usage yourself. For simple counting, this is fairly straightforward – you just query your database to figure out how many instances of a feature or resource exist and compare it with the limit set by the entitlement.&lt;/p&gt;

&lt;p&gt;However, when your usage is interval-based (such as a limit on API calls per month), it becomes more tricky. You need to design a system that can aggregate usage data for every passing interval, and that can correctly reset or roll-over counts when the interval ends.&lt;/p&gt;

&lt;p&gt;For example, to support the entitlement, you’ll need to track and count the number of API calls:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--eJgknzq4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wi8z151xcwbjaqid5gpo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--eJgknzq4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wi8z151xcwbjaqid5gpo.png" alt="Image description" width="800" height="541"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://carbon.now.sh/?bg=rgba%2874%2C144%2C226%2C1%29&amp;amp;t=one-dark&amp;amp;wt=bw&amp;amp;l=javascript&amp;amp;width=677&amp;amp;ds=false&amp;amp;dsyoff=20px&amp;amp;dsblur=68px&amp;amp;wc=false&amp;amp;wa=false&amp;amp;pv=0px&amp;amp;ph=0px&amp;amp;ln=false&amp;amp;fl=1&amp;amp;fm=Fira+Code&amp;amp;fs=14px&amp;amp;lh=152%25&amp;amp;si=false&amp;amp;es=4x&amp;amp;wm=false&amp;amp;code=function%2520canAccessApi%28tenant%29%2520%257B%250A%2520%2520%252F%252F%2520assume%2520we%2520count%2520and%2520store%2520the%2520monthly%2520API%2520calls%2520somewhere%250A%2520%2520const%2520usage%2520%253D%2520countApiCallsOfCurrentMonth%28tenant.id%29%253B%250A%2520%2520const%2520entitlement%2520%253D%2520tenant.entitlements%255B%27api-calls%27%255D%253B%250A%2520%2520return%2520usage%2520%253C%2520entitlement.limit%253B%250A%257D%250A%250Aapp.post%28%27%252Fsome-endpoint%27%252C%2520async%2520%28req%252C%2520res%29%2520%253D%253E%2520%257B%250A%2520%2520const%2520tenant%2520%253D%2520req.user.currentTenant%253B%250A%2520%2520if%2520%28%21canAccessApi%28tenant%29%29%2520%257B%250A%2520%2520%2520%2520%252F%252F%2520monthly%2520API%2520calls%2520limit%2520has%2520been%2520reached%250A%2520%2520%2520%2520res.status%28429%29.send%28%27Monthly%2520API%2520calls%2520limit%2520reached%27%29%253B%250A%2520%2520%2520%2520return%253B%250A%2520%2520%257D%250A%250A%2520%2520%252F%252F%2520increment%2520the%2520API%2520calls%2520counter%250A%2520%2520await%2520trackApiCalled%28tenant%29%253B%250A%250A%2520%2520%252F%252F%2520...%2520rest%2520of%2520the%2520endpoint%2520logic%250A%257D%29"&gt;Link to code&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Hard stops
&lt;/h2&gt;

&lt;p&gt;For entitlements with hard limits, once the limit is reached, access to the feature must be revoked to prevent any overuse. This is especially relevant for free plans. Hard stops also introduce perfect upsell opportunities, whether you support a self-service or sales-led model. For self-service products, present a paywall for your customer to allow them to upgrade and overcome the limit they’ve just experienced. For sales-led products, you can trigger a notification to your sales team to reach out to their customer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Optimizing plans for success
&lt;/h2&gt;

&lt;p&gt;Metering isn't just about enforcing entitlements. It's also a powerful analytical tool. By analyzing usage patterns, you can gain insights into how different features or resources are being used. This can inform decisions about how to optimize entitlement limits for future pricing packages and enables modern ways to go to market, such as hybrid pricing (i.e. combining fixed costs with limits and variable costs into a new pricing model).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--w76_QBD---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/f6u8rxhmwwae2anv77iz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--w76_QBD---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/f6u8rxhmwwae2anv77iz.png" alt="Image description" width="500" height="703"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pro Tip:&lt;/strong&gt; When talking about optimizing pricing plans, GTM teams will need a way to experiment with different versions of a plan. A good entitlement service should allow changing the upsell interface (e.g. paywalls or trial banners) dynamically to easily accommodate those experiments without additional coding.&lt;/p&gt;

&lt;h2&gt;
  
  
  Entitlements vs Billing
&lt;/h2&gt;

&lt;p&gt;Billing software has a very specific role in managing financial transactions, invoicing, payment scheduling, and processing. Simply put, they handle everything when a customer swipes their credit card. In pricing &amp;amp; packaging, especially in self-service products, a lot is happening before and after that swipe, though. Many times, users first access your product through a free plan or trial period. &lt;a href="https://www.stigg.io/blog-posts/the-only-guide-youll-ever-need-to-implement-upgrade-downgrade-flows-part-2"&gt;Complex upgrade &amp;amp; downgrade flows&lt;/a&gt; require very smooth controls over who has access to what feature. &lt;/p&gt;

&lt;p&gt;The key distinction between the two systems is that while billing software is focused on the commercial aspect, entitlements are all about provisioning and access control.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The key distinction between the two systems is that while billing software is focused on the commercial aspect, entitlements are all about provisioning and access control.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--NoUb-5oh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/67qytda7y9g4bpb2lsih.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--NoUb-5oh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/67qytda7y9g4bpb2lsih.png" alt="Image description" width="800" height="458"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why does your entitlement service have to be separated from your billing?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Bridging between application and billing&lt;/strong&gt;&lt;br&gt;
An entitlement service can be seen as a bridge between your application behavior and your billing process, and can become the source of truth for both pricing and packaging. Besides acting as an “&lt;a href="https://learn.microsoft.com/en-us/azure/architecture/patterns/anti-corruption-layer"&gt;anti-corruption layer&lt;/a&gt;” to shield potential integration errors with the billing software, it also provides a fail-safe layer in case the billing system is unreachable. If the need rises, this can allow you to switch between billing solutions in the future easier with much less coupling. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Dealing with legacy plans&lt;/strong&gt;&lt;br&gt;
Billing systems typically do not handle the concept of "grandfathering" (i.e. allowing legacy customers to stay on their plan, while updating the plan for new customers), or gradually transitioning customers from legacy plans to the new ones (e.g. when you add a new feature to an existing plan). There is also no native support for scheduling migrations of customer segments, so moving e.g. your legacy customers to the new plan at the end of their billing period will be pretty hard doing it, involving custom scripts and programmatically scheduling the transition. Entitlement services can automate this process, reducing the manual effort and ensuring a smoother transition for customers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Centralized entitlement management&lt;/strong&gt;&lt;br&gt;
The main benefit of an entitlement service is that it allows you to manage each feature's business logic centrally. It's an independent system that decides who has access to what based on the user's subscription status. Billing software, on the other hand, isn't designed to carry out these checks or grant access to features. It's solely concerned with financial transactions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Scalability and low latency&lt;/strong&gt;&lt;br&gt;
Entitlement checks are crucial for ensuring that applications operate reliably. These checks need to be made quickly and efficiently, which requires a system designed with low latency and scalability in mind. Billing systems typically don't offer this kind of performance because they delegate these concerns to the integrating system.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Flexible access management&lt;/strong&gt;&lt;br&gt;
An entitlement service allows for a much greater level of flexibility when it comes to managing user access. It can handle multiple sources of access rights, such as subscription plans, add-ons, and ad-hoc features (like promotions) granted to a customer. Billing systems are far more limited, as they’re focused on pricing and payment, not on deciding who gets access to what.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Decoupling pricing and access control&lt;/strong&gt;&lt;br&gt;
By separating entitlements from billing, you can decouple pricing from access control. With this separation, you can give users access to features independently of their billing status, offering more flexibility and control for use-cases like free &amp;amp; reverse trials, and customisations for enterprise customers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Enforcing plan immutability&lt;/strong&gt;&lt;br&gt;
Billing systems usually don’t have safeguards to control changes of an existing plan. If one of your team members makes a change unintentionally, this will affect all your existing customers immediately. An entitlement service ensures that plan details are immutable, providing peace of mind for both operators and customers by preventing unexpected changes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Development, Staging and Production environments&lt;/strong&gt;&lt;br&gt;
Billing systems often lack support for different environments or the development release lifecycle. For changes in your pricing model, whether you add or remove a feature, update the price or overhaul your entire model, you’ll probably want to build in development, test in staging, and only then move to production. Billing systems offer limited support for change management, delegating the tedious tasks of seeding data and syncing it between multiple development accounts to you. An entitlement service should support multiple environments and allow automatically promoting changes between different development, staging, and production in order to prevent potential drift.&lt;/p&gt;

&lt;h2&gt;
  
  
  In Part 2 of this technical deep-dive
&lt;/h2&gt;

&lt;p&gt;...we're going to discuss different approaches to gating feature access for your end users. We're taking a closer look at authorization, feature flags, and plan identifiers, how to gate features with each of these approaches and the downsides you need to be aware of, before choosing any of these methods. Check out the top shortcomings we've identified &lt;a href="https://dev.to/getstigg/how-to-gate-end-user-access-to-features-shortcomings-of-plan-identifiers-authorization-feature-flags-2pa5-temp-slug-881198?preview=26b778047e99f2317bcb2e1c0f8b9682ff9fee28519853ffde8d17fe578b58b0ef337f9d32f1fb92684edf5285b58e64845ee77a37c3071ed73e7da4"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  In Part 3 of this technical deep-dive
&lt;/h2&gt;

&lt;p&gt;...we're sharing a curated list of best practices when working with entitlements. We've made it our company's mission to solve this complex issue for engineering teams and have learned a lot building Stigg. Whether you decide to look for a solution or build a service yourself, this list will help you make informed decisions. Check out our best practices &lt;a href="https://dev.to/getstigg/best-practices-every-developer-should-know-when-working-with-entitlements-3m9c-temp-slug-6885361?preview=1f335e3b2ed59fd5c0c0a9cc36c5d10b957185a10ca9ff6b6ad6371604f5543080581d55ca6047311659670caa5f54e5310104cd6d2a461f75842e87"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  To wrap up
&lt;/h2&gt;

&lt;p&gt;Operating in highly dynamic markets, SaaS companies have much less chances of success when compromising on  rigid pricing models  managed in the codebase. &lt;/p&gt;

&lt;p&gt;Entitlements are the base for a truly flexible taxonomy and speed in go to market, allowing developers to quickly build and update pricing and giving GTM teams the opportunity to experiment with pricing &amp;amp; packaging, without code.&lt;/p&gt;

&lt;p&gt;We made it our mission at &lt;a href="https://www.stigg.io"&gt;Stigg&lt;/a&gt; to solve entitlements for SaaS. Our API gives you a flexible and scalable infrastructure, including metering, built-in experiments, a bunch of snap-in widgets, and native billing integrations, out of the box, and provides the automation to orchestrate everything about your pricing &amp;amp; packaging. If you consider buying over building, check out our &lt;a href="http://app.stigg.io/signup"&gt;Free plan&lt;/a&gt;. You can use one of our sandboxes to see all functionalities of Stigg.&lt;/p&gt;

</description>
      <category>api</category>
      <category>learning</category>
      <category>node</category>
      <category>python</category>
    </item>
    <item>
      <title>Best practices I wish we knew, when introducing Stripe webhooks</title>
      <dc:creator>Stigg</dc:creator>
      <pubDate>Sun, 11 Jun 2023 09:07:37 +0000</pubDate>
      <link>https://dev.to/getstigg/best-practices-i-wish-we-knew-when-introducing-stripe-webhooks-592p</link>
      <guid>https://dev.to/getstigg/best-practices-i-wish-we-knew-when-introducing-stripe-webhooks-592p</guid>
      <description>&lt;p&gt;In this article we will describe what are the best practices to create the webhook integration with Stripe. We will be focusing only on one side of our two-way integration. The side that is listening to webhooks and acting on them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why are we so obsessed with Stripe incoming webhooks in Stigg?
&lt;/h2&gt;

&lt;p&gt;One of the value propositions of our product is our no-code Stripe integration. When integrating with &lt;a href="https://www.stigg.io"&gt;Stigg&lt;/a&gt; we take care of everything related to interacting with Stripe for our customers. We sync all the plans and add-ons, customers, and subscriptions and manage the state of those entities in Stigg and propagate changes to Stripe if needed. We are also listening to webhooks from Stripe that indicate that something changed to the synced entities on Stripe's side. &lt;/p&gt;

&lt;p&gt;One of the most important flows in Stigg is the Create Subscription flow. When having a Stripe integration and a paid subscription is created we are waiting for an incoming webhook from Stripe to acknowledge that the payment was successfully made in order to make the subscription active. Failing to create subscriptions for any reason can lead to our customers losing money and therefore this has to be a well-tested, highly resilient, and easy to scale.&lt;/p&gt;

&lt;p&gt;In this article, we will describe what are the best practices to create the webhook integration with Stripe. We will be focusing only on one side of our two-way integration. The side that is listening to webhooks and acting on them.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--hk23FxtJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0nrik050822fx8lg1wv0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--hk23FxtJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0nrik050822fx8lg1wv0.png" alt="Stigg integration with Stripe, listening to webhooks" width="800" height="361"&gt;&lt;/a&gt;&lt;br&gt;
‍&lt;/p&gt;
&lt;h2&gt;
  
  
  What’s the problem with spinning up an API to listen to Stripe webhooks and act on them?
&lt;/h2&gt;

&lt;p&gt;It is fairly easy to just listen to incoming webhooks with a service and act upon them and that's how we started when developing the proof of concept for this feature.&lt;/p&gt;

&lt;p&gt;It could be something along those lines:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--3KwWPGvR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lav1lbpamm1ml93t3vs2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--3KwWPGvR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lav1lbpamm1ml93t3vs2.png" alt="Stripe webhooks sending data to Amazon RDS via Stigg API" width="800" height="319"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For us, this was great for the proof of concept but not good enough to go to prod. &lt;/p&gt;

&lt;p&gt;There are a few issues with this design that we, in the engineering team, spotted and wanted to address in order to be confident about this new part of the integration:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What happens if we are down and webhooks are not being processed?&lt;/li&gt;
&lt;li&gt;What happens if our processing fails silently?&lt;/li&gt;
&lt;li&gt;How can we monitor failures properly?&lt;/li&gt;
&lt;li&gt;How can we be sure that we can reprocess failed events on our own when we fix a potential bug on our side?&lt;/li&gt;
&lt;li&gt;If Stripe bombards us with events, how do we scale our processing speed without losing data?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We had to build something way different to answer these questions. And with the complexity of the design, we found out that there are many caveats in the way Stripe is implementing their webhooks. Some of those limitations we found the hard way, so reading this can actually save you a lot of pain trying to build your webhook integration.&lt;/p&gt;

&lt;p&gt;So here are a few limitations we had to take into account when designing the new solution for reliable Stripe webhooks processing and what it meant for us:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;- “Stripe does not guarantee delivery of events in the order in which they are generated” ~&lt;a href="https://stripe.com/docs/webhooks/best-practices"&gt;Stripe docs&lt;/a&gt;. That means that we need to be very careful processing an event and we can’t rely on a &lt;code&gt;customer.subscription.created&lt;/code&gt; event to arrive before the &lt;code&gt;customer.subscription.updated&lt;/code&gt; event. It’s awesome to allow us to process in parallel as much as we want. But really not awesome when we want to first create a subscription and then update it as it happens naturally.&lt;/li&gt;
&lt;li&gt;- “Webhook endpoints might occasionally receive the same event more than once” ~&lt;a href="https://stripe.com/docs/webhooks/best-practices"&gt;Stripe docs&lt;/a&gt;. This is a good one. Anything you build relying on Stripe webhook has to be Idempotence. Don’t miss this one!&lt;/li&gt;
&lt;li&gt;- You can’t persist the webhook and reprocess them later. Stripe is limiting us to verify the signature of the webhook within 5 minutes to avoid &lt;a href="https://stripe.com/docs/webhooks/signatures#replay-attacks"&gt;replay attacks&lt;/a&gt;. Failing to do so, will make the event fail with an error. While it increases the security, it doesn’t allow us to propose a design where we persist all webhooks and process them later. It’s not possible because the first step of processing a webhook is unpacking it with &lt;code&gt;stripe.webhooks.constructEvent&lt;/code&gt; which verifies the signature and would fail if invoked more than 5 minutes after the webhooked arrived.&lt;/li&gt;
&lt;li&gt;- “Stripe will attempt to notify you of a misconfigured endpoint via email if an endpoint has not responded with a 2xx HTTP status code for multiple days in a row” ~&lt;a href="https://stripe.com/docs/webhooks/best-practices"&gt;Stripe docs&lt;/a&gt;. If webhooks start failing, don’t rely on Stripe to let you know there is an issue. It will take a few days until they will let you know something is off.&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;We are addressing all the Stripe limitations and our requirements both on the code application level and the way we build our architecture.&lt;br&gt;
‍&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;
  
  
  How do we mitigate limitations 1 and 2?
&lt;/h2&gt;

&lt;p&gt;We are mitigating those two limitations on the application level and not on architecture level. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Limitation 1: Stripe does not guarantee delivery of events in the order in which they are generated&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is a very interesting topic that caused us quite a lot of trouble in the beginning when we just started. For us, the key was to listen to the right events for different use cases and fetch data from Stripe when missing. Let’s look at a concrete example. When creating a subscription we are listening to a subscription.created event in order to activate a subscription after the payment was successful. We also listen to subscription.updated webhook to sync subscription status. In some cases when we just started we received subscription.updated events before subscription.created events (exactly as Stripe docs are promising).It caused our lambda to start throwing exceptions as the webhooks arrived in the wrong order. The subscription.updated handle expected that we already processed a subscription.created webhook and created a subscription.&lt;/p&gt;

&lt;p&gt;The solution for us was to fetch the subscription and create it if it’s not yet created in our database. The code could look something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;‍module.exports = leftpad;

function leftpad(str, len, ch) {
  str = String(str);
  var i = -1;

  if (!ch &amp;amp;&amp;amp; ch !== 0) ch = ' ';

  len = len - str.length;

  while (i++ &amp;lt; len) {
    str = ch + str;
  }
  return str;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We always keep this limitation in mind when we grow and listen and process more events. We should always think about that in our designs and implementations.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Limitation 2: Webhook endpoints might occasionally receive the same event more than once.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We didn’t experience that one too often but we did build our processing mechanism in a way that we are working in an idempotent manner. We can always run the same message through our system and the result will always be the same.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;The last two limitations are mitigated on the infrastructure level and therefore let’s try to get a better understanding of how to design a Stripe webhook integration.&lt;/code&gt;&lt;br&gt;
‍&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--wc5nh23J--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/25zqikb8gmwhq9445ucs.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--wc5nh23J--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/25zqikb8gmwhq9445ucs.png" alt="Stigg architecture for listening to Stripe webhooks" width="800" height="306"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Overview:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;AWS API Gateway to take the hit from Stripe.&lt;/li&gt;
&lt;li&gt;Lambda function Gateway integration that verifies the signature of the webhooks and persists them on a queue.&lt;/li&gt;
&lt;li&gt;AWS SQS with DLQ for reprocessing,&lt;/li&gt;
&lt;li&gt;AWS Lambda function that will read and handle the event.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;And now let's review each infra item and explain how it answers all our tech requirements.&lt;br&gt;
‍&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS API Gateway to take the hit from Stripe. (limitation 3)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Our requirement here was to build an API to receive the webhooks from Stripe and persist them on a queue without having extra logic that can become a point of failure and process them with a Lambda function later.&lt;/p&gt;

&lt;p&gt;We knew that API Gateway has native integrations with SQS (and Lambda). &lt;/p&gt;

&lt;p&gt;This wouldn’t be sufficient for us as we had to have some code verifying and constructing the messages at the moment they arrive synchronously (limitation No 3).&lt;/p&gt;

&lt;p&gt;In our implementation, we had to extract the Stripe signature from the headers to use it to verify the event's authenticity and the Stripe event itself. We achieved that by using Mapping Template on the Integration Request. Essentially it allows us to remap the http requests properties to a JSON that would be easily processed by the lambda. In our case, we are mapping the Stripe Signature from the header of the webhook to a stripeSignature parameter and the body of the webhook to a stripeEvent parameter. The mapping code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { Detail } from "@raycast/api";

export default function Command() {
  return &amp;lt;Detail markdown="Hello World" /&amp;gt;;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;‍&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lambda function Gateway integration (limitation No 4)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We ended up choosing to have a native API Gateway Lambda integration that would be super simple, doing as little processing as possible. This is important for us as it is the first component that the external stripe webhook hits when entering our cloud. Failing to persist an event here would cause a failure to act upon that webhook and its data will be lost forever.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { Detail } from "@raycast/api";

export default function Command() {
  return &amp;lt;Detail markdown="Hello World" /&amp;gt;;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This mapping code should be planted in the API Gateway in the Integration requests:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ggPC95kL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tpmeqyn1h7lt632xb0z5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ggPC95kL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tpmeqyn1h7lt632xb0z5.png" alt="Integrations request" width="617" height="248"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--UFbzL4Tx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gxbceozog0j878f2zsqc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--UFbzL4Tx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gxbceozog0j878f2zsqc.png" alt="Mapping template" width="800" height="583"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;‍&lt;/p&gt;

&lt;p&gt;Moreover, we are very carefully monitoring this lambda for errors to actively know if we are missing any webhooks immediately. According to limitation No 4, it will take Stripe a few days to let us know if there is a problem with the webhooks. As Stripe webhooks are so crucial to our business, we can’t afford not knowing that something is wrong with them only in a few days.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS SQS with DLQ for reprocessing&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We already rely heavily in Stigg on having SQS with DLQs deployed alongside in order to help us scale quickly and process with multiple lambdas when needed. We also rely on that architecture to automatically retry or manually reprocess messages that have failed after fixing the issue causing the failure.&lt;/p&gt;

</description>
      <category>api</category>
      <category>stripe</category>
      <category>javascript</category>
      <category>react</category>
    </item>
  </channel>
</rss>
