<?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: larskarbo</title>
    <description>The latest articles on DEV Community by larskarbo (@larskarbo).</description>
    <link>https://dev.to/larskarbo</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%2F22266%2F09b31690-17d2-4588-ae3e-9832daad5a3b.jpeg</url>
      <title>DEV Community: larskarbo</title>
      <link>https://dev.to/larskarbo</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/larskarbo"/>
    <language>en</language>
    <item>
      <title>How to work around Vercel's 4kb environment restriction</title>
      <dc:creator>larskarbo</dc:creator>
      <pubDate>Fri, 24 Jun 2022 12:16:03 +0000</pubDate>
      <link>https://dev.to/larskarbo/how-to-work-around-vercels-4kb-environment-restriction-gm4</link>
      <guid>https://dev.to/larskarbo/how-to-work-around-vercels-4kb-environment-restriction-gm4</guid>
      <description>&lt;p&gt;At Layer3 we run our whole platform in a full-stack beautifully orchestrated environment on Vercel. All server-side and client-side code is written in TypeScript and share many of the modules and types.&lt;/p&gt;

&lt;p&gt;Everything was going well, until one day…&lt;/p&gt;

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

&lt;p&gt;Vercel has a 4kb environment variable limit. It is caused by the underlying AWS Lambda infrastructure, but whereas AWS has some solutions for proper secret management, Vercel basically says you need to &lt;a href="https://vercel.com/support/articles/how-do-i-workaround-vercel-s-4-kb-environment-variables-limit"&gt;roll your own&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;At this time, there are so many pros of being on a platform like Vercel, and we save so much time by not having to set up complex cloud infrastructure on AWS.&lt;/p&gt;

&lt;p&gt;So we decided to fix this.&lt;/p&gt;

&lt;p&gt;Our solution focused on two things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Secure management and deployment of secrets in preview and production&lt;/li&gt;
&lt;li&gt;Keep the excellent dev experience of pulling development keys to local environments&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Check out the example repository here: &lt;a href="https://github.com/larskarbo/next-env-encrypt-decrypt/blob/main/README.md"&gt;larskarbo/next-env-encrypt-decrypt&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Meet Doppler — an environment manager
&lt;/h2&gt;

&lt;p&gt;Doppler is a service that specializes on environment variable management. It sounded perfect for our usecase, they even have a Vercel integration!&lt;/p&gt;

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

&lt;p&gt;However, we quickly realized that even though Doppler has a Vercel integration, it doesn’t help the 4kb problem at all. It actually just — kind of &lt;em&gt;contributes&lt;/em&gt; to it... By adding more &lt;code&gt;DOPPLER_&lt;/code&gt; variables.&lt;/p&gt;

&lt;p&gt;However, the Doppler interface and API is amazing, and we figured it we could make a working solution with some hacking.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fetching secrets from Doppler instead of Vercel
&lt;/h2&gt;

&lt;p&gt;Once you add all your environment variables on Doppler instead of Vercel, you can work around the 4kb restriction pretty easily by fetching secrets from Doppler instead of Vercel.&lt;/p&gt;

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

&lt;p&gt;The only environment variable you need on Vercel is a &lt;em&gt;Doppler token&lt;/em&gt;. The easiest way to add tokens for &lt;code&gt;development&lt;/code&gt;, &lt;code&gt;preview&lt;/code&gt; and &lt;code&gt;production&lt;/code&gt; is to have both Vercel CLI and Doppler CLI installed and generate three different keys from the terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;doppler configs tokens create vercel-gitops &lt;span class="nt"&gt;--config&lt;/span&gt; dev &lt;span class="nt"&gt;--plain&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | vercel &lt;span class="nb"&gt;env &lt;/span&gt;add DOPPLER_TOKEN development

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;doppler configs tokens create vercel-gitops &lt;span class="nt"&gt;--config&lt;/span&gt; stg &lt;span class="nt"&gt;--plain&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | vercel &lt;span class="nb"&gt;env &lt;/span&gt;add DOPPLER_TOKEN preview

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;doppler configs tokens create vercel-gitops &lt;span class="nt"&gt;--config&lt;/span&gt; prd &lt;span class="nt"&gt;--plain&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | vercel &lt;span class="nb"&gt;env &lt;/span&gt;add DOPPLER_TOKEN production
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We’ll then make a script &lt;code&gt;fetchSecrets.ts&lt;/code&gt; that fetches these variables at build time and writes them to &lt;code&gt;.env&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;fs&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;fs/promises&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;secrets&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;@larskarbo/gitops-secrets&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="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;main&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;payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;secrets&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;providers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;doppler&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;envFile&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;entries&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nx"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(([&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;value&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="nx"&gt;envFile&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;\n`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nx"&gt;envFile&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="s2"&gt;`DOPPLER_TOKEN=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DOPPLER_TOKEN&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;\n`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;writeFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;.env&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;envFile&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Changes in package.json:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"build"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"npm run fetch-secrets &amp;amp;&amp;amp; nextjs build"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"fetch-secrets"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ts-node fetchSecrets.ts"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Yes, that's all you need.&lt;/p&gt;

&lt;p&gt;In development, you’ll simply run &lt;code&gt;npm run fetch-env&lt;/code&gt;. This flow doesn’t add a lot of moving parts and it feels very similar to the &lt;code&gt;vercel env pull&lt;/code&gt; workflow.&lt;/p&gt;

&lt;h2&gt;
  
  
  Taking it a step further with encrypted secrets
&lt;/h2&gt;

&lt;p&gt;Now that we are rolling our own secrets management, why not take it a step further and improve the security?&lt;/p&gt;

&lt;p&gt;The current environment variable setup &lt;a href="https://diogomonica.com/2017/03/27/why-you-shouldnt-use-env-variables-for-secret-data/"&gt;can be a security risk&lt;/a&gt;. A rogue npm package could &lt;a href="https://youtu.be/jUhiPj6h_L8?t=795"&gt;dump all the freely available&lt;/a&gt; &lt;code&gt;process.env&lt;/code&gt; variables and send it to a remote server. And remember, this could be the dependency of one of your dependencies too. Most npm apps have a boatload of dependencies when you look at the dependency tree, so the surface risk area might be bigger than you think.&lt;/p&gt;

&lt;p&gt;Our goal will be to create a system where:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Secrets are always encrypted, both in transit and at rest.&lt;/li&gt;
&lt;li&gt;Secrets are difficult to unintentionally leak when consumed by the final application.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Many platforms have sophisticated solutions for this, like &lt;a href="https://aws.amazon.com/kms/"&gt;AWS KMS&lt;/a&gt; and &lt;a href="https://docs.docker.com/engine/swarm/secrets/"&gt;Docker Secrets&lt;/a&gt;. The idea is that these tools hold the secret in encrypted form and provides it to the application at runtime. &lt;/p&gt;

&lt;p&gt;We’ll solve this in a simple and custom way, with some unique considerations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We need &lt;code&gt;NEXT_PUBLIC_&lt;/code&gt; variables to be available in the environment.&lt;/li&gt;
&lt;li&gt;We want to be able to override secrets with &lt;code&gt;.env.local&lt;/code&gt; for our local dev environments.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;Building on the Doppler setup, we'll add another environment variable to Vercel, &lt;code&gt;SECRETS_KEY&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gen_key &lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; openssl rand &lt;span class="nt"&gt;-base64&lt;/span&gt; 32 &lt;span class="o"&gt;}&lt;/span&gt;

gen_key | vercel &lt;span class="nb"&gt;env &lt;/span&gt;add SECRETS_KEY development
gen_key | vercel &lt;span class="nb"&gt;env &lt;/span&gt;add SECRETS_KEY preview
gen_key | vercel &lt;span class="nb"&gt;env &lt;/span&gt;add SECRETS_KEY production
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we’ll make some changes to our &lt;code&gt;fetch-secrets.ts&lt;/code&gt; script.&lt;/p&gt;

&lt;p&gt;It needs to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Fetch secrets from doppler.&lt;/li&gt;
&lt;li&gt;Write all &lt;code&gt;NEXT_PUBLIC_&lt;/code&gt; vars to &lt;code&gt;.env&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Write all other secrets to a special file &lt;code&gt;.encrypted-secrets&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Commit this file into git like this, and &lt;em&gt;then&lt;/em&gt; add it to &lt;code&gt;.gitignore&lt;/code&gt;. This allows us to run the app without depending on the generated file.&lt;/p&gt;

&lt;p&gt;Our super-charged &lt;code&gt;fetch-secrets.ts&lt;/code&gt; looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Cryptr&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;cryptr&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;fs&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;fs/promises&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;gitopsSecrets&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;@larskarbo/gitops-secrets&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ENCRYPTED_SECRETS_FILE&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;../src/utils&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="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;main&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;payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;gitopsSecrets&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;providers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;doppler&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;SECRETS_KEY&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;SECRETS_KEY is not set&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cryptr&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;Cryptr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;SECRETS_KEY&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;encryptedText&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;cryptr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;encrypt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;writeFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ENCRYPTED_SECRETS_FILE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;encryptedText&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;envFile&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;entries&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(([&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;startsWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;NEXT_PUBLIC_&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="nx"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(([&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;value&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="nx"&gt;envFile&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;\n`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nx"&gt;envFile&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="s2"&gt;`DOPPLER_TOKEN=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DOPPLER_TOKEN&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;\n`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;envFile&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="s2"&gt;`SECRETS_KEY=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;SECRETS_KEY&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;\n`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;writeFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;.env&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;envFile&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then we need to decrypt the secrets in the runtime code. We'll make a helper function for this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;decryptedSecrets&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&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;key&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="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="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="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;readFileSync&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;fs&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Cryptr&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;cryptr&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;path&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;path&lt;/span&gt;&lt;span class="dl"&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;ENCRYPTED_SECRETS_FILE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;.encrypted-secrets&lt;/span&gt;&lt;span class="dl"&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;getSecret&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// in case you have some overrides in `.env.local`&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NODE_ENV&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;development&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;key&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="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// only decrypt secrets the first time&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;decryptedSecrets&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="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;SECRETS_KEY&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="kc"&gt;undefined&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;encryptedSecrets&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;readFileSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cwd&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nx"&gt;ENCRYPTED_SECRETS_FILE&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;utf8&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;cryptr&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;Cryptr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;SECRETS_KEY&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;decryptedSecrets&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cryptr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;decrypt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;encryptedSecrets&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="nx"&gt;decryptedSecrets&lt;/span&gt;&lt;span class="p"&gt;?.[&lt;/span&gt;&lt;span class="nx"&gt;key&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;Voila! Now you can use the secrets anywhere in your app like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// back-end&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;apiKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;getSecret&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;API_KEY&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;// front-end&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;somePublicKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NEXT_PUBLIC_KEY&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check out the working demo here: (&lt;a href="https://next-env-encrypt-decrypt.vercel.app/"&gt;link&lt;/a&gt;, &lt;a href="https://github.com/larskarbo/next-env-encrypt-decrypt"&gt;github repo&lt;/a&gt;).&lt;/p&gt;

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

&lt;p&gt;Vercel may have a 4kb environment restriction, but with some creative engineering, you might end up with a system that is more developer-friendly and secure than before.&lt;/p&gt;

&lt;p&gt;This approach might be right if you are an early stage startup. When you get bigger, and have stricter requirements to managing confidential stuff, you’ll probably end up with a more complex cloud orchestrated infrastructure.&lt;/p&gt;

&lt;p&gt;At Layer3, we use Vercel and Doppler to move fast. If you enjoyed this post and love the idea of building new types of applications that take advantage of the decentralised web, you should &lt;a href="https://www.notion.so/Open-Roles-c7edc26d9c314721a058b2ffc7b711b9"&gt;join our team&lt;/a&gt;!&lt;/p&gt;

</description>
    </item>
    <item>
      <title>The three Phases of Creating Useful Stuff on the Web</title>
      <dc:creator>larskarbo</dc:creator>
      <pubDate>Sat, 31 Oct 2020 10:45:36 +0000</pubDate>
      <link>https://dev.to/larskarbo/the-three-phases-of-creating-useful-stuff-on-the-web-1hoh</link>
      <guid>https://dev.to/larskarbo/the-three-phases-of-creating-useful-stuff-on-the-web-1hoh</guid>
      <description>&lt;p&gt;This model will make you more successful at creating useful stuff on the web.&lt;/p&gt;

&lt;p&gt;It is simple and easy to understand.&lt;/p&gt;

&lt;h2&gt;
  
  
  The three Phases
&lt;/h2&gt;

&lt;p&gt;I have &lt;em&gt;a lot&lt;/em&gt; of failed projects that never got out of phase one (at least 150), I have some things that I released, but never took it further. And I have a few successful projects.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I have &lt;em&gt;a lot&lt;/em&gt; of failed projects that never got out of phase one&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;By recognizing these phases and thinking rationally about them we will become better at creating &lt;strong&gt;successful projects&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--aSyse6v3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/4q9chqvok3qo1bvcuh8e.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--aSyse6v3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/4q9chqvok3qo1bvcuh8e.png" alt="Three phases"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Phase 1: Building
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--uNUY87vV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/uobm9vjhagk95qeakdcp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--uNUY87vV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/uobm9vjhagk95qeakdcp.png" alt="me building a random idea I got"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is the phase where you start coding the project.&lt;/p&gt;

&lt;p&gt;You have this amazing idea. Maybe you think you'll be able to finish it quickly.&lt;/p&gt;

&lt;p&gt;You make a github repo and things start to take form.&lt;/p&gt;

&lt;p&gt;If you release it, big or small, you go on to &lt;strong&gt;phase two ↓&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Phase 2: Releasing
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--keygATN3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/0mt6h672u2emret2bwi1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--keygATN3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/0mt6h672u2emret2bwi1.png" alt="me posting to producthunt"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You'll stay in phase two as long as you are still releasing and improving.&lt;/p&gt;

&lt;p&gt;Some marketing and outreach might be necessary in this phase.&lt;/p&gt;

&lt;p&gt;When you start getting traction and people start to use your product, you move on to &lt;strong&gt;phase 3 ↓&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Phase 3: Traction
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--HcgXuEvt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/i91uxzw2vc6qfl4yw23t.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--HcgXuEvt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/i91uxzw2vc6qfl4yw23t.png" alt="me showing google analytics"&gt;&lt;/a&gt;&lt;br&gt;
Congratulations! You made something that is usable and valuable for people!&lt;/p&gt;

&lt;p&gt;From here you can leave it, continue to work on it or start monetizing it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common pitfall #1: Never releasing
&lt;/h2&gt;

&lt;p&gt;This is the most common pitfall.&lt;/p&gt;

&lt;p&gt;You start building something, never get to the point where you can share it.&lt;/p&gt;

&lt;p&gt;Sometimes, it is ok. If your idea wasn't that good anyways.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Think small, and minimum viable product, to quickly move on to phase 2&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Common pitfall #2: Lose motivation after first release
&lt;/h2&gt;

&lt;p&gt;I &lt;em&gt;love&lt;/em&gt; building and shipping new things. Sometimes I get extremely motivated in the beginnings.&lt;/p&gt;

&lt;p&gt;The next step is harder. The step to take the creation and make it into something better. It requires iteration and hard work, and doesn't give you instant gratification.&lt;/p&gt;

&lt;p&gt;The idea didn't &lt;em&gt;take off&lt;/em&gt;, and you are unsure whether it's any point in continuing.&lt;/p&gt;

&lt;p&gt;I think it is important to learn that it might take more work before you find traction.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Your &lt;strong&gt;motivation&lt;/strong&gt; is a bad indicator of whether you should work on something or not.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  A note about business.
&lt;/h2&gt;

&lt;p&gt;You are probably thinking of something that is missing.&lt;/p&gt;

&lt;p&gt;What about &lt;em&gt;validation&lt;/em&gt; before building anything? What about &lt;em&gt;monetizing&lt;/em&gt; and &lt;em&gt;scaling&lt;/em&gt;?&lt;/p&gt;

&lt;p&gt;This model is solely based on the &lt;em&gt;build&lt;/em&gt; aspect. It doesn't mean the business parts aren't important. Because they are.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Business parts are important, but not included in this model&lt;/p&gt;
&lt;/blockquote&gt;

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

&lt;p&gt;Recognize the phases and the common pitfalls and you will have an easier time building useful stuff on the web.&lt;/p&gt;

&lt;p&gt;Make sure to share this with anyone you think would like to hear it!&lt;/p&gt;

&lt;p&gt;Which phase do your projects usually end up in? Let me know in the comments.&lt;/p&gt;

</description>
      <category>web</category>
      <category>indiehacking</category>
      <category>strategy</category>
    </item>
  </channel>
</rss>
