<?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: Amogh Jalan</title>
    <description>The latest articles on DEV Community by Amogh Jalan (@amoghjalan).</description>
    <link>https://dev.to/amoghjalan</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%2F506008%2F7b07a58c-e94a-406f-bf6f-eac8de71ffe3.png</url>
      <title>DEV Community: Amogh Jalan</title>
      <link>https://dev.to/amoghjalan</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/amoghjalan"/>
    <language>en</language>
    <item>
      <title>Overcoming Hard Rate Limits: Efficient Rate Limiting with Token Bucketing and Redis</title>
      <dc:creator>Amogh Jalan</dc:creator>
      <pubDate>Mon, 29 Jul 2024 06:53:11 +0000</pubDate>
      <link>https://dev.to/middleware/overcoming-hard-rate-limits-efficient-rate-limiting-with-token-bucketing-and-redis-4gcb</link>
      <guid>https://dev.to/middleware/overcoming-hard-rate-limits-efficient-rate-limiting-with-token-bucketing-and-redis-4gcb</guid>
      <description>&lt;p&gt;As a developer, dealing with external integrations often presents challenges, especially when those integrations impose strict rate limits.&lt;/p&gt;

&lt;p&gt;Recently, I faced a significant issue where our application was repeatedly hitting hard rate limits from an external API, resulting in frequent request failures and breaking our data ingestion pipeline. &lt;/p&gt;

&lt;p&gt;Initial attempts to manage the situation, such as naive retry mechanisms and manual rate limiting, proved inadequate. This led me to explore more sophisticated solutions: the token bucketing algorithm and exponential backoff, leveraging Redis to ensure scalability in our distributed system.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Challenge: Rate Limits and Failed Requests
&lt;/h2&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%2Fyse9r7f8gmdr5up6z2ds.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%2Fyse9r7f8gmdr5up6z2ds.png" alt="Meme with Status 429 as hurricane"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When interacting with third-party APIs, you often encounter rate limits—restrictions on the number of requests you can make within a given time frame. Exceeding these limits can lead to denied requests, service disruptions, and ultimately a poor user experience. &lt;/p&gt;

&lt;p&gt;In our case, the challenge was compounded by the nature of our traffic and the distributed nature of our system. This issue arose when one of our customers linked all of their 400 repositories to be synced with &lt;a href="https://github.com/middlewarehq/middleware/" rel="noopener noreferrer"&gt;Middleware&lt;/a&gt;, our developer productivity analytics platform. &lt;/p&gt;

&lt;p&gt;To give you an idea, the data to sync included over 50,000 pull requests, each requiring about 4 requests, totalling 200,000 requests. Meanwhile, the rate limits for the account allowed just 2000 requests per hour. &lt;br&gt;
Simple solutions like fixed delays and single-server rate limiting were insufficient, prompting the need for a more robust approach.&lt;/p&gt;
&lt;h2&gt;
  
  
  High-Level Solution: Traffic Shaping with Token Bucketing and Exponential Backoff
&lt;/h2&gt;

&lt;p&gt;To address the rate limit issue effectively, I aimed to implement a traffic-shaping strategy combining the token bucketing algorithm and exponential backoff. Here's a high-level overview of these concepts:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Traffic Shaping&lt;/strong&gt;: Managing the flow of requests to ensure smooth interaction with the external API without exceeding rate limits.

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Token Bucketing&lt;/strong&gt;: A rate-limiting algorithm that allows bursts of traffic while enforcing a steady rate over time.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Exponential Backoff&lt;/strong&gt;: A retry mechanism that increases the wait time between retries exponentially, reducing the likelihood of overwhelming the external API.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;
  
  
  Understanding Traffic Shaping
&lt;/h2&gt;

&lt;p&gt;Traffic shaping involves controlling the rate at which requests are sent to an external service. By implementing a token bucketing system, we can allow short bursts of traffic while maintaining a consistent request rate over time. This prevents overwhelming the external service and reduces the risk of hitting rate limits.&lt;/p&gt;
&lt;h3&gt;
  
  
  The Token Bucketing Algorithm
&lt;/h3&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%2F0a9f2mn9rh4lhipiqxqx.png" alt="token bucketing algorithm visualization"&gt;
    &lt;p&gt;&lt;em&gt;Source: &lt;a href="https://www.geeksforgeeks.org/token-bucket-algorithm/" rel="noopener noreferrer"&gt;GeeksforGeeks&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;


&lt;p&gt;The token bucket algorithm is a simple yet effective method for rate limiting. Here’s how it works:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A bucket holds a certain number of tokens, each token representing permission to make one request.&lt;/li&gt;
&lt;li&gt;Tokens are added to the bucket at a fixed rate.&lt;/li&gt;
&lt;li&gt;When a request is made, a token is removed from the bucket. If the bucket is empty, the request is denied or delayed.&lt;/li&gt;
&lt;li&gt;The bucket has a maximum capacity, preventing it from holding more tokens than a predefined limit.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;
  
  
  Finding the Right Max Tokens and Refill Rate
&lt;/h4&gt;

&lt;p&gt;Determining the optimal values for the maximum tokens and refill rate requires understanding your application's traffic patterns and the external API's rate limits. For example, if an API allows 100 requests per minute:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Max Tokens: Set to 100, allowing a burst of up to 100 requests.&lt;/li&gt;
&lt;li&gt;Refill Rate: Set to approximately 1.67 tokens per second (100 tokens/60 seconds).&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Exponential Backoff Algorithm
&lt;/h2&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%2F5mu06z7fd3iui2gzmhxv.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%2F5mu06z7fd3iui2gzmhxv.png" alt="Exponential Backoff Algorithm Graph"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Exponential backoff is a retry strategy where the wait time between retries increases exponentially. This approach reduces the load on the external API by spacing out retries and avoiding a flood of requests after an initial failure.&lt;/p&gt;

&lt;p&gt;Here's a simple implementation in pseudocode:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;initial_delay = 1 second
max_delay = 32 seconds
attempts = 0

while (attempts &amp;lt; max_attempts) {
    result = make_request()
    if (result.success) {
        break
    }
    delay = min(initial_delay * 2^attempts, max_delay)
    sleep(delay)
    attempts += 1
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  Integrating Token Bucketing and Exponential Backoff with Redis
&lt;/h2&gt;

&lt;p&gt;Redis, an in-memory data structure store, is an excellent choice for implementing rate limiting in a distributed system due to its speed and support for atomic operations. &lt;/p&gt;
&lt;h3&gt;
  
  
  Why Redis?
&lt;/h3&gt;

&lt;p&gt;Redis is chosen for its robust feature set that perfectly complements the needs of a distributed rate limiting solution:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Scalability&lt;/strong&gt;: Handles high throughput with low latency, making it suitable for distributed systems.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Atomic Operations&lt;/strong&gt;: Ensures that token bucket updates and checks are performed reliably across multiple instances.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Persistence&lt;/strong&gt;: Optionally persists data to disk, ensuring that the rate limiting state survives restarts.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;
  
  
  Implementing Token Bucketing with Redis
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;redis&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;

&lt;span class="n"&gt;redis_client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;redis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;StrictRedis&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;host&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;localhost&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;6379&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_tokens&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bucket_key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;refill_rate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;max_tokens&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="err"&gt;    &lt;/span&gt;&lt;span class="n"&gt;current_time&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;time&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="err"&gt;    &lt;/span&gt;&lt;span class="n"&gt;last_refill_time&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;redis_client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hget&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bucket_key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;last_refill_time&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;current_time&lt;/span&gt;
&lt;span class="err"&gt;    &lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;redis_client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hget&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bucket_key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;tokens&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;max_tokens&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="err"&gt;    &lt;/span&gt;
&lt;span class="err"&gt;    &lt;/span&gt;&lt;span class="n"&gt;elapsed_time&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;current_time&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nf"&gt;float&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;last_refill_time&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="err"&gt;    &lt;/span&gt;&lt;span class="n"&gt;new_tokens&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;elapsed_time&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;refill_rate&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="err"&gt;    &lt;/span&gt;
&lt;span class="err"&gt;    &lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;new_tokens&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;max_tokens&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="err"&gt;    &lt;/span&gt;&lt;span class="n"&gt;redis_client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bucket_key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;tokens&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="err"&gt;    &lt;/span&gt;&lt;span class="n"&gt;redis_client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bucket_key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;last_refill_time&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;current_time&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="err"&gt;    &lt;/span&gt;
&lt;span class="err"&gt;    &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;tokens&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;consume_token&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bucket_key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;refill_rate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;max_tokens&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="err"&gt;    &lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_tokens&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bucket_key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;refill_rate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;max_tokens&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="err"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;tokens&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="err"&gt;        &lt;/span&gt;&lt;span class="n"&gt;redis_client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hincrby&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bucket_key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;tokens&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="err"&gt;        &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;
&lt;span class="err"&gt;    &lt;/span&gt;&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="err"&gt;        &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;By combining token bucketing and exponential backoff, we can manage external API rate limits effectively. Redis provides the necessary infrastructure to implement these algorithms in a distributed system, ensuring scalability and reliability. &lt;/p&gt;

&lt;p&gt;This approach not only prevents service disruptions but also enhances the overall user experience by maintaining a smooth and consistent interaction with third-party services.&lt;/p&gt;

&lt;p&gt;By the way if you are looking to improve developer productivity in your organization check out &lt;a href="https://github.com/middlewarehq/middleware/" rel="noopener noreferrer"&gt;Middleware&lt;/a&gt;.&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&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%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/middlewarehq" rel="noopener noreferrer"&gt;
        middlewarehq
      &lt;/a&gt; / &lt;a href="https://github.com/middlewarehq/middleware" rel="noopener noreferrer"&gt;
        middleware
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      ✨ Open-source DORA metrics platform for engineering teams ✨
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;p&gt;
&lt;a href="https://www.middlewarehq.com/" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Fmiddlewarehq%2Fmiddleware%2Fraw%2Fmain%2Fmedia_files%2Flogo.png" alt="Middleware Logo" width="300px"&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Open-source engineering management that unlocks developer potential&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;
&lt;a href="https://github.com/middlewarehq/middleware/actions/workflows/build.yml" rel="noopener noreferrer"&gt;&lt;img alt="continuous integration" src="https://camo.githubusercontent.com/d72013fe354cb76bdb62fae188d011c4d1bce7723189bacad6cbde6387cf52e6/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f616374696f6e732f776f726b666c6f772f7374617475732f6d6964646c657761726568712f6d6964646c65776172652f6275696c642e796d6c3f6272616e63683d6d61696e266c6162656c3d6275696c64267374796c653d666f722d7468652d6261646765"&gt;&lt;/a&gt;
&lt;a href="https://github.com/middlewarehq/middleware/graphs/commit-activity" rel="noopener noreferrer"&gt;&lt;img alt="Commit activity per month" src="https://camo.githubusercontent.com/b694441288287fd6f4c8750f3887fcc6853aef9bfc84ee8a0e1e490a7633639a/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f636f6d6d69742d61637469766974792f6d2f6d6964646c657761726568712f6d6964646c65776172653f7374796c653d666f722d7468652d6261646765"&gt;&lt;/a&gt;
&lt;a href="https://github.com/middlewarehq/middleware/graphs/contributors" rel="noopener noreferrer"&gt;&lt;img alt="contributors" src="https://camo.githubusercontent.com/15f7e201a0b0e240425157b1a7251f24a91dcd6b6bbec76af4ad66efda00eeba/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f636f6e7472696275746f72732d616e6f6e2f6d6964646c657761726568712f6d6964646c65776172653f636f6c6f723d79656c6c6f77267374796c653d666f722d7468652d6261646765"&gt;&lt;/a&gt;
&lt;br&gt;
&lt;a href="https://opensource.org/licenses/Apache-2.0" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/44fae73fb8fb80dc9f5673dc4e1d0e57b1ac81da1166a70c8a5ce52bb39ed67f/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f617061636865253230322e302d707572706c652e7376673f7374796c653d666f722d7468652d6261646765266c6162656c3d6c6963656e7365" alt="license"&gt;&lt;/a&gt;
&lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/eab8dfd78113b2679d98f9f33a66e3a157276c68cc4cf3541fa1287f4dddb379/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f73746172732f6d6964646c657761726568712f6d6964646c65776172653f7374796c653d666f722d7468652d6261646765"&gt;&lt;img src="https://camo.githubusercontent.com/eab8dfd78113b2679d98f9f33a66e3a157276c68cc4cf3541fa1287f4dddb379/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f73746172732f6d6964646c657761726568712f6d6964646c65776172653f7374796c653d666f722d7468652d6261646765" alt="Stars"&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;&lt;a href="https://mhq.link/oss-community" rel="nofollow noopener noreferrer"&gt;Join our Open Source Community&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://github.com/middlewarehq/middleware/blob/main/media_files/banner.gif"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Fmiddlewarehq%2Fmiddleware%2Fraw%2Fmain%2Fmedia_files%2Fbanner.gif" alt="Middleware Opensource"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Introduction&lt;/h2&gt;
&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Middleware&lt;/strong&gt; is an open-source tool designed to help engineering leaders measure and analyze the effectiveness of their teams using the &lt;a href="https://dora.dev" rel="nofollow noopener noreferrer"&gt;DORA metrics&lt;/a&gt;. The DORA metrics are a set of &lt;a href="https://dora.dev/guides/dora-metrics-four-keys/" rel="nofollow noopener noreferrer"&gt;four key values&lt;/a&gt; that provide insights into software delivery performance and operational efficiency.&lt;/p&gt;

&lt;p&gt;They are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Deployment Frequency&lt;/strong&gt;: The frequency of code deployments to production or an operational environment.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lead Time for Changes&lt;/strong&gt;: The time it takes for a commit to make it into production.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mean Time to Restore&lt;/strong&gt;: The time it takes to restore service after an incident or failure.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Change Failure Rate&lt;/strong&gt;: The percentage of deployments that result in failures or require remediation.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Table of Contents&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/middlewarehq/middleware#introduction" rel="noopener noreferrer"&gt;Middleware - Open Source&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/middlewarehq/middleware#-features" rel="noopener noreferrer"&gt;Features&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/middlewarehq/middleware#-quick-start" rel="noopener noreferrer"&gt;Quick Start&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/middlewarehq/middleware#-installing-middleware" rel="noopener noreferrer"&gt;Installing Middleware&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/middlewarehq/middleware#%EF%B8%8F-troubleshooting" rel="noopener noreferrer"&gt;Troubleshooting&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;a href="https://github.com/middlewarehq/middleware#-developer-setup" rel="noopener noreferrer"&gt;Developer Setup&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/middlewarehq/middleware#%EF%B8%8F-using-gitpod" rel="noopener noreferrer"&gt;Using Gitpod&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/middlewarehq/middleware#-using-docker" rel="noopener noreferrer"&gt;Using Docker&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/middlewarehq/middleware#%EF%B8%8F-manual-setup" rel="noopener noreferrer"&gt;Manual Setup&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;&lt;a href="https://github.com/middlewarehq/middleware#-usage" rel="noopener noreferrer"&gt;Usage&lt;/a&gt;&lt;/li&gt;

&lt;li&gt;&lt;a href="https://github.com/middlewarehq/middleware#-how-we-calculate-dora-metrics" rel="noopener noreferrer"&gt;How we Calculate DORA&lt;/a&gt;&lt;/li&gt;

&lt;li&gt;&lt;a href="https://github.com/middlewarehq/middleware#%EF%B8%8F-roadmap" rel="noopener noreferrer"&gt;Roadmap&lt;/a&gt;&lt;/li&gt;

&lt;li&gt;&lt;a href="https://github.com/middlewarehq/middleware#%EF%B8%8F-contributing-guidelines" rel="noopener noreferrer"&gt;Contributing guidelines&lt;/a&gt;&lt;/li&gt;

&lt;li&gt;…&lt;/li&gt;

&lt;/ul&gt;

&lt;/li&gt;

&lt;/ul&gt;
&lt;/div&gt;
&lt;br&gt;
  &lt;/div&gt;
&lt;br&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/middlewarehq/middleware" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;br&gt;
&lt;/div&gt;
&lt;br&gt;


&lt;p&gt;PS: Do upvote the post for good karma 😇!!&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
      <category>productivity</category>
      <category>architecture</category>
    </item>
  </channel>
</rss>
