<?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>Forem: jspiliot</title>
    <description>The latest articles on Forem by jspiliot (@jspiliot).</description>
    <link>https://forem.com/jspiliot</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%2F1143858%2F192b2aee-1201-448a-ae6a-12d990ed4794.jpeg</url>
      <title>Forem: jspiliot</title>
      <link>https://forem.com/jspiliot</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/jspiliot"/>
    <language>en</language>
    <item>
      <title>Does size matter in Pull Requests: Analysis on 30k Developers</title>
      <dc:creator>jspiliot</dc:creator>
      <pubDate>Fri, 24 Nov 2023 17:12:01 +0000</pubDate>
      <link>https://forem.com/adadot/does-size-matter-in-pull-requests-analysis-on-30k-developers-1aef</link>
      <guid>https://forem.com/adadot/does-size-matter-in-pull-requests-analysis-on-30k-developers-1aef</guid>
      <description>&lt;p&gt;At one point or another you might have found yourself putting a Pull Request up for review that was significantly bigger than what you were expecting it to be. And you found yourself wondering:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;“How big should it really be? Is there a sweet spot for the size of a review? If we could theoretically always fully control it, how big should we make it?”&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You googled around, and you found a lot of resources, sites, and articles like this one, analysing the subject and ending up with something along the lines of:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;“Too few lines might not offer a comprehensive view of the changes, while an excessively large PR can overwhelm reviewers and make it challenging to identify issues or provide meaningful feedback”&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And although you understood the sentiment of the writer, you also understood that the theoretical answer could only be vague, as there is not a silver bullet. As always life is more complicated than that.&lt;/p&gt;

&lt;p&gt;What we are gonna be doing in this article is something different however:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;“We will analyze the PRs of ~30k developers to see how the size of PRs correlates with lead time, comments received and change failure, to try and find what statistically is the best size, as well as examine what affects it.”&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;u&gt;Disclaimer&lt;/u&gt;: For anyone who has played around with data, and especially if you did any courses/training in data, the above might bring back some memories of this phrase &lt;em&gt;“Correlation does not mean causation”&lt;/em&gt;. First of all hello to you my fellow scholar, and secondly you are absolutely right. We will try to look at it from various angles to see how this correlation varies by company, developer, per developer and amount of code committed, and any other angles which might help us understand what others values, for any reason, follow relevant patterns. However, these are “only” numbers and correlations, they do not explain the reason behind them, so any assumptions for causes that we make are more anecdotal and less scientifically backed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Methodology
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Lead Time
&lt;/h3&gt;

&lt;p&gt;In this case we use as lead time the time between the earliest PR event (either 1st commit, or PR open), and when the PR gets merged in.&lt;/p&gt;

&lt;h3&gt;
  
  
  Data Preparation
&lt;/h3&gt;

&lt;p&gt;Data that are removed as outliers:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;PRs that had a lead time of more that 6 months&lt;/li&gt;
&lt;li&gt;PRs that had a lead time of less than 5 minutes&lt;/li&gt;
&lt;li&gt;File changes of more than 20k lines&lt;/li&gt;
&lt;li&gt;PRs with more than 20k line changes&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;After we have done that we have a few hundreds of thousands of merged Pull Requests that are used to produce the below analysis.&lt;/p&gt;

&lt;h3&gt;
  
  
  Algorithm
&lt;/h3&gt;

&lt;p&gt;All Correlations have been done using the kendall tau method, which should be able to better estimate the correlation in the case of non-linear relationships.&lt;/p&gt;

&lt;h2&gt;
  
  
  How does Lead Time relate to PR size
&lt;/h2&gt;

&lt;p&gt;Before we go more deeply, intuitively we expect that the size of a PR should correlate in one way or another with the lead time, but is it actually the case? Running correlation between the two variables for the whole dataset, gives us as a result the below correlation matrix. &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%2Finsightsadadot.files.wordpress.com%2F2023%2F11%2Fimage-2.png%3Fw%3D346" 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%2Finsightsadadot.files.wordpress.com%2F2023%2F11%2Fimage-2.png%3Fw%3D346" alt="PR size to Lead Time correlation"&gt;&lt;/a&gt;PR size to Lead Time correlation&lt;/p&gt;

&lt;p&gt;From these numbers we could say that there seems to be some correlation between the two variables but it seems to be a bit above the limit of statistical insignificance, meaning that: &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Their correlation is there, but is not very strong, maybe less than one would have expected.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Seems like we’ll have to dig deeper to see why this correlation appears to be so weak, and unfortunately, plotting the graph of total line changes to lead time if anything makes things less clear, as although the trend seems to suggest that the ones with the higher lead time had slightly bigger size on average, we see that any link between them is not so clear to see.&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%2Finsightsadadot.files.wordpress.com%2F2023%2F11%2Fimage-9.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%2Finsightsadadot.files.wordpress.com%2F2023%2F11%2Fimage-9.png" alt="Total PR size to Lead Time"&gt;&lt;/a&gt;Total PR size to Lead Time&lt;/p&gt;

&lt;p&gt;Now, if we change this chart a bit, by grouping the data points by day, and taking the median of the total changes by day, we start to see a bit more clearly how they relate and potentially an explanation for why their correlation is not that high.&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%2Finsightsadadot.files.wordpress.com%2F2023%2F11%2Fimage-12.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%2Finsightsadadot.files.wordpress.com%2F2023%2F11%2Fimage-12.png" alt="Mean total PR size to daily Lead Time"&gt;&lt;/a&gt;Mean total PR size to daily Lead Time&lt;/p&gt;

&lt;p&gt;So this suggests that at fast lead times the PRs are consistently low in lines changed, and as they get bigger there is a linear increase on the lead time. However, higher lead times can be produced by any size of PR and the correlation is very low between them.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is the best size
&lt;/h2&gt;

&lt;p&gt;To try and answer this question, we’d first have to ask ourselves what is it that matters to us, ie what are we trying to optimize for. Now, that is a question with endless possibilities. For our purposes however, we will examine what is the largest size of PR that &lt;strong&gt;statistically&lt;/strong&gt; works better given these 3 wants:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Low lead time (aka be done fast)&lt;/li&gt;
&lt;li&gt;High number of comments (not too big to review properly)&lt;/li&gt;
&lt;li&gt;Low defects/reverts (aka we are not breaking things)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If we plot in a heatmap the probability of a PR getting done in a number of weeks to the size we get the below.&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%2Finsightsadadot.files.wordpress.com%2F2023%2F11%2Fimage-11.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%2Finsightsadadot.files.wordpress.com%2F2023%2F11%2Fimage-11.png" alt="Heatmap of probability of PR of a size (x axis) getting done in a number of weeks (y axis)"&gt;&lt;/a&gt;Heatmap of probability of PR of a size (x axis) getting done in a number of weeks (y axis)&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Meaning that a PR of less than 100 lines of code has ~80% chance of getting done within the first week&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A similar heatmap for the amount of comments gives us the below.&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%2Finsightsadadot.files.wordpress.com%2F2023%2F11%2Fimage-7.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%2Finsightsadadot.files.wordpress.com%2F2023%2F11%2Fimage-7.png" alt="Heatmap of probability of a PR of a size (x axis) getting an amount of comments (y axis)"&gt;&lt;/a&gt;Heatmap of probability of a PR of a size (x axis) getting an amount of comments (y axis)&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Which means that a PR of 6000 lines of code has the same probability of getting 0 review comments as much as a review of less than 50 lines of code.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And finally doing the same for the probability of reverts gives us the below heatmap, and depicting the probability of no commits from that PR getting reverted gives us the below.&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%2Finsightsadadot.files.wordpress.com%2F2023%2F11%2Fimage-4.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%2Finsightsadadot.files.wordpress.com%2F2023%2F11%2Fimage-4.png" alt="Probability of a PR of a size (x axis) not having to be reverted (0 reverts)"&gt;&lt;/a&gt;Probability of a PR of a size (x axis) not having to be reverted (0 reverts)&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Which means that generally larger size PRs have a larger probability of being having some parts of their code reverted (ie faulty)&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;From the above if we plot on the same graph the probabilities of completing an PR within the 1st week, the probability of getting at least 1 or more comments, and the probability to not have to revert a commit from that PR, we get the below.&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%2Finsightsadadot.files.wordpress.com%2F2023%2F11%2Fimage-8.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%2Finsightsadadot.files.wordpress.com%2F2023%2F11%2Fimage-8.png" alt="Probability (y axes) of a PR getting done in a week (blue), to have comments (green) and to be reverted (red) over lines of code"&gt;&lt;/a&gt;Probability (y axes) of a PR getting done in a week (blue), to have comments (green) and to be reverted (red) over lines of code&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Therefore, statistically, below ~400 lines of code per PR gives a good probability of getting some review comments, completing it within the first week and not having issues with the code.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Of course that is only “statistically” the case. It surely depends on a lot of things. Let examine some potential ones.&lt;/p&gt;

&lt;h2&gt;
  
  
  Does it depend on the user
&lt;/h2&gt;

&lt;p&gt;We would potentially expect it to vary per user, but how different it could be per user, either that being the author or the reviewer, could be more interesting. After removing all users that have one or more of the below:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Less than 10 merged PRs&lt;/li&gt;
&lt;li&gt;Less than 10 commits&lt;/li&gt;
&lt;li&gt;Less than 100 lines of code changed&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And all reviewers that have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Less than 10 approved and merged PRs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We perform correlation analysis between Lead Time and PR size per user. If we then put the result of the analysis on a histogram showing how many users had each of the correlation value, we get the below charts:&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%2Finsightsadadot.files.wordpress.com%2F2023%2F11%2Fimage-3.png%3Fw%3D1024" 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%2Finsightsadadot.files.wordpress.com%2F2023%2F11%2Fimage-3.png%3Fw%3D1024" alt="Histograms of Lead Time to PR size correlation per amount of unique PR authors (left) and PR reviewers (right)"&gt;&lt;/a&gt;Histograms of Lead Time to PR size correlation per amount of unique PR authors (left) and PR reviewers (right)&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;The correlation between Lead Time and PR size heavily depends on the PR author as well as the PR reviewer&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;There are a wide range of reasons why that could potentially happen, like level of seniority, company/team process, coding language, review tool, etc. &lt;/p&gt;

&lt;p&gt;Below we plot the relation of the correlation depending on the amount of lines of code a developer has written throughout the last 6 months. Although that instinctively could lead us to think that that means a more “experienced” developer, it is not necessarily true, as it may be also affected by multiple factors, such as eg amount of meetings, mentoring, collaboration per day, which could vary on seniority, the tasks each one took up, etc., and so on and so forth. &lt;/p&gt;

&lt;p&gt;Nonetheless we depict it here for anyone that might find it interesting. Also keep in mind that the difference in the correlation between a user with many PRs merged and few is not a very large one.&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%2Finsightsadadot.files.wordpress.com%2F2023%2F11%2Fimage-5.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%2Finsightsadadot.files.wordpress.com%2F2023%2F11%2Fimage-5.png" alt="Lead Time to PR size correlation value for developers per code committed within the last 6 months"&gt;&lt;/a&gt;Lead Time to PR size correlation value for developers per code committed within the last 6 months&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;The more lines one has written the more correlated the PR size is with the lead time. This could also mean that lead time becomes more predictable in this case, and it depends more heavily on the size of the PR and not other parameters (e.g. complexity). However, more analysis would be required to establish that.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Does it depend on the company
&lt;/h2&gt;

&lt;p&gt;We mentioned earlier that there are various potential reasons for a correlation between Lead Time and PR size, and we also said that the cause of the strength of the correlation would also be multivariate. One of the potential causes being company/team processes. If that would be the case we’d expect to see the correlation varies by the company. &lt;/p&gt;

&lt;p&gt;Taking a small sample of companies and examining the strength of that correlation seems to suggest that that is a valid assumption as well, as we can see here it varies from 0.1 suggesting that the two metrics are not related at all for the specific company to almost 0.7 suggesting a relatively strong correlation between the two.&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%2Finsightsadadot.files.wordpress.com%2F2023%2F11%2Fimage-10.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%2Finsightsadadot.files.wordpress.com%2F2023%2F11%2Fimage-10.png" alt="Lead Time to PR size mean correlation per company (sample)"&gt;&lt;/a&gt;Lead Time to PR size mean correlation per company (sample)&lt;/p&gt;

&lt;p&gt;How much PR size relates to Lead Time seems to depend heavily on the specific company &lt;/p&gt;

&lt;h2&gt;
  
  
  Does it change over time
&lt;/h2&gt;

&lt;p&gt;It absolutely does, and massively so! Unfortunately, it’s rather hard to depict that for everyone in a single chart. However, I’m putting here my own correlation over time that I got from our &lt;a href="https://me.adadot.com/case-analysis" rel="noopener noreferrer"&gt;free analytics platform&lt;/a&gt; so you can get an image of how much it can vary.&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%2Finsightsadadot.files.wordpress.com%2F2023%2F11%2Fimage-6.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%2Finsightsadadot.files.wordpress.com%2F2023%2F11%2Fimage-6.png" alt="Lead Time to PR Size Correlation over time chart for myself"&gt;&lt;/a&gt;Lead Time to PR Size Correlation over time chart for myself&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;We examined the correlation between Lead Time and PR size to try and see if we can draw some conclusions about what is the size we should be aiming for. We found that statistically there are some generalisations we can do and estimate an optimal size. However we also came to the conclusion that the link between them heavily depends on the company, the team and even the individual developer. Which seems to suggest in the end that:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Each developer works in unique ways, and only you, if anyone, knows what is the optimal for you, and your team.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now If you would like to check where you or your team/company stands wrt this correlation between Lead Time and PR size, we created a simple way for developers and teams to get insight on how this correlation changes over  and see where &lt;strong&gt;they&lt;/strong&gt; stand, either individual, team, or as a whole company. If you are curious about it, feel free to check it out.&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%2Finsightsadadot.files.wordpress.com%2F2023%2F11%2Fimage-13.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%2Finsightsadadot.files.wordpress.com%2F2023%2F11%2Fimage-13.png" alt="Example correlation analysis page"&gt;&lt;/a&gt;Example correlation analysis page&lt;/p&gt;

</description>
      <category>programming</category>
      <category>productivity</category>
      <category>codenewbie</category>
      <category>git</category>
    </item>
    <item>
      <title>Serverless at Scale: Lessons From 200 Million Lambda Invocations</title>
      <dc:creator>jspiliot</dc:creator>
      <pubDate>Fri, 10 Nov 2023 11:15:42 +0000</pubDate>
      <link>https://forem.com/adadot/serverless-at-scale-lessons-from-200-million-lambda-invocations-46h3</link>
      <guid>https://forem.com/adadot/serverless-at-scale-lessons-from-200-million-lambda-invocations-46h3</guid>
      <description>&lt;p&gt;Serverless computing, with Lambda functions at the heart of it, has irrevocably changed the way we build and scale applications, more than anything, by adding another question to the list of questions in the beginning of every project: &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Should we do this serverless”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And although sometimes the answer is as simple as “None of our other systems are serverless, let’s not start mixing them”, a lot of other times it’s more complicated than that. Partially because:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Serverless architecture promises flexibility, infinite scalability, fast setups, cost efficiency, and abstracting infrastructure allowing us to focus on the code. But does it deliver?”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;With a technology that makes these promises it’s hard to mute that voice inside us asking questions like: &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“what if it’s easier to maintain? Maybe it’s at least easier to spin up? Could it make us go faster? Would it be cheaper? What if we can scale more easily?”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Therefore, sooner or later we all will be intrigued enough to search it more, or implement it in a PoC just to see if it makes sense to go forward. And that’s where it gets tricky!&lt;/p&gt;

&lt;p&gt;At Adadot we ran over 200 million Lambdas in the last year. In this year-long journey to harness the power of Lambda functions, we saw both good and sides of them that we didn’t really expect.&lt;/p&gt;

&lt;h1&gt;Our Setup&lt;/h1&gt;

&lt;p&gt;&lt;em&gt;Context: At Adadot we are dealing with developer data, which at the end of the day means A LOT of data. So capturing and processing big volumes of data is our focus.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;When we started, we were a small team of a couple of software engineers, and we didn’t know how the system requirements would need to scale over the next year(s). We had very little production experience of serverless architectures, but each one of us a decade with "traditional" architectures. We &lt;em&gt;thought&lt;/em&gt; we at least knew the below about serverless:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It would scale "infinitely", as long as we have enough money to throw on it, so it could save us in a scenario of unexpected traffic growth.&lt;/li&gt;


&lt;li&gt;We won’t have to pay almost anything as long as our traffic is very low&lt;/li&gt;

&lt;li&gt;Lambdas specifically have high warmup times, and we dont wanna have to manage warm up strategies, or use provisioned capacity (I’ll explain this in-depth in another article)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So since we had no clue how our traffic would be over the next years, we took early on a decision on our architecture’s principles:&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%2Finsightsadadot.files.wordpress.com%2F2023%2F11%2Fserverless_decision.jpg%3Fw%3D426" 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%2Finsightsadadot.files.wordpress.com%2F2023%2F11%2Fserverless_decision.jpg%3Fw%3D426" alt=""&gt;&lt;/a&gt;&lt;/p&gt;


&lt;li&gt;Any API that is customer-facing, needs fast response and doesn’t do heavy operations will be handled by an always-on server.&lt;/li&gt;


&lt;li&gt;Any other function would be run in lambdas.&lt;/li&gt;


&lt;li&gt;Anything else that can be serverless (databases, queues, streams etc) will be serverless.&lt;/li&gt;


&lt;li&gt;Cost evaluation is to be performed periodically as traffic gets more predictable to re-evaluate.&lt;/li&gt;

&lt;p&gt;Fast forward 2 years, and especially due to our requirements on what to deliver to the end-users, we ended up with a serverless-heavy architecture, which includes only 1 “monolithic” API server and some always-on databases, but also about 200 lambdas, 100 step functions, 100 kinesis streams, 100 SQS, DynamoDB (including streams), Redshift &amp;amp; Neptune clusters, API Gateways and a whole bunch on AWS networking components.&lt;/p&gt;

&lt;p&gt;And all that is how we got to us running over 200 million lambdas in the last year alone. However, as you may have deduced from the above these were not serving customer-facing APIs, but rather various tasks we had to do on the backend, based on various different triggers and events in order to have all of your data ready, in the right format, and with the required analyses done so our customers could have access to them.&lt;/p&gt;

&lt;p&gt;So without further ado, let’s move on to the more interesting part of what we learned out of this year.&lt;/p&gt;

&lt;h1&gt;Ease &amp;amp; Speed&lt;/h1&gt;

&lt;p&gt;So are Lambdas easier to spin up? For us that has been a constant and resounding “yes” throughout our whole journey. We found that Lambdas need significantly less configuration and setup in order to spin up and start working, and less initial thinking over instance sizing configurations. We are using the serverless framework for our Lambdas, so in order to have a Lambda function up and running it’s as simple as creating the serverless.yml file and running `serverless deploy`. We mainly use JS (with TypeScript) and Python, so something like the below is enough to have a function up and running even including bundling, IAM permissions, environmental variables, x-ray tracing, custom layers and really everything needed for each one. So really once you’ve written the function in 5 minutes you are ready to go.&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%2Finsightsadadot.files.wordpress.com%2F2023%2F11%2Fimage.png%3Fw%3D1024" 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%2Finsightsadadot.files.wordpress.com%2F2023%2F11%2Fimage.png%3Fw%3D1024" alt="serverless.yml for TypeScript and Python"&gt;&lt;/a&gt;serverless.yml for TypeScript and Python&lt;/p&gt;

&lt;p&gt;That being said, we use Terraform to manage the rest of our infrastructure, but we found that managing Lambda functions using Terraform was of significantly bigger complexity. That means however, that we have 2 different ways to manage our infrastructure, and they don't play that well between them, meaning that they don’t really communicate resource information, so unfortunately that means, that there are cases that the id of a resource needs to be hardcoded or put as a wildcard to be used by the other.&lt;/p&gt;

&lt;h1&gt;Infinite (or not) Scaling&lt;/h1&gt;

&lt;p&gt;Usually serverless architectures are presented as being “infinitely horizontally scaling”, which is, at most times, too good to be true. And I’m obviously not talking about the practical impossibility of anything reaching infinity, but a much much more tangible limit. In the case of Lambdas that limit is the maximum amount of unreserved - or reserved - concurrency. These two types of concurrencies can be summarized as below:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Unreserved concurrency is the maximum amount of concurrent Lambda executions for an AWS account, once you reach that amount you are getting throttled by AWS (ouch!)&lt;/li&gt;

&lt;li&gt;Reserved concurrency is an amount of the unreserved concurrency that you have reserved for a specific Lambda function. That amount can only be used by that function and is the maximum amount that that function can use.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So unreserved is the maximum amount for the account. For our region that limit is set to 1000. That might sound like a lot when you start, but you quickly realise it’s easy to reach once you have enough Lambdas and you scale up. We found ourselves hitting that limit a lot once our traffic and therefore our demands from our system started scaling. &lt;/p&gt;

&lt;p&gt;For us the answer was to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;implement an improved caching logic and not rerun heavy operations when they had already been calculated before and&lt;/li&gt;

&lt;li&gt;spread the calculation loads throughout the day, depending on the end-users timezone.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You can see the difference these made for us in the graph below (note that we had less than 700 unreserved concurrency because we had reserved the rest).&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%2Finsightsadadot.files.wordpress.com%2F2023%2F11%2Fimage-1.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%2Finsightsadadot.files.wordpress.com%2F2023%2F11%2Fimage-1.png" alt="Daily concurrent lambda execution"&gt;&lt;/a&gt;Daily concurrent lambda execution&lt;/p&gt;

&lt;h1&gt;Function Resources&lt;/h1&gt;

&lt;p&gt;Upon setting up a Lambda function you have to set up the amount of memory for the container, and that will internally be reflected as a CPU specification for the Lambda function container. There is a limit of 10Gb. Although if we were to do everything with Lambda we would reach that limit easily, it generally hasn’t been an issue for us. Where the issue lies is that &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“cost of lambda  = memory * execution time”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Which means these settings have massively affected the cost of the Lambdas. All good up to here, however, it gets more complicated if the function load is not always constant, but  depends on some external parameters (e.g. the volume of data for the user). You are then forced to put the Lambda memory on the highest setting even if it’s the 99th percentile of your Lambdas’ loads, and overpay 99% of the times. You can not just say “take as much as you need” (for obvious reasons). So&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;the classic old problem of “how big should our server be” is still there, you just don't need to ask “how many of them” as long as they follow the other limits&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1&gt;Storage Limits&lt;/h1&gt;

&lt;p&gt;Unless you go for the hard path of using Fargates (which we had to do in cases), Lambdas have a size limit of 10GB container image (uncompressed including layers), and for all account’s Lambdas combined there is a limit of 75GB. That might sound like plenty, and it might indeed be, but it heavily depends on what language you use for your Lambda. If you use a language that has a sort of tree-shaking so you upload only what you really use (e.g. JS) then you are probably covered forever. On the other hand, for other languages (looking at you, Python), 10GB is hardly enough. Once you import Pandas you’re on the limit. You can forget Pandas and scipy at the same Lambda.&lt;/p&gt;

&lt;h1&gt;Time Limits&lt;/h1&gt;

&lt;p&gt;Initially we thought “it’s just like an on-demand server”. It spins up, does what you need and drops down again. Yes and no. It spins up and does what you want, but only up to 15 minutes, and drops again. If a task needs more than 15 minutes to finish you have to go a different way. Also, of course, if a function runs 99.9% of the time relatively fast, but the rest 0.01% it takes consistently more than 15 minutes, you need to find an alternative, or break it into smaller pieces. This is part of why we have about 200 Lambda functions, as we were not really happy with the logging retention of Fargate, we chose to stay mostly on Lambdas and kept breaking them into executable chunks. Also in our case a lot of the timeouts were expected and acceptable due to backoffs on the downstream resources, so our system is resilient to this. Still our execution duration graph shows how far is the average execution time to the maximum.&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%2Finsightsadadot.files.wordpress.com%2F2023%2F11%2Fimage-1-2.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%2Finsightsadadot.files.wordpress.com%2F2023%2F11%2Fimage-1-2.png" alt="Max, min and average daily lambda execution durations"&gt;&lt;/a&gt;Max, min and average daily lambda execution durations&lt;/p&gt;

&lt;h1&gt;Monitor and Analyze&lt;/h1&gt;

&lt;p&gt;Proper monitoring and logging are essential for maintaining visibility into your serverless application. Especially when they are triggered not by customer-request, but by events. You really should not just fire and forget them, cause it’s really easy for nothing to work without anyone noticing unless you go above and beyond on your monitoring and alerting. &lt;/p&gt;

&lt;p&gt;AWS provides tools like CloudWatch, X-Ray, and CloudTrail, which help you gain insights into your Lambda functions' performance, trace requests, and monitor your entire architecture. By setting up alarms and automating responses, you can ensure that your application remains healthy and responsive.&lt;/p&gt;

&lt;p&gt;We combine all of these AWS services to get full coverage. Also on alert states, we notify the team through specific Slack channels, and we fight constantly against false positives to keep these channels as quiet as possible when no intervention from the team is required. Now due to the fact that some failures are expected, this is a complicated thing to get right so you only get notified for what “matters”.&lt;/p&gt;

&lt;h1&gt;Emphasize Resilience&lt;/h1&gt;

&lt;p&gt;Apart from the classic resiliency strategies for unexpected outages from AWS  (multi-AZ, multi-region, etc), in serverless, it’s important to have strategies in place from the beginning to mitigate all the issues if the various places that can go wrong, like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Internal failures due to downstream services&lt;/li&gt;

&lt;li&gt;Internal failures due to bugs (oops!)&lt;/li&gt;

&lt;li&gt;Message payload failures &lt;/li&gt;

&lt;li&gt;Orchestration failures&lt;/li&gt;

&lt;li&gt;AWS throttling errors&lt;/li&gt;

&lt;li&gt;AWS random errors (yes, they do occur)&lt;/li&gt;

&lt;li&gt;Lambda timeouts&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Which means that you need to figure out what happens when it fails, and more importantly what happens with the payload, especially if the task still needs to be run. How you can find it again and how to re-drive it through the system. Ideally this should be automatic, but what happens if the automatic system failed, or tried multiple times and couldn’t succeed?&lt;/p&gt;

&lt;p&gt;To this end we make extended use of Dead-Letter Queues (DLQs) with redrive strategies and alerts on failing to empty the queue, meaning the processing of the message cannot be done.&lt;/p&gt;

&lt;h1&gt;Orchestration&lt;/h1&gt;

&lt;p&gt;SQS, SNS, Dynamo streams, kinesis streams, API gateways, crons, etc, all are very useful to stream, circulate, and trigger processes and lambda functions in particular, but you quickly find yourself in a sea of events that happen whenever wherever, or just wanting to call a Lambda after another one has finished, or orchestrate various operations and procedures in various AWS services. That’s where the Step Functions are coming into place. They are state machines that you can just build using the visual editor and orchestrate any kind of complicated series of events.&lt;/p&gt;

&lt;p&gt;What the catch, you said? Well, there is a limit of 25 thousand events per standard step function (there is also the express but I wouldn’t recommend it, I’ll write another article about step functions and I’ll dive into this). Which means if you have a lot of loops happening (maybe because you broke your lambdas into small pieces for the time limit issue like us), you will end up having to also break your step functions into smaller state machines and call off from the other. It quickly gets fairly complicated, however it still is a great tool that allows you to orchestrate anything fast and easily.&lt;/p&gt;



&lt;h2&gt;Cost&lt;/h2&gt;

&lt;p&gt;As we said cost calculation is relatively simple it’s:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“cost of lambda  = memory * execution time ms”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Or to be more precise&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“cost of lambda  = memory * round_up(execution time ms, 100)”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So with us running 200 million of them, it is time to answer the one of the questions you’ve probably been waiting for. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Was it actually more cost efficient than just running a couple of servers?”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Well let’s see what the maths say:&lt;/p&gt;

&lt;p&gt;First of all let’s calculate how many servers would we need if we were to replace all our lambdas by a set of EC2 servers that had the same specs, for the last year, and taking the average duration per lambda run we had.&lt;/p&gt;

&lt;p&gt;In our case these relations could be described by the relation in the chart, where medium instance is the one with the average specs of our lambdas, and xlarge the one that satisfies our most demanding lambda requirements. (Will explain it more, and beautify it), showing the number of servers to the number of monthly invocations. &lt;/p&gt;

&lt;p&gt;We see that in our case the ~17 million average monthly invocations would be about 25 on-demand, always-on medium instances or 6 xlarge ones. Which would most definitely satisfy the actual needs we’ve had the past year.&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%2Finsightsadadot.files.wordpress.com%2F2023%2F11%2Fimage-1-1.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%2Finsightsadadot.files.wordpress.com%2F2023%2F11%2Fimage-1-1.png" alt="monthly cost per millions of lambda invocations, number of xlarge and number of medium EC2 servers" title="Chart"&gt;&lt;/a&gt;monthly cost per millions of lambda invocations, number of xlarge and number of medium EC2 servers&lt;/p&gt;

&lt;p&gt;So if it is in the end more expensive than just having a couple of servers running all the time (not even taking into account dynamic scaling etc), why would anyone do it? Well, why did we do it at least. We did it because it allowed our team to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Spend less time on infrastructure scaling concerns, and focus more on our goals&lt;/li&gt;

&lt;li&gt;Be ready for almost any load at any time, sudden growth was less of a fear, so when it happened we could focus on customers issues, and not infrastructure&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;So in that essence we were hoping to exchange money for time to focus on other things, and less stress for our team. Did we succeed? Hard to tell, no one really knows how it would have been if we had gone the other direction.&lt;/p&gt;

&lt;h2&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;In conclusion, let’s see where we stand with regards to these bugging questions we had when we started:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Is it easier to spin up: absolutely! &lt;/li&gt;

&lt;li&gt;Is it easier to maintain: No really, just different.&lt;/li&gt;

&lt;li&gt;Does it make your team go faster: It makes starting or making a new “service” faster, but overall you will end up spending that time elsewhere, eg monitoring.&lt;/li&gt;

&lt;li&gt;Is it cheaper: When you have no traffic it absolutely is, later on, it quickly isn’t.&lt;/li&gt;

&lt;li&gt;Can it scale more easily: Only for your very first customers, after that you have same amount of concerns, a lot of the same and some slightly different, but it definitely isn’t fire-and-forget scalability&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And for us, was it worth it? Probably, especially in the beginning, but as costs increase and traffic becomes more predictable we might mix it more with plain, old servers.&lt;/p&gt;

&lt;p&gt;Overall, we've learned valuable lessons about scaling, resource allocation, storage, execution time, monitoring, resilience, and cost efficiency and how these affect each other. &lt;/p&gt;

&lt;p&gt;We were also reminded multiple times that:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“even if something is "infinitely" scaling, the hardest part is that everything that it interacts with needs to be "equally infinitely" scalable”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Which in reality is not easy to achieve.&lt;/p&gt;

&lt;p&gt;Our decision to go serverless aimed to save time, reduce infrastructure concerns, and be ready for any growth at any time. While it has its advantages, the cost-efficiency of serverless depends on the project's specific needs and priorities, and the complexity of use when it comes to actual real-world applications is, as always, more than initially expected or wished for.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>aws</category>
      <category>cloud</category>
      <category>programming</category>
    </item>
  </channel>
</rss>
