<?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: André Eriksson</title>
    <description>The latest articles on DEV Community by André Eriksson (@eandre).</description>
    <link>https://dev.to/eandre</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%2F592353%2F3af77389-9fb0-40dc-ac78-853ca00317c4.jpeg</url>
      <title>DEV Community: André Eriksson</title>
      <link>https://dev.to/eandre</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/eandre"/>
    <language>en</language>
    <item>
      <title>Encore.ts: a new type of framework</title>
      <dc:creator>André Eriksson</dc:creator>
      <pubDate>Thu, 26 Sep 2024 18:01:00 +0000</pubDate>
      <link>https://dev.to/encore/encorets-a-new-type-of-framework-3c48</link>
      <guid>https://dev.to/encore/encorets-a-new-type-of-framework-3c48</guid>
      <description>&lt;p&gt;We recently published &lt;a href="https://dev.to/encore/encorets-9x-faster-than-expressjs-3x-faster-than-bun-zod-4boe"&gt;performance benchmarks&lt;/a&gt; showing how Encore.ts achieves 9x request throughput compared to Express.js, and 2x compared to Fastify.&lt;/p&gt;

&lt;p&gt;But it seems that almost every new JavaScript framework these days claims that it's "lightning fast" or "lightweight", with "clean syntax" or a "minimalistic API". Sound familiar?&lt;/p&gt;

&lt;p&gt;Understandably, the cynicism and scepticism is relatable, and I can't really blame you if you're thinking "do we really need yet another JavaScript backend framework?".&lt;/p&gt;

&lt;p&gt;Still, I'd like to give it a go to convince you otherwise, by explaining how Encore.ts is different. Why it isn't just another backend framework, and why &lt;em&gt;this time&lt;/em&gt; it actually might be worth paying a bit closer attention.&lt;/p&gt;

&lt;h2&gt;
  
  
  Local Development Dashboard
&lt;/h2&gt;

&lt;p&gt;Encore.ts focuses heavily on providing an incredible developer experience, and takes this much further than just providing clean syntax or a tiny bundle size.&lt;/p&gt;

&lt;p&gt;When you start your Encore.ts backend you'll be greeted by Encore's Local Development Dashboard.&lt;/p&gt;

&lt;p&gt;This dashboard provides easy access to everything you need to rapidly develop and debug your application. Check it out:&lt;/p&gt;


    


&lt;h3&gt;
  
  
  Request Tracing
&lt;/h3&gt;

&lt;p&gt;Encore provides a live log of all requests, complete with a full trace of exactly what happened. This includes things like API calls, database queries, Pub/Sub messages, logging, and of course the complete request and response payloads.&lt;/p&gt;



&lt;h3&gt;
  
  
  API Documentation
&lt;/h3&gt;

&lt;p&gt;The Local Development Dashboard provides automatic API documentation for all your API endpoints.&lt;/p&gt;

&lt;p&gt;This is powered by Encore.ts's static analysis, which directly processes your TypeScript type definitions. As a result the API documentation is always up to date, and always accurate.&lt;/p&gt;

&lt;p&gt;Never again worry about keeping the OpenAPI spec up-to-date.&lt;/p&gt;



&lt;h3&gt;
  
  
  API Explorer
&lt;/h3&gt;

&lt;p&gt;Encore.ts also comes with an API Explorer that makes calling your API endpoints extremely simple. It's similar to tools like Postman, but much more powerful.&lt;/p&gt;

&lt;p&gt;Because Encore.ts understands exactly what your API looks like it is pre-populated with all of your API endpoints and the exact fields they expect, including things like HTTP headers and query parameters.&lt;/p&gt;

&lt;p&gt;And like Postman you can save frequently used requests, and easily share them with your team.&lt;/p&gt;

&lt;h3&gt;
  
  
  Architecture Diagrams
&lt;/h3&gt;

&lt;p&gt;Encore.ts is designed from the ground up to support building complex, distributed systems. It supports building not just a single backend service, but an entire network of services that communicate with each other.&lt;/p&gt;

&lt;p&gt;For more complex systems like this, the Local Development Dashboard provides a built-in architecture diagram that represents your entire system, and which services depend on each other.&lt;/p&gt;

&lt;p&gt;It even includes infrastructure resources, like databases and Pub/Sub topics and subscriptions. More on that 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%2Fencore.dev%2Fassets%2Fdocs%2Fflow-diagram.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%2Fencore.dev%2Fassets%2Fdocs%2Fflow-diagram.png" title="Encore Flow"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Integrating infrastructure in application code makes development AND deployment simpler
&lt;/h2&gt;

&lt;p&gt;Typical backend frameworks focus on making it easy to define API endpoints. But modern, cloud-native backends require so much more than that.&lt;/p&gt;

&lt;p&gt;Modern backend applications make use of cloud infrastructure resources like databases, Pub/Sub topics, Cron Jobs, secrets, and so on.&lt;/p&gt;

&lt;p&gt;Once you start breaking down your backend into multiple independent services, using Pub/Sub for event-driven communication is a must-have to ensure your system stays in sync and can scale horizontally.&lt;/p&gt;

&lt;p&gt;It's high time for backend frameworks to step up their game and realize this is a core part of building a backend application. Encore.ts does just that, by providing built-in support for using such infrastructure resources directly in the framework.&lt;/p&gt;

&lt;p&gt;For example, to define a Pub/Sub topic is just a few lines of code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Topic&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;encore.dev/pubsub&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;UserSignupEvent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;userID&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;userSignups&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Topic&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;UserSignupEvent&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;user-signup&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;deliveryGuarantee&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;at-least-once&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then just start publishing messages: &lt;code&gt;await userSignups.publish({userID: "123"})&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Defining a Pub/Sub subscription is just as easy:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Subscription&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;encore.dev/pubsub&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Subscription&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userSignups&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;notify-crm-system&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Notify CRM system of new signup.&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The best part of all of this is that Encore.ts's static analysis understands this, and automatically creates and configures the Pub/Sub topic when you run your application.&lt;/p&gt;

&lt;p&gt;And when you deploy to the cloud you can easily wire up the topic to a real Pub/Sub topic in your cloud provider. Encore.ts comes with built-in integrations with AWS and Google Cloud. If you decide to use the Encore Platform it can even automatically provision the necessary infrastructure for you, directly into your own cloud account.&lt;/p&gt;

&lt;h3&gt;
  
  
  Benefits over Terraform/AWS CDK
&lt;/h3&gt;

&lt;p&gt;If you're familiar with the cloud you've probably heard of (or used) tools like Terraform, AWS CDK or Pulumi. They're tools that allow you to describe the infrastructure you need using code, and then automatically provision that infrastructure.&lt;/p&gt;

&lt;p&gt;Compared to clicking around in the cloud provider console this offers several benefits, like reproducibility and version control. But it also has several drawbacks:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Local development&lt;/strong&gt;: The infrastructure setup is entirely specific to the cloud provider, making anything but the simplest setup difficult to run locally.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Error prone&lt;/strong&gt;: Differences between local development and production can cause bugs that are difficult to catch before they cause production issues.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Cloud Provider-specific&lt;/strong&gt;: Since code is entirely specific to your cloud provider, you end up being quite locked-in to that provider.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Production-centric&lt;/strong&gt;: Since you tailor the infrastructure setup heavily towards your production setup, it's very difficult to have a different setup for testing or staging environments. This causes those environments to be very expensive as they mirror production unnecessarily closely. And any divergence from that causes additional complexity, leading to bugs and incidents.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Encore.ts solves all these problems by defining the infrastructure resources directly in code, in a cloud provider-agnostic way. Encore.ts provides built-in adapters for major cloud providers, so you can easily switch between them while preserving the application semantics.&lt;/p&gt;

&lt;p&gt;And last but not least, Encore automatically sets up all the necessary infrastructure locally, so you can easily develop and test your application locally without having to write a bunch of mocks or docker-compose manifests.&lt;/p&gt;

&lt;h2&gt;
  
  
  TypeScript-native API validation
&lt;/h2&gt;

&lt;p&gt;If you're familiar with TypeScript you might know that it needs to be transpiled into JavaScript to actually run it. This involves stripping out all the type information. As a result, all those nice type annotations you've added to the code have no effect at runtime.&lt;/p&gt;

&lt;p&gt;This isn't normally a problem, except when it comes to user input. You might write a type-safe API endpoint definition that expects two fields to be set, and the code looks flawless. But at runtime there's nothing stopping a user from sending a request with only one of those fields set, or using the wrong field name, or sending the wrong type (a string instead of a number, for example).&lt;/p&gt;

&lt;p&gt;This has led to a bunch of libraries that allow you to define the API schema in a way that can be used at runtime to validate the request.&lt;br&gt;
Perhaps the most popular of these is Zod, but there are many others. This is great, but the syntax is much more verbose and noisy compared to the clean, concise TypeScript type syntax.&lt;/p&gt;

&lt;p&gt;Encore.ts improves on this by using the natural TypeScript types directly. When Encore.ts compiles your application it parses the TypeScript types and generates a JSON schema from them, which it then uses to automatically validate incoming requests. As a result, you get the best of both worlds: the clean, concise TypeScript syntax, &lt;strong&gt;and&lt;/strong&gt; runtime schema validation.&lt;/p&gt;

&lt;p&gt;Encore.ts also makes it easy to define API endpoints that combine data from different sources: some fields from the request body, others from the query parameters, and yet others from the HTTP headers. It looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;Request&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Maximum number of items to return. Parsed from the query parameters.&lt;/span&gt;
  &lt;span class="nl"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;Query&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// Custom header that must be set. Parsed from the HTTP headers.&lt;/span&gt;
  &lt;span class="nl"&gt;myHeader&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Header&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;X-My-Header&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// Regular field. Parsed from the request body.&lt;/span&gt;
  &lt;span class="nl"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;sprocket&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;widget&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;myEndpoint&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Response&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;expose&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/my/endpoint&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Implementation...&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Just take a look at these two examples, comparing Zod to Encore.ts:&lt;/p&gt;

&lt;h3&gt;
  
  
  Zod
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;headersSchema&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;object&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;x-foo&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;queryStringSchema&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;object&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;optional&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="na"&gt;excitement&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;number&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;optional&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;bodySchema&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;object&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;someKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;optional&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="na"&gt;someOtherKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;number&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;optional&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="na"&gt;requiredKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;number&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt;
  &lt;span class="na"&gt;nullableKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;number&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;nullable&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;optional&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="na"&gt;multipleTypesKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;union&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;number&lt;/span&gt;&lt;span class="p"&gt;()]).&lt;/span&gt;&lt;span class="nf"&gt;optional&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="na"&gt;enumKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;enum&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Alice&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Bob&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]).&lt;/span&gt;&lt;span class="nf"&gt;optional&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Encore.ts
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;Schema&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Header&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;x-foo&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;name&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;Query&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;excitement&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;Query&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;someKey&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;someOtherKey&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;requiredKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;
  &lt;span class="nl"&gt;nullableKey&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;multipleTypesKey&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;enumKey&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Alice&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Bob&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I know which one I'd rather write. And read. And maintain.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it today
&lt;/h2&gt;

&lt;p&gt;Hopefully this gives you a good idea of what Encore.ts is all about, and that it's more than "just another backend framework". It's &lt;a href="https://github.com/encoredev/encore" rel="noopener noreferrer"&gt;Open Source&lt;/a&gt;, so give it a try and let us know what you think. You can &lt;a href="https://encore.dev/discord" rel="noopener noreferrer"&gt;find us on Discord&lt;/a&gt; if you have any questions or feedback.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Fastify v5 vs v4 — vs Encore.ts</title>
      <dc:creator>André Eriksson</dc:creator>
      <pubDate>Fri, 20 Sep 2024 13:54:25 +0000</pubDate>
      <link>https://dev.to/encore/fastify-v5-breaking-changes-should-you-upgrade-2e6d</link>
      <guid>https://dev.to/encore/fastify-v5-breaking-changes-should-you-upgrade-2e6d</guid>
      <description>&lt;p&gt;Fastify, one of the most popular web frameworks for Node, just announced a major new release, version 5.0.&lt;/p&gt;

&lt;p&gt;We were initially very excited to see if any cool new features or improvements were added, in case there was something we could take inspiration from in &lt;a href="https://encore.dev" rel="noopener noreferrer"&gt;Encore.ts&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Unfortunately, after taking it for a spin, we felt it was quite underwhelming: the new release is extremely light on new features, but extremely heavy on breaking changes.&lt;/p&gt;

&lt;p&gt;Let's take a look at what's new, and whether it's worth upgrading.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fastify v5 comes with lots of breaking changes
&lt;/h2&gt;

&lt;p&gt;Don't believe me? Check out the &lt;a href="https://fastify.dev/docs/latest/Guides/Migration-Guide-V5/" rel="noopener noreferrer"&gt;migration guide&lt;/a&gt;. It clocks in at almost 2000 words! It lists no less than &lt;strong&gt;20&lt;/strong&gt; breaking changes. That's a lot of breakage to consider.&lt;/p&gt;

&lt;p&gt;As far as we can tell, most of the breaking changes amount to "cleaning things up" in the code base.&lt;/p&gt;

&lt;p&gt;It removes a lot of convenient shorthands for defining JSON Schemas, making schema validation more verbose and less readable.&lt;/p&gt;

&lt;p&gt;It also removes a lot of deprecated functionality. Sometimes this is a good thing, if the functionality carries a significant maintenance burden. But in this case, a lot of the removed functionality is essentially a one-liner.&lt;/p&gt;

&lt;p&gt;For example, &lt;code&gt;reply.getResponseTime()&lt;/code&gt; is removed in favor of &lt;code&gt;reply.elapsedTime&lt;/code&gt;. Maybe &lt;code&gt;elapsedTime&lt;/code&gt; is a better name, but the cost of maintaining &lt;code&gt;getResponseTime&lt;/code&gt;, with its one-line implementation, is zero.&lt;/p&gt;

&lt;p&gt;Still, it's easy enough to grep a codebase for &lt;code&gt;getResponseTime&lt;/code&gt; and fix it. Other breaking changes are more difficult.&lt;/p&gt;

&lt;p&gt;Consider for example &lt;code&gt;fastify.hasRoute()&lt;/code&gt; still exists, with exactly the same signature, but the behavior has changed in a subtle way. It used to support passing in a request path to evaluate whether an incoming request matches a route or not.&lt;/p&gt;

&lt;p&gt;In Fastify v5 the method instead only supports matching the exact string given when registering the route. Migrating over to this is much more difficult, as it's not always clear whether you need to change your code or not.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fastify v5 has very few, minor improvements
&lt;/h2&gt;

&lt;p&gt;At this point I hear you say, "surely all the breaking changes were done for good reason!". Unfortunately the documentation is surprisingly sparse when it comes to the benefits of upgrading to v5.&lt;/p&gt;

&lt;p&gt;The only new feature listed is support for the &lt;a href="https://fastify.dev/docs/latest/Guides/Migration-Guide-V5/#diagnostic-channel-support" rel="noopener noreferrer"&gt;Diagnostic Channel API&lt;/a&gt;.&lt;br&gt;
Is that worth upgrading for? For most people, probably not.&lt;/p&gt;

&lt;p&gt;The other improvement is a modest performance boost.&lt;/p&gt;

&lt;p&gt;From our benchmarking, Fastify v5 is about 5-10% faster than v4, depending on configuration settings and whether you use schema validation. That's nothing to sneeze at! But it's not a game changer either.&lt;/p&gt;

&lt;h2&gt;
  
  
  Is upgrading worth the hassle? Probably not.
&lt;/h2&gt;

&lt;p&gt;It's disappointing to see a major new release of a popular framework that is so light on new features, and so heavy on breaking changes.&lt;br&gt;
Particularly when the breaking changes are seemingly done to clean up internals rather than improving the user experience.&lt;/p&gt;

&lt;p&gt;Our belief is that migrating from Fastify v4 to Fastify v5 is probably not worth it for most people.&lt;/p&gt;

&lt;p&gt;If you care about performance or reliability, you should probably check out Encore.ts instead of spending time migrating to Fastify v5.&lt;/p&gt;

&lt;p&gt;It's about twice as fast, and provides a lot of other benefits as well, like TypeScript-native schema validation, automatic tracing (no need to manually instrument your code with the Diagnostic Channel API!), and more.&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%2F7t0rv4n9udtfs492oiwa.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%2F7t0rv4n9udtfs492oiwa.png" alt="Encore.ts vs Fastify performance"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What's more, Encore.ts takes backwards compatibility extremely seriously, and follows the Linux Kernel model when it comes to breaking changes: don't.&lt;br&gt;
And Encore.ts is of course also &lt;a href="https://github.com/encoredev/encore" rel="noopener noreferrer"&gt;open source&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>node</category>
      <category>javascript</category>
      <category>fastify</category>
      <category>backend</category>
    </item>
    <item>
      <title>Designing an algorithmic cloud infrastructure provisioning system</title>
      <dc:creator>André Eriksson</dc:creator>
      <pubDate>Mon, 24 Oct 2022 13:05:10 +0000</pubDate>
      <link>https://dev.to/encore/designing-an-algorithmic-cloud-infrastructure-provisioning-system-kde</link>
      <guid>https://dev.to/encore/designing-an-algorithmic-cloud-infrastructure-provisioning-system-kde</guid>
      <description>&lt;p&gt;Provisioning and configuring cloud infrastructure is tough. To begin with, each cloud provider offers a myriad of different products. Many of them do almost the same thing, only in subtly different ways.&lt;/p&gt;

&lt;p&gt;For example, Google Cloud Platform lists &lt;a href="https://cloud.google.com/hosting-options"&gt;seven official products&lt;/a&gt; for hosting applications, and the corresponding high-level chart compares 13 technical properties. But that's obviously just the tip of the iceberg. Combine this with a ton of database/data storage options and network configurations, and you're likely to suffer a small nervous breakdown.&lt;/p&gt;

&lt;p&gt;The feature sprawl across the major cloud providers is hard to keep up with and the resulting combinatorial explosion can easily lead to choice paralysis. Yet what is perhaps less appreciated is the incredible minutiae you have to deal with just to deploy even the simplest of backend applications. The creative drain this represents is beastly, a mighty dragon indeed.&lt;/p&gt;

&lt;p&gt;Since everyone loves being made aware of an annoying noise they had subconsciously blocked out, it's our great honor and privilege to bring the sheer magnitude of cloud clutter to your attention with this article. You're welcome.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ZE288p49--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jt364h1qlll5w034kxc5.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ZE288p49--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jt364h1qlll5w034kxc5.jpg" alt="cloud clutter dragon" width="880" height="880"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  But first, a cloud native digression
&lt;/h3&gt;

&lt;p&gt;Our day job at &lt;a href="https://encore.dev/"&gt;Encore&lt;/a&gt; is building a &lt;em&gt;backend development engine&lt;/em&gt; designed to help you create &lt;em&gt;cloud native&lt;/em&gt; backend applications. Before you start muttering, &lt;em&gt;"What does cloud native even mean?"&lt;/em&gt;, let's just say it means applications that are designed from the ground up to leverage cloud infrastructure to its fullest. Such applications are, to a large extent, &lt;em&gt;composed&lt;/em&gt; of cloud infrastructure services. We call those services &lt;em&gt;cloud primitives&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Cloud primitives are the core building blocks of your application, such as services, databases, caches, PubSub topics, and scheduled jobs. Fully leveraging such primitives makes for incredibly scalable and reliable systems.&lt;/p&gt;

&lt;p&gt;Unfortunately, going all in on cloud primitives has been a time-tested way to get absolutely nothing useful accomplished, at least any time soon. This is partly because it increases the complexity of your application, and partly because the developer experience is truly awful.&lt;/p&gt;

&lt;h2&gt;
  
  
  Models to train dragons
&lt;/h2&gt;

&lt;p&gt;Freeing you from cloud complexity and poor developer experience is what we are working on at Encore. Through static analysis of your code, combined with service insights, we are able to infer your infrastructure requirements and automatically provision exactly what you need. You don't really have to do anything beyond writing your application business logic. Just sit back, relax, and watch the progress bar.&lt;/p&gt;

&lt;p&gt;However, sometimes you have specific requirements that Encore is unable to infer automatically. You might for example want to use an existing database or SQL server. Or you may want to deploy your stuff to your own pre-existing Kubernetes cluster.&lt;/p&gt;

&lt;p&gt;To support these use cases, and many more things, we are currently revising our infrastructure planning algorithm to allow for user-defined constraints, and many of the examples of cloud infrastructure insanity we're about to show you stemmed from this work.&lt;/p&gt;

&lt;p&gt;So how does it work, you ask? The infrastructure computation is evaluated in three layers, starting with a very abstract representation of the infrastructure requirements and gradually adding more and more details.&lt;/p&gt;

&lt;p&gt;We start with the Needs model, followed by the Entity model, and finally we reach the Resource model, which is the point where subjects spontaneously &lt;del&gt;gouge their eyes out&lt;/del&gt; laugh uncontrollably as a coping mechanism.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Need Model
&lt;/h3&gt;

&lt;p&gt;Pushing your code to Encore kicks off our CI/CD pipeline. Aside from compiling and testing your app, the build step also runs the static analysis to extract those infrastructure needs.&lt;/p&gt;

&lt;p&gt;From a high-level perspective, this takes the form of a graph containing all your backend primitives and the links between them. You can see a simple example on the right.&lt;/p&gt;


&lt;center&gt;
&lt;br&gt;
    &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--GDyf08O4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://encore.dev/assets/blog/1_needmodel.png" alt="The need model" title="The need model" width="880" height="543"&gt;&lt;br&gt;
&lt;/center&gt;

&lt;p&gt;The planning algorithms then calculate the difference between the currently deployed version of the application and the recently pushed changes. We call the output of this comparison the Need Model. The Need Model is flat, cloud agnostic, and expresses the environment-independent core needs of your application.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--dv2qf0P0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://encore.dev/assets/blog/2_needmodel.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--dv2qf0P0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://encore.dev/assets/blog/2_needmodel.png" alt="Existing need model" title="Existing need model" width="850" height="667"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--V8SbBUMO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://encore.dev/assets/blog/3_needmodel.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--V8SbBUMO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://encore.dev/assets/blog/3_needmodel.png" alt="New Need Model (new needs highlighted in green)" title="New Need Model (new needs highlighted in green)" width="880" height="480"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  The Entity Model
&lt;/h3&gt;

&lt;p&gt;Any new needs identified are added to a high-level model we call the &lt;em&gt;Entity Model&lt;/em&gt;. The Entity model is a hierarchical graph of your application. It contains abstract representations of cloud-specific products and their relations. For each cloud, we've modeled supported cloud products as "providers" of entities that are capable of fulfilling your (every) need.&lt;/p&gt;


&lt;center&gt;
&lt;br&gt;
    &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--UgHr2Z4B--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://encore.dev/assets/blog/4_entitymodel.png" alt="The entity model" title="The entity model" width="880" height="515"&gt;&lt;br&gt;
&lt;/center&gt;

&lt;p&gt;For example, a &lt;a href="https://cloud.google.com/run"&gt;Cloud Run&lt;/a&gt; Container is a provider for Services, whereas a &lt;a href="https://cloud.google.com/sql"&gt;Cloud SQL&lt;/a&gt; Server is a provider for databases. The providers are wrapped in parent containers that encapsulate the capabilities of their children.&lt;/p&gt;

&lt;p&gt;Each top-level node of the Entity Model is called a &lt;em&gt;Root Entity&lt;/em&gt;. A Root Entity can for example be a GCP organization, an AWS account, or your existing external database which fulfills the need for a specific database.&lt;/p&gt;

&lt;p&gt;Each provider level can use the Need Model, metrics, and knowledge of the current infrastructure topology to make informed proposals of which provider/product is best suited for your needs. If you have special requirements or &lt;del&gt;crazy ideas&lt;/del&gt; preferences, you can add constraints by specifying your own infrastructure preferences.&lt;/p&gt;

&lt;p&gt;You can for example instruct the planner to deploy a specific service to a Kubernetes cluster or provide a connection string to satisfy your particular database need.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Resource Model
&lt;/h3&gt;

&lt;p&gt;The Entity Model is then further refined into what we call the Resource Model. The Resource Model is pretty much a mapping of the high-level entities to lower-level cloud provider resources. This model is what is used by our provisioning system to create and modify the cloud resources which is necessary to host your app's building blocks.&lt;/p&gt;


&lt;center&gt;
&lt;br&gt;
    &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--zmLlb2aj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://encore.dev/assets/blog/5_resourcemodel.png" alt="The resource model" title="The resource model" width="880" height="474"&gt;&lt;br&gt;
&lt;/center&gt;

&lt;p&gt;The Resource Model is computed by mapping each entity in the Entity Model to a set of resources and/or updates to shared resources. This step is a bit more complex because the resources are sometimes indirectly coupled and can in the worst case affect each other. For example, a Cloud Run Container can for example only use one &lt;a href="https://cloud.google.com/vpc/docs/configure-serverless-vpc-access"&gt;Serverless VPC Access connector&lt;/a&gt;, which impacts how Encore configures the connection to database servers and caches.&lt;/p&gt;

&lt;p&gt;The general idea is similar to the Entity Model though: generate a list of resources that satisfy the needs of a bunch of higher-level entities.&lt;/p&gt;

&lt;h3&gt;
  
  
  This is fine
&lt;/h3&gt;

&lt;p&gt;In the example depicted above, we use a boring app that had a very neat mapping between the Encore needs and the resulting cloud resources. Alas, this is unfortunately not always the case. Relatively small tweaks to the Entity Model quickly balloon into extremely complex Resource Models.&lt;/p&gt;

&lt;p&gt;To illustrate this, let's move one of our Encore Services to run on a separate Cloud Run Container. Given this new constraint, the infrastructure planner happily computes a proposal for a new Entity and Resource Model which satisfies the Need Model.&lt;/p&gt;


&lt;center&gt;
&lt;br&gt;
    &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--lWiCpInK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://encore.dev/assets/blog/6_thisisfine.png" alt="A slightly complicated entity model" title="A slightly complicated entity model" width="880" height="398"&gt;&lt;br&gt;
&lt;/center&gt;


&lt;center&gt;
&lt;br&gt;
    &lt;a href="https://encore.dev/assets/blog/7_travelsize.png"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--kJkRIBqE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://encore.dev/assets/blog/7_travelsize.png" alt="A travel sized resource model" title="A travel sized resource model" width="880" height="404"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/center&gt;

&lt;p&gt;Well, that escalated quickly, really that got out of hand fast! Where did all the colorful boxes come from? Well, since the services now run on separate Cloud Run Containers, we need to configure a bunch of additional IAM bindings (plus associated roles and accounts).&lt;/p&gt;

&lt;p&gt;Now let's say we for some obscure reason want to run one of our databases on a separate Cloud SQL Server. To make things even more interesting we may as well shove it into a separate GCP Project and ditch the Cloud SQL Proxy. After crunching your demands, the planner generates yet another infrastructure proposal, and this time it's starting to look rather unwieldy.&lt;/p&gt;

&lt;p&gt;We're moving the database to a separate GCP Project, and since we don't use Cloud SQL Proxy, we also need to configure the Cloud Run Container to use a Serverless VPC Access connector. We also configure a Shared VPC to virtually host both databases in the same VPC. On top of this, we need to dish out some more IAM bindings, configure Global Address, etc.&lt;/p&gt;


&lt;center&gt;
&lt;br&gt;
    &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--G2pHSOS1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://encore.dev/assets/blog/8_obscure.png" alt="An entity model with an obscure constraint" title="An entity model with an obscure constraint" width="880" height="462"&gt;&lt;br&gt;
&lt;/center&gt;


&lt;center&gt;
&lt;br&gt;
    &lt;a href="https://encore.dev/assets/blog/9_inlaws.png"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--gfjZXYh1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://encore.dev/assets/blog/9_inlaws.png" alt="A Resource Model the in-laws can be proud of" title="A Resource Model the in-laws can be proud of" width="880" height="544"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/center&gt;

&lt;p&gt;This is still an extremely simple app (albeit some would argue it has silly constraints), but the infrastructure configuration has already started to balloon out of proportion.&lt;/p&gt;

&lt;p&gt;Fortunately for those of you using Encore, you don't really need to care. Encore does all the heavy lifting for you and will soon happily cater to all (or at least most) of your obscure, exotic, and slightly lunatic needs. Unfortunately for those of you working on Encore, this is your life now.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's next
&lt;/h2&gt;

&lt;p&gt;We're currently in the midst of fine-tuning the next generation of our planning algorithm and will eventually migrate our existing, more naive models to the shiny, handsome, and all in all superior replacement. This will yield a plethora of exciting new opportunities!&lt;/p&gt;

&lt;p&gt;To begin with, we're working on extending our visual architecture tool, &lt;a href="https://encore.dev/docs/develop/encore-flow"&gt;Encore Flow&lt;/a&gt;, to also give you a fabulous overview of your provisioned cloud infrastructure. We'll also be able to better visualize changes to your infrastructure, and eventually you'll be able to effortlessly plan and migrate services, databases, or whole apps between whatever products you fancy.&lt;/p&gt;

&lt;p&gt;And if you don't want to bother, &lt;a href="https://encore.dev"&gt;Encore&lt;/a&gt; will automatically provision what's best for you, taking both cost and performance into account.&lt;/p&gt;

&lt;p&gt;We'll tame the complexity dragon and disperse the cloud clutter, creating a brighter and sunnier future for us all.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If you're curious, why not &lt;a href="https://encore.dev"&gt;try Encore today&lt;/a&gt;, using the free built-in developer cloud?&lt;/strong&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Advanced Go Concurrency</title>
      <dc:creator>André Eriksson</dc:creator>
      <pubDate>Thu, 02 Jun 2022 15:33:34 +0000</pubDate>
      <link>https://dev.to/encore/advanced-go-concurrency-478n</link>
      <guid>https://dev.to/encore/advanced-go-concurrency-478n</guid>
      <description>&lt;p&gt;&lt;strong&gt;Learn how to use Go's singleflight and errgroup packages, and other important design patterns for concurrency, with real-world examples.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you've used Go for a while you're probably aware of some of the basic Go concurrency primitives:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;go&lt;/code&gt; keyword for spawning goroutines&lt;/li&gt;
&lt;li&gt;Channels, for communicating between goroutines&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;context&lt;/code&gt; package for propagating cancellation&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;sync&lt;/code&gt; and &lt;code&gt;sync/atomic&lt;/code&gt; packages for lower-level primitives such as mutexes and atomic memory access&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These language features and packages combine to provide a very rich set of tools for building concurrent applications.&lt;br&gt;
What you might not have discovered yet is a set of higher-level concurrency primitives available in the "extended standard library" available at &lt;a href="https://pkg.go.dev/golang.org/x/sync"&gt;golang.org/x/sync&lt;/a&gt;.&lt;br&gt;
We'll be taking a look at these in this article.&lt;/p&gt;
&lt;h2&gt;
  
  
  Package singleflight
&lt;/h2&gt;

&lt;p&gt;As the &lt;a href="https://pkg.go.dev/golang.org/x/sync/singleflight?tab=doc"&gt;package documentation&lt;/a&gt; states, this package provides a duplicate function call suppression mechanism.&lt;/p&gt;

&lt;p&gt;This package is extremely useful for cases where you are doing something computationally expensive (or just slow, like network access) in response to user activity.&lt;br&gt;
For example, let's say you have a database with weather information per city and you want to expose this as an API.&lt;br&gt;
In some cases you might have multiple users ask for the weather for the same city at the same time.&lt;/p&gt;

&lt;p&gt;When that happens, wouldn't it be great if you could just query the database, and then share the result to all the waiting requests?&lt;br&gt;
That's exactly what the &lt;code&gt;singleflight&lt;/code&gt; package does!&lt;/p&gt;

&lt;p&gt;To use it, create a &lt;code&gt;singleflight.Group&lt;/code&gt; somewhere. It needs to be shared across all the requests to work correctly.&lt;br&gt;
Then wrap the slow or expensive operation in a call to &lt;code&gt;group.Do(key, fn)&lt;/code&gt;. Multiple concurrent requests for the same &lt;code&gt;key&lt;/code&gt;&lt;br&gt;
will only call &lt;code&gt;fn&lt;/code&gt; once, and the result will be returned to all callers once &lt;code&gt;fn&lt;/code&gt; returns.&lt;/p&gt;

&lt;p&gt;Here's how it looks in practice:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;weather&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;Info&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;TempC&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;TempF&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="c"&gt;// temperature in Celsius and Farenheit&lt;/span&gt;
    &lt;span class="n"&gt;Conditions&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="c"&gt;// "sunny", "snowing", etc&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;group&lt;/span&gt; &lt;span class="n"&gt;singleflight&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Group&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;City&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;city&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Info&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;group&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Do&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;city&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;interface&lt;/span&gt;&lt;span class="p"&gt;{},&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;fetchWeatherFromDB&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;city&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c"&gt;// slow operation&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Errorf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"weather.City %s: %w"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;city&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Info&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that the closure we pass to &lt;code&gt;group.Do&lt;/code&gt; must return &lt;code&gt;(interface{}, error)&lt;/code&gt; to work with the Go type system.&lt;br&gt;
The third return value from &lt;code&gt;group.Do&lt;/code&gt;, which is ignored in the example above, indicates whether the result was&lt;br&gt;
shared between multiple callers or not.&lt;/p&gt;
&lt;h2&gt;
  
  
  Package errgroup
&lt;/h2&gt;

&lt;p&gt;Another invaluable package is the &lt;a href="https://pkg.go.dev/golang.org/x/sync/errgroup?tab=doc"&gt;errgroup package&lt;/a&gt;.&lt;br&gt;
It is best described as a &lt;code&gt;sync.WaitGroup&lt;/code&gt; but where the tasks return errors that are propagated back to the waiter.&lt;/p&gt;

&lt;p&gt;This package is useful when you have multiple operations that you want to wait for, but you also want to determine&lt;br&gt;
if they all completed successfully.&lt;br&gt;
For example, to build on the weather example from above, let's say you want to lookup the weather for multiple cities&lt;br&gt;
at once, and fail if any of the lookups fails.&lt;/p&gt;

&lt;p&gt;Start by defining an &lt;code&gt;errgroup.Group&lt;/code&gt;, and use the &lt;code&gt;group.Go(fn func() error)&lt;/code&gt; method for each city.&lt;br&gt;
This method spawns a goroutine to run the task. When you've spawned all the tasks you want, use&lt;br&gt;
&lt;code&gt;group.Wait()&lt;/code&gt; to wait for them to complete. Note that this method returns an &lt;code&gt;error&lt;/code&gt;, unlike &lt;code&gt;sync.WaitGroup&lt;/code&gt;'s equivalent.&lt;br&gt;
The error is &lt;code&gt;nil&lt;/code&gt; if and only if all the tasks returned a &lt;code&gt;nil&lt;/code&gt; error.&lt;/p&gt;

&lt;p&gt;In practice it looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;Cities&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cities&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;([]&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Info&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;g&lt;/span&gt; &lt;span class="n"&gt;errgroup&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Group&lt;/span&gt;
    &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;mu&lt;/span&gt; &lt;span class="n"&gt;sync&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Mutex&lt;/span&gt;
    &lt;span class="n"&gt;res&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nb"&gt;make&lt;/span&gt;&lt;span class="p"&gt;([]&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Info&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cities&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="c"&gt;// res[i] corresponds to cities[i]&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;city&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="k"&gt;range&lt;/span&gt; &lt;span class="n"&gt;cities&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;city&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;city&lt;/span&gt; &lt;span class="c"&gt;// create locals for closure below&lt;/span&gt;
        &lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Go&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;City&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;city&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;mu&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Lock&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;info&lt;/span&gt;
            &lt;span class="n"&gt;mu&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Unlock&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;
        &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Wait&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here we are allocating an slice of results so that each goroutine can write to its own index.&lt;br&gt;
While the above code is safe even without the &lt;code&gt;mu&lt;/code&gt; mutex, since each goroutine is writing to its own entry in the slice, we use one anyway in case the code is changed over time.&lt;/p&gt;
&lt;h2&gt;
  
  
  Bounded concurrency
&lt;/h2&gt;

&lt;p&gt;The code above will lookup weather information for all the given cities concurrently.&lt;br&gt;
That's fine when the number of cities is small, but can cause performance issues if the number of cities is massive.&lt;br&gt;
In those cases it's useful to introduce &lt;em&gt;bounded concurrency&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Go makes it really easy to create bounded concurrency with the use of &lt;a href="https://www.guru99.com/semaphore-in-operating-system.html"&gt;semaphores&lt;/a&gt;. A semaphore is a concurrency primitive that you might have come across if you studied Computer Science, but if not, don't worry. You can use semaphores for several purposes, but we're just going to use them to keep track of how many tasks are running, and to block until there is room for another task to start.&lt;/p&gt;

&lt;p&gt;In Go we can accomplish this through a clever use of channels! If we want to allow up to 10 tasks to run at once,&lt;br&gt;
we create a channel with space for 10 items: &lt;code&gt;semaphore := make(chan struct{}, 10)&lt;/code&gt;. You can picture this as a pipe that can fit 10 balls.&lt;/p&gt;

&lt;p&gt;To start a new task, blocking if too many tasks are already running, we simply attempt to send a value on the channel: &lt;code&gt;semaphore &amp;lt;- struct{}{}&lt;/code&gt;. This is analogous to trying to push another ball into the pipe. If the pipe is full, it waits until there is room.&lt;/p&gt;

&lt;p&gt;When a task completes, mark it as such by taking a value out of the channel: &lt;code&gt;&amp;lt;-semaphore&lt;/code&gt;. This is analogous to pulling a ball out at the other end of the pipe, which leaves room for another ball to be pushed in (another task started).&lt;/p&gt;

&lt;p&gt;And that's it! Our modified &lt;code&gt;Cities&lt;/code&gt; looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;Cities&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cities&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;([]&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Info&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;g&lt;/span&gt; &lt;span class="n"&gt;errgroup&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Group&lt;/span&gt;
    &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;mu&lt;/span&gt; &lt;span class="n"&gt;sync&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Mutex&lt;/span&gt;
    &lt;span class="n"&gt;res&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nb"&gt;make&lt;/span&gt;&lt;span class="p"&gt;([]&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Info&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cities&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="c"&gt;// res[i] corresponds to cities[i]&lt;/span&gt;
    &lt;span class="n"&gt;sem&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nb"&gt;make&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;chan&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt;&lt;span class="p"&gt;{},&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;city&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="k"&gt;range&lt;/span&gt; &lt;span class="n"&gt;cities&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;city&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;city&lt;/span&gt; &lt;span class="c"&gt;// create locals for closure below&lt;/span&gt;
        &lt;span class="n"&gt;sem&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt;&lt;span class="p"&gt;{}{}&lt;/span&gt;
        &lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Go&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;City&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;city&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;mu&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Lock&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;info&lt;/span&gt;
            &lt;span class="n"&gt;mu&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Unlock&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="n"&gt;sem&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;
        &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Wait&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Weighted bounded concurrency
&lt;/h3&gt;

&lt;p&gt;And finally, sometimes you want bounded concurrency, but not all tasks are equally expensive.&lt;br&gt;
In that case the amount of resources we'll consume will vary drastically depending on the distribution&lt;br&gt;
of cheap and expensive tasks and how they happen to start.&lt;/p&gt;

&lt;p&gt;A better solution for this use case is to use &lt;em&gt;weighted bounded concurrency&lt;/em&gt;.&lt;br&gt;
How this works is simple: instead of reasoning about the number of tasks we want to run concurrently,&lt;br&gt;
we come up with a "cost" for every task and acquire and release that cost from a semaphore.&lt;/p&gt;

&lt;p&gt;We can't model this with channels any longer since we need the whole cost acquired and released at once.&lt;br&gt;
Fortunately the "extended standard library" comes to our rescue once more!&lt;br&gt;
The &lt;a href="https://pkg.go.dev/golang.org/x/sync@v0.0.0-20190911185100-cd5d95a43a6e/semaphore?tab=doc"&gt;golang.org/x/sync/sempahore&lt;/a&gt;&lt;br&gt;
package provides a weighted semaphore implementation exactly for this purpose.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;sem &amp;lt;- struct{}{}&lt;/code&gt; operation is called "Acquire" and the &lt;code&gt;&amp;lt;-sem&lt;/code&gt; operation is called "Release".&lt;br&gt;
You will note that the &lt;code&gt;semaphore.Acquire&lt;/code&gt; method returns an error; that is because it can be used&lt;br&gt;
with the &lt;code&gt;context&lt;/code&gt; package to abort the operation early. For the purpose of this example we will ignore it.&lt;/p&gt;

&lt;p&gt;The weather lookup example is realistically too simple to warrant a weighted semaphore,&lt;br&gt;
but for the sake of simplicity let's pretend the cost varies with the length of the city name.&lt;br&gt;
Then we arrive at the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;Cities&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cities&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;([]&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Info&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TODO&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c"&gt;// replace with a real context&lt;/span&gt;
    &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;g&lt;/span&gt; &lt;span class="n"&gt;errgroup&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Group&lt;/span&gt;
    &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;mu&lt;/span&gt; &lt;span class="n"&gt;sync&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Mutex&lt;/span&gt;
    &lt;span class="n"&gt;res&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nb"&gt;make&lt;/span&gt;&lt;span class="p"&gt;([]&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Info&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cities&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="c"&gt;// res[i] corresponds to cities[i]&lt;/span&gt;
    &lt;span class="n"&gt;sem&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;semaphore&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewWeighted&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c"&gt;// 100 chars processed concurrently&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;city&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="k"&gt;range&lt;/span&gt; &lt;span class="n"&gt;cities&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;city&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;city&lt;/span&gt; &lt;span class="c"&gt;// create locals for closure below&lt;/span&gt;
        &lt;span class="n"&gt;cost&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="kt"&gt;int64&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;city&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;sem&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Acquire&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cost&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;break&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Go&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;City&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;city&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;mu&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Lock&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;info&lt;/span&gt;
            &lt;span class="n"&gt;mu&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Unlock&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="n"&gt;sem&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Release&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cost&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;
        &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Wait&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;The above examples show how easy it is to add concurrency to a Go program, and then fine-tune it based on your needs.&lt;/p&gt;

&lt;p&gt;Feedback, corrections and suggestions on how this article can be improved are very welcome! Please reach out to me on &lt;a href="https://twitter.com/_eandre"&gt;Twitter&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Catch you in the cloud,&lt;br&gt;
&lt;em&gt;André&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This article was originally published on the &lt;a href="https://encore.dev/blog"&gt;Encore Blog&lt;/a&gt; on 18-02-2020.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>go</category>
      <category>backend</category>
      <category>encore</category>
    </item>
    <item>
      <title>Putting the fun back into software development with Encore</title>
      <dc:creator>André Eriksson</dc:creator>
      <pubDate>Mon, 30 May 2022 13:33:43 +0000</pubDate>
      <link>https://dev.to/encore/putting-the-fun-back-into-software-development-with-encore-396e</link>
      <guid>https://dev.to/encore/putting-the-fun-back-into-software-development-with-encore-396e</guid>
      <description>&lt;p&gt;&lt;strong&gt;How my frustrations with building for the cloud led me to create the backend development engine I believe will transform developers' lives, and announcing our company has raised $3 million in seed funding.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I've always loved programming for how creative you can be. There's a deep joy and satisfaction in playing around with ideas and building things that delight people. That's what got me into it as a young boy. I taught myself to code at a very early age, building interactive websites and simple games. When I started playing World of Warcraft, I began building user interface modifications to improve the game for myself and my fellow players. By the time I was thirteen the mods I'd built were used by millions of people. I ended up collaborating with Blizzard – the studio behind the game – and many of the innovations I developed even made their way into the official game, which was a truly fulfilling experience.&lt;/p&gt;

&lt;p&gt;A few years later I got my first real job as a developer and joined Spotify. This meant I had to learn anew how to build backend applications. I was used to simple setups with one or two servers, but now we were building massively scalable systems for The Cloud™. The complexity of cloud infrastructure started to dominate my work. Instead of being all about building a great product we were spending all our time provisioning, orchestrating and managing cloud services. It was all very mundane and repetitive, and a hundred times slower and more complicated compared to the days of the single server. Nothing like the creative process I used to love growing up.&lt;/p&gt;

&lt;p&gt;At one point the complexities of cloud infrastructure resulted in what felt like several months of hell. The payments systems I was working on at Spotify started having a nasty intermittent performance problem, and were constantly at the verge of falling over. My team kept being woken up by alerts almost every night, and we were doing everything in our power to keep everything up and running so that the company could accept payments. The experience was so stressful I ended up hospitalised with gastritis. I felt strongly that it would never have happened were it not for the complex nature of large distributed systems.&lt;/p&gt;

&lt;p&gt;I realised I had to do something for all the developers out there like me who find building for the cloud incredibly frustrating and uncreative. There are loads of tools for improving small parts of the process. But they only have a small impact on the job overall. I wanted to find a way to massively reduce the time developers spend managing the complexities of the cloud; something that would free us to get on with the fun and creative part of actually building things.&lt;/p&gt;

&lt;p&gt;I realised the reason we spend so much time configuring cloud services is because the tools we rely on have no idea what we're trying to do. So it's up to the developer to do almost all of the work. I had the idea that in order to make a real impact, I needed to build a tool that does understand that. That's the key insight behind &lt;a href="https://encore.dev"&gt;Encore&lt;/a&gt;. It's a backend development engine for building scalable cloud-based software, that has the same mental model as the developer for how things work and how they're connected.&lt;/p&gt;

&lt;p&gt;In 2017 I started by sketching the idea out to see if there was something in it. Once I realized it could work it took over my life. It felt so exciting and important. For years I'd get home from work at 6pm, start hacking away and suddenly it would be 4am.&lt;/p&gt;

&lt;p&gt;By early 2020 &lt;a href="https://encore.dev"&gt;Encore&lt;/a&gt; was ready for early adopters so I left Spotify to work on it full-time. We open-sourced it last year and the reception blew us away. We went from having a handful of users to thousands of developers trying it out.&lt;/p&gt;

&lt;p&gt;Today I'm excited to announce we're releasing &lt;a href="https://encore.dev"&gt;Encore&lt;/a&gt; v1.0. It's my belief that &lt;a href="https://encore.dev"&gt;Encore&lt;/a&gt; will transform developers' lives. Our work will become far more joyful and creative. Rather than it being 80% about configuring tools and services that have been reconfigured thousands of times before, we'll spend our days building new products that have a real impact. And that could have huge benefits for society. Imagine the exciting innovations if the world's 25+ million developers are freed up to be five times more productive!&lt;/p&gt;

&lt;p&gt;I also hope that &lt;a href="https://encore.dev"&gt;Encore&lt;/a&gt; will open things up to a more diverse range of folks than the usual bearded men. You won't need to have a deep understanding of the nuances of cloud services to develop world-changing software. Instead it will be something people can do who are expert and passionate about the real-world issues they're actually trying to solve.&lt;/p&gt;

&lt;p&gt;I'm happy to share that we've raised $3 million in seed funding, led by &lt;a href="https://crane.vc/"&gt;Crane Venture Partners&lt;/a&gt;, to help us accomplish these lofty goals. With this funding round we're able to both grow our core team, and invest much more in supporting our incredible developer community. We're now able to accelerate our efforts to provide a radically improved experience for backend developers everywhere.&lt;/p&gt;

&lt;p&gt;Catch you in the cloud,&lt;br&gt;
&lt;em&gt;André&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This was originally published on the Encore blog 19/04/22.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>go</category>
      <category>distributedsystems</category>
      <category>api</category>
      <category>encore</category>
    </item>
    <item>
      <title>Distributed tracing made simple</title>
      <dc:creator>André Eriksson</dc:creator>
      <pubDate>Wed, 05 May 2021 09:23:03 +0000</pubDate>
      <link>https://dev.to/encore/distributed-tracing-made-simple-1k1f</link>
      <guid>https://dev.to/encore/distributed-tracing-made-simple-1k1f</guid>
      <description>&lt;p&gt;&lt;strong&gt;Everything's on fire. That much is clear. But what's the root cause? Nobody's got a clue.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When we build distributed systems we're used to fumbling in the dark, desperately searching for the problem. Sometimes it's due to not having enough data. But paradoxically it's often due to having too much data.&lt;/p&gt;

&lt;p&gt;The real challenge lies in &lt;em&gt;making sense of it all&lt;/em&gt;. Unfortunately, general-purpose observability tools often have no idea how your application works. The end result is that distributed tracing, an incredibly powerful idea, largely under-delivers and is mostly useful for tracking down performance issues. That's good, but it could be &lt;em&gt;so much better&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  How tracing (normally) works
&lt;/h2&gt;

&lt;p&gt;Tracing builds on a few pretty simple concepts.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Spans&lt;/strong&gt; are the operations we track, with a start and an end, and references between them. They're things like a running request, or a database query. Spans can reference each other, for example to communicate that one request made an API call which results in another request. In this way we can build up a tree (or in practice a directed acyclic graph, a "DAG") of all spans.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ba66Cp7w--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://encore.dev/assets/blog/tracing-data-model.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ba66Cp7w--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://encore.dev/assets/blog/tracing-data-model.png" alt="Tracing data model"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Each span contains &lt;strong&gt;tags&lt;/strong&gt;, which are key-value pairs, and &lt;strong&gt;events&lt;/strong&gt; which are timestamped things that happened within the span, such as emitting log messages.&lt;/p&gt;

&lt;p&gt;Not too bad so far, right? The devil's in the details. Because this data model is so generic, it's difficult to make sense of exactly what a span represents. Is it an API call? Is it a database query? They don't know, so in practice they render everything as one long list of spans. Not too helpful.&lt;/p&gt;

&lt;p&gt;The other challenge is in the instrumentation itself. In order to build up this DAG of spans, we need to pass along IDs to tie everything together. This data must be carefully threaded through our whole application. How to do that in practice differs from language to language, but setting it all up is quite a nuisance and if you miss some place you'll silently get lower quality data.&lt;/p&gt;

&lt;p&gt;In practice it ends up being lots of error-prone, boilerplate-y code. And the end result is less useful than we would like. So we figured, since Encore is all about deeply understanding how your application works using static analysis, let's try to build a better distributed tracing solution that leverages that understanding. We wanted to offer much better insights, and do it in a way that works out of the box without having to do a thing. A tall order, but we believe we've succeeded.&lt;/p&gt;

&lt;h2&gt;
  
  
  How tracing works with Encore
&lt;/h2&gt;

&lt;p&gt;The way you write &lt;a href="https://github.com/encoredev/encore"&gt;Encore applications&lt;/a&gt; has been carefully designed to facilitate static analysis. When you do, Encore can easily understand, among other things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The services and APIs you have defined, and their precise request/response schemas&lt;/li&gt;
&lt;li&gt;Precisely when and where you make an API call to another service, output structured log messages, execute database queries, and more&lt;/li&gt;
&lt;li&gt;What infrastructure each service requires to run&lt;/li&gt;
&lt;li&gt;Your database(s) schemas&lt;/li&gt;
&lt;li&gt;And lots more&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Using nothing but static code analysis we build up a detailed graph of your application as a distributed system.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--bwx6Rwin--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://encore.dev/assets/blog/app-graph.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--bwx6Rwin--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://encore.dev/assets/blog/app-graph.png" alt="Distributed Tracing"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So now, when Encore compiles your application, it queries this graph to automatically add instrumentation in the right places. Instead of having to manually propagate trace IDs, Encore comes with a slightly modified Go runtime that automatically propagates trace information for you. And since it understands exactly what every operation &lt;em&gt;is&lt;/em&gt;, and not just a generic "span", Encore can add incredibly rich information that is unique to each type of event. Among other things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Database queries, transactions, commits/rollbacks&lt;/li&gt;
&lt;li&gt;API calls, complete with input/output data&lt;/li&gt;
&lt;li&gt;Outgoing HTTP requests, along with precise timings for things like DNS resolution, TLS handshakes, and more&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We also break things down further. Encore captures goroutine timings, allowing you to see at a glance when an API call runs multiple things in parallel. Including exactly what each goroutine did: database queries, API calls, log messages, and more.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--1MkkxUrd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://encore.dev/assets/blog/tracing-goroutines.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--1MkkxUrd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://encore.dev/assets/blog/tracing-goroutines.png" alt="Tracing goroutines"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Stack traces
&lt;/h2&gt;

&lt;p&gt;Finally, Encore now captures &lt;em&gt;complete stack traces&lt;/em&gt; for everything. But wait, I hear you say, won't that be incredibly slow? Nope.&lt;/p&gt;

&lt;p&gt;Encore only collects the program counter for each stack frame, and then uses delta encoding and varint encoding. Then when it comes to viewing a trace, we map the program counters back to the files, lines, and function names using the program's symbol table stored in the binary, that we conveniently kept around from when we did our original static analysis pass.&lt;/p&gt;

&lt;p&gt;The end result is collecting a stack trace in as little as 300ns, and each stack frame often takes 1-2 bytes to encode. This remarkable result is only possible by the tight integration between Encore and the Go runtime. It's fast enough that we can capture stack traces for pretty much &lt;em&gt;everything&lt;/em&gt;. Database queries, API calls, log messages, and more.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--DNBN6enU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://encore.dev/assets/blog/tracing-stacks.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--DNBN6enU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://encore.dev/assets/blog/tracing-stacks.png" alt="Stack traces"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Making sense of it all
&lt;/h2&gt;

&lt;p&gt;Encore is all about enabling simplicity through understanding your application, and we wanted the tracing experience to reflect that. So instead of doing all this instrumentation by hand, and presenting a trace as a long list of spans, we wanted it to "just work" with no additional effort on your part, and with a UI that reflects the structure of your app.&lt;/p&gt;

&lt;p&gt;Stack traces are great and all, but when you're building a distributed system they're often insufficient to get a complete picture of what's going on. That's what's so beautiful about our approach: the stack traces are themselves embedded in the distributed trace data.&lt;/p&gt;

&lt;p&gt;In the end, the traces are much richer in information and at the same time faster to collect. And by understanding exactly what each span represents, we can visualize traces in a much more understandable way. And the stack traces we capture? We can correlate them across the whole system, so you can understand the exact code path across all your services that led to a particular event.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s---VqxzLfM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://encore.dev/assets/blog/tracing-ui.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s---VqxzLfM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://encore.dev/assets/blog/tracing-ui.png" alt="Stack traces"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Tracing Everywhere
&lt;/h2&gt;

&lt;p&gt;The final piece of the puzzle was the realization that such a powerful experience is useful for many more things than just performance analysis. That's why we made it available &lt;em&gt;everywhere&lt;/em&gt;, and perhaps most critically, for local development.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--gH3nI8O_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://encore.dev/assets/blog/tracing-list.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--gH3nI8O_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://encore.dev/assets/blog/tracing-list.png" alt="Local tracing"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That way, the next time you're building a new feature, and you find a bug, you can immediately open up the trace view and see exactly where the problem is. Same goes for production, your test environment, and for all your Preview Environments that Encore automatically spins up for each Pull Request you open. And that time when everything was on fire and you couldn't figure out why? It sure would be nice to immediately pinpoint where the problem was.&lt;/p&gt;

&lt;p&gt;Why, you ask? Because it's about time we developers got better tools.&lt;/p&gt;

&lt;h2&gt;
  
  
  Want to play around? This stuff's all open source.
&lt;/h2&gt;

&lt;p&gt;You'll go from zero to a running backend in the cloud in less than 5 minutes. With the world's most advanced tracing integration, fully open source.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://encore.dev/docs/intro/speedrun"&gt;Try it now&lt;/a&gt;&lt;/p&gt;

</description>
      <category>go</category>
      <category>tracing</category>
      <category>backend</category>
      <category>api</category>
    </item>
  </channel>
</rss>
