<?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: swyx</title>
    <description>The latest articles on DEV Community by swyx (@swyx).</description>
    <link>https://dev.to/swyx</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%2F47766%2F26fbd2bf-c352-447c-9b4f-f66652dc4899.jpg</url>
      <title>DEV Community: swyx</title>
      <link>https://dev.to/swyx</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/swyx"/>
    <language>en</language>
    <item>
      <title>Running Docker without Docker Desktop</title>
      <dc:creator>swyx</dc:creator>
      <pubDate>Fri, 27 May 2022 19:51:36 +0000</pubDate>
      <link>https://dev.to/swyx/running-docker-without-docker-desktop-339h</link>
      <guid>https://dev.to/swyx/running-docker-without-docker-desktop-339h</guid>
      <description>&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;brew install colima&lt;/code&gt; (&lt;a href="https://github.com/abiosoft/colima/" rel="noopener noreferrer"&gt;colima&lt;/a&gt; replaces the docker/dockerd binary)&lt;/li&gt;
&lt;li&gt;Install the right &lt;code&gt;docker-compose&lt;/code&gt; binary for your chipset from &lt;a href="https://github.com/docker/compose/releases" rel="noopener noreferrer"&gt;the releases page&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;sudo curl -L https://github.com/docker/compose/releases/download/v2.5.1/docker-compose-darwin-aarch64 -o /usr/local/bin/docker-compose&lt;/code&gt; for M1 Macs&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;chmod +x /usr/local/bin/docker-compose&lt;/code&gt; to let it execute&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You should now be able to run &lt;code&gt;docker-compose up&lt;/code&gt; as per normal but without needing Docker Desktop.&lt;/p&gt;

&lt;h2&gt;
  
  
  Context
&lt;/h2&gt;

&lt;p&gt;Although I am &lt;a href="https://twitter.com/swyx/status/1330900941747277826" rel="noopener noreferrer"&gt;known&lt;/a&gt; for loving Docker (the containerization technology), I am also known for being a critic of Docker Desktop (the desktop UI client for Docker) for its poor performance:&lt;/p&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-1523368365728837633-568" src="https://platform.twitter.com/embed/Tweet.html?id=1523368365728837633"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1523368365728837633-568');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1523368365728837633&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;p&gt;Docker Desktop is Docker (the company)'s first wedge into getting you to run proprietary software, so Docker strongly guides you to download it as the only way to get started:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.docker.com/get-started/" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F6764957%2F170769916-89b00431-4f4b-4c5a-85b7-5c73fd07a127.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In fact, new users can go pretty far without even realizing that you don't need Docker Desktop to run the Docker daemon. I've asked multiple people how to do it and nobody had a good answer, or at least, nobody had an answer that worked on M1 Macs, until now.&lt;/p&gt;

&lt;p&gt;For my &lt;a href="https://www.swyx.io/new-mac-setup/" rel="noopener noreferrer"&gt;2022 New Mac Setup guide&lt;/a&gt; I resolved to figure it out once and for all, and what you see here is what I ended up with!&lt;/p&gt;

</description>
      <category>docker</category>
    </item>
    <item>
      <title>Why TurboRepo Will Be The First Big Trend of 2022</title>
      <dc:creator>swyx</dc:creator>
      <pubDate>Mon, 27 Dec 2021 16:08:03 +0000</pubDate>
      <link>https://dev.to/swyx/why-turborepo-will-be-the-first-big-trend-of-2022-4gfj</link>
      <guid>https://dev.to/swyx/why-turborepo-will-be-the-first-big-trend-of-2022-4gfj</guid>
      <description>&lt;p&gt;TurboRepo was &lt;a href="https://vercel.com/blog/vercel-acquires-turborepo" rel="noopener noreferrer"&gt;acquired by Vercel&lt;/a&gt; recently and I caught up on Jared Palmer's excellent intro demo to see what the fuss is all about:&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/YX5yoApjI3M"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Here are quick notes for those too busy to view the whole thing, followed by personal reflections at the end.&lt;/p&gt;

&lt;h2&gt;
  
  
  TLDR
&lt;/h2&gt;

&lt;p&gt;See the TLDR of this post in thread form:&lt;/p&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-1475509463813529601-746" src="https://platform.twitter.com/embed/Tweet.html?id=1475509463813529601"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1475509463813529601-746');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1475509463813529601&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Monorepos
&lt;/h2&gt;

&lt;p&gt;You can refer to other sources for &lt;a href="https://www.toptal.com/front-end/guide-to-monorepos" rel="noopener noreferrer"&gt;definitions of Monorepos&lt;/a&gt; (2022 edit: Nrwl just launched &lt;a href="https://monorepo.tools/" rel="noopener noreferrer"&gt;https://monorepo.tools/&lt;/a&gt; which has their perspectives and comparisons), but we'll spend some time on covering why they are a worthwhile goal:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You can easily make &lt;strong&gt;cross cutting code changes&lt;/strong&gt; across multiple applications (eg &lt;code&gt;/frontend&lt;/code&gt; and &lt;code&gt;/backend&lt;/code&gt;) in one atomic commit&lt;/li&gt;
&lt;li&gt;You can easily &lt;strong&gt;search&lt;/strong&gt; across all projects&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Single source of truth&lt;/strong&gt; for many environment concerns you will want to standardize across your company, for example:

&lt;ul&gt;
&lt;li&gt;dependency management (important deps in one &lt;code&gt;package.json&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;code reuse of shared packages (e.g. &lt;code&gt;/design-system&lt;/code&gt; or &lt;code&gt;/common-utils&lt;/code&gt; or &lt;code&gt;/schema&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;configs (ESlint, TSconfig, etc)&lt;/li&gt;
&lt;li&gt;tests (from unit to e2e)&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;For library authors, it is also easier to publish packages with dependencies on each other.&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;Major JS ecosystem tools like React, Jest, pnpm, Next.js, and Yarn itself have moved to Monorepos, as have small startups and large companies like FB and &lt;a href="https://cacm.acm.org/magazines/2016/7/204032-why-google-stores-billions-of-lines-of-code-in-a-single-repository/fulltext" rel="noopener noreferrer"&gt;Google&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Origin of TurboRepo
&lt;/h2&gt;

&lt;p&gt;The origin story of TurboRepo started with this &lt;a href="https://github.com/jaredpalmer/tsdx/issues/122" rel="noopener noreferrer"&gt;looongstanding open issue&lt;/a&gt; on TSDX from Nate Moore:&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%2Fop5kdp9dw4ns6yeugdk2.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%2Fop5kdp9dw4ns6yeugdk2.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As an early volunteer on TSDX I studiously avoided  this issue because I never worked at a company with a large monorepo, and thought that it should be solved by dedicated tools like &lt;code&gt;yarn workspace&lt;/code&gt;, which at the time was just gaining traction itself.&lt;/p&gt;

&lt;p&gt;To solve this, Jared tried to extract Lerna into a monorepo tool, and when researching how big monorepo shops like Facebook and Google did task running, discovered that a lot of their advanced techniques had not made it into the larger JS ecosystem.&lt;/p&gt;

&lt;p&gt;So, TurboRepo was started with 3 objectives: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;make a monorepo tool that utilizes as many of these advanced techniques as possible &lt;strong&gt;with zero config&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;make it easy to &lt;strong&gt;incrementally adopt&lt;/strong&gt; (eg when moving from Lerna)&lt;/li&gt;
&lt;li&gt;make sure that it &lt;strong&gt;scales&lt;/strong&gt; (eg API design and architectural choices are flexible enough)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The fuller story of TurboRepo is told by Jared in this thread:&lt;/p&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-1346217789942591488-214" src="https://platform.twitter.com/embed/Tweet.html?id=1346217789942591488"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1346217789942591488-214');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1346217789942591488&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;h2&gt;
  
  
  What TurboRepo does
&lt;/h2&gt;

&lt;p&gt;The basic principle of TurboRepo is to &lt;strong&gt;never recompute work that has been done before&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;To do this, it generates a dependency graph from your build pipeline from a &lt;code&gt;turbo&lt;/code&gt; config in package.json, executes each task in turn, and fingerprints the input/caches the output of each task. &lt;/p&gt;

&lt;p&gt;When it is run a second time, if it finds work that matches a fingerprint, it restores from cache, and &lt;strong&gt;replays the logs&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to use TurboRepo
&lt;/h2&gt;

&lt;p&gt;The main CLI surface area is surprisingly small:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;npx create-turbo@latest turbo-demo&lt;/code&gt; scaffolds a monorepo with apps (&lt;code&gt;docs&lt;/code&gt;, &lt;code&gt;web&lt;/code&gt;) and packages (design system and shared configs (eslint, tsconfig))&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;turbo run build&lt;/code&gt; builds all apps at once, but importantly, when you run this command again the second build completes in 100ms because everything is cached. There are a &lt;a href="https://turborepo.org/docs/reference/command-line-reference" rel="noopener noreferrer"&gt;long list of flags you can add&lt;/a&gt; to modify what &lt;code&gt;turbo run&lt;/code&gt; does and outputs.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;turbo prune --scope=&amp;lt;target&amp;gt;&lt;/code&gt; generates a sparse/partial monorepo with a pruned lockfile for a target package.&lt;/li&gt;
&lt;li&gt;Remote Caching commands: &lt;code&gt;turbo login&lt;/code&gt; and &lt;code&gt;turbo link&lt;/code&gt; (explained later)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The &lt;code&gt;turbo&lt;/code&gt; config key
&lt;/h2&gt;

&lt;p&gt;TurboRepo uses a special key in &lt;code&gt;package.json&lt;/code&gt; called &lt;code&gt;turbo&lt;/code&gt; (&lt;a href="https://turborepo.org/docs/reference/configuration" rel="noopener noreferrer"&gt;docs here&lt;/a&gt;), and it is here that topological relationships between build tasks (and where to fingerprint for cache artifacts) are defined:&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="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"turbo"&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="nl"&gt;"baseBranch"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"origin/main"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"pipeline"&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="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="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"dependsOn"&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="s2"&gt;"^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;"outputs"&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="s2"&gt;".next/**"&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="nl"&gt;"test"&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="nl"&gt;"dependsOn"&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="s2"&gt;"^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;"outputs"&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="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"lint"&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="nl"&gt;"outputs"&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="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"dev"&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="nl"&gt;"cache"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&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="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="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;This helps Turbo create a Directed Acyclic Graph of your build that it can then walk in reverse for building and checking against its cache. You can even use &lt;a href="https://turborepo.org/docs/reference/command-line-reference#--graph" rel="noopener noreferrer"&gt;the &lt;code&gt;--graph&lt;/code&gt; flag&lt;/a&gt; to visualize your build graph with Graphviz.&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%2Fyj4pqsvaws83v8owgxtw.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%2Fyj4pqsvaws83v8owgxtw.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;(Having &lt;a href="https://twitter.com/swyx/status/1032665787436343297" rel="noopener noreferrer"&gt;tried out visualization tools before&lt;/a&gt;, imo this is a fun demo but not practically all that useful 🤷‍♂️) &lt;/p&gt;

&lt;p&gt;The other important thing to know is that you can run all these tasks together and Turbo will parallelize as much as possible:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;turbo run build &lt;span class="nb"&gt;test &lt;/span&gt;lint
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To understand what is running in parallel and debug build pipelines, you can even make Turbo output a profile with the &lt;code&gt;--profile&lt;/code&gt; flag to inspect the traces in Chrome DevTools!&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%2Fqhydl0cqmbisayfunrks.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%2Fqhydl0cqmbisayfunrks.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Remote Caching
&lt;/h2&gt;

&lt;p&gt;Remote caching is a beta feature, but is set to be far and away the showstopper in making TurboRepo scale. Normally, caches are generated and checked locally, so if you are reviewing code that a coworker has written, you'll have to build it locally too. &lt;/p&gt;

&lt;p&gt;Sounds inefficient? We can fix that.&lt;/p&gt;

&lt;p&gt;Remote Caching shares that cache globally (this is secure to that extent that hashes are secure), turning TurboRepo from a "single player" experience to a "co-op multiplayer" mode. The analogy that resonates a lot with users is that this is basically "Dropbox for your &lt;code&gt;dist&lt;/code&gt; directory".&lt;/p&gt;

&lt;p&gt;This is where Vercel's backing comes in - they are offering &lt;strong&gt;free remote caching&lt;/strong&gt; on TurboRepo builds - you'll still need to make a Vercel account, and they may charge for this later - but this works whether or not your app is built or hosted on Vercel. Brilliant move for everyone concerned! All TurboRepo users get free speedups, Vercel gets a bunch of signups (with network effect) and a possible future revenue source.&lt;/p&gt;

&lt;p&gt;Usage is pretty simple:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx turbo login &lt;span class="c"&gt;# login to Vercel&lt;/span&gt;
npx turbo &lt;span class="nb"&gt;link&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it! Could not be easier, and offers free speedups.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Future
&lt;/h2&gt;

&lt;p&gt;Jared ended the livestream by making a few comments on the TurboRepo roadmap&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Telemetry&lt;/li&gt;
&lt;li&gt;Sharding Parallel Tasks in other processes (currently, TurboRepo runs parallel tasks in the same singlethreaded process like Node does - to actually make use of full concurrency it should distribute that work to other processes.  Temporal, &lt;a href="https://www.swyx.io/why-temporal/" rel="noopener noreferrer"&gt;the project I work on&lt;/a&gt;, could be an interesting tool for that in future&lt;/li&gt;
&lt;li&gt;Presets (referred to as "Turbo Season 2")&lt;/li&gt;
&lt;li&gt;Smaller features

&lt;ul&gt;
&lt;li&gt;Public/private security model like npm&lt;/li&gt;
&lt;li&gt;More intelligent watch mode&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;There will probably be &lt;a href="https://vercel.com/contact/turborepo" rel="noopener noreferrer"&gt;Enterprise features&lt;/a&gt; too.&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;You can vote on feature ideas on the TurboRepo &lt;a href="https://github.com/vercel/turborepo/discussions" rel="noopener noreferrer"&gt;GitHub Community&lt;/a&gt; as well.&lt;/p&gt;

&lt;h2&gt;
  
  
  What About Nx?
&lt;/h2&gt;

&lt;p&gt;TurboRepo is most often compared to Nx, so I'm very grateful that Victor Savin (creator of Nx) has written &lt;a href="https://nx.dev/l/r/guides/turbo-and-nx" rel="noopener noreferrer"&gt;a page on the Nx docs&lt;/a&gt; detailing the differences he sees vs Turborepo.&lt;/p&gt;

&lt;p&gt;He's also made benchmarks for Nx vs TurboRepo you can try out:&lt;/p&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-1471209016827207689-81" src="https://platform.twitter.com/embed/Tweet.html?id=1471209016827207689"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1471209016827207689-81');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1471209016827207689&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;h2&gt;
  
  
  Personal Takeaways
&lt;/h2&gt;

&lt;p&gt;TurboRepo is a big deal for the JS community not just because it addresses build speeds (which are always a crowd pleaser), but also that it is a well defined abstraction that brings a lot of value out of the box, with a declarative build pipeline, great debugging/profiling options, and great docs.&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%2Fo3cfc9ybz5t6lq7w6htf.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%2Fo3cfc9ybz5t6lq7w6htf.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With 74% of its code in Go, TurboRepo is a great example of the &lt;a href="https://www.swyx.io/js-third-age/#the-third-age" rel="noopener noreferrer"&gt;Systems Core, Scripting Shell&lt;/a&gt; thesis, proving out the idea that the age of "JS tools in JS" is over because the need for speed on hot paths outweighs contributor learning curve concerns.&lt;/p&gt;

&lt;p&gt;Many people in the JS community (like my old self) have heard about the benefits of monorepos, but have been held back by the lack of good tooling tackling this problem head on. While there is a &lt;a href="https://turborepo.org/docs/guides/complimentary-tools" rel="noopener noreferrer"&gt;long list of monorepo tooling&lt;/a&gt; tackling various parts of the problem, I see TurboRepo as leading the charge for the new wave of monorepo tooling that will rise to prominence in the Third Age of JavaScript, thanks to strong backing and great developer marketing from Jared and Team Vercel.&lt;/p&gt;

&lt;h2&gt;
  
  
  Followup: Nx Chat
&lt;/h2&gt;

&lt;p&gt;I did a followup chat with the Nx founders to learn more about how they think about Monorepo Tooling: &lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/eZQ_jWaTCVM"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  Further reading
&lt;/h2&gt;

&lt;p&gt;Robin Wieruch did a much better writeup on &lt;a href="https://www.robinwieruch.de/javascript-monorepos/" rel="noopener noreferrer"&gt;what Monorepos are&lt;/a&gt; with code examples and more ideas on use cases!&lt;/p&gt;

&lt;p&gt;(&lt;a href="https://twitter.com/swyx/status/1478180887884083200" rel="noopener noreferrer"&gt;share it on Twitter here!&lt;/a&gt;)&lt;/p&gt;

</description>
      <category>devtools</category>
      <category>summaries</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Using Datasette for an ELT Personal Twitter Data Warehouse</title>
      <dc:creator>swyx</dc:creator>
      <pubDate>Sun, 26 Dec 2021 20:52:14 +0000</pubDate>
      <link>https://dev.to/swyx/using-datasette-for-an-elt-personal-twitter-data-warehouse-h6i</link>
      <guid>https://dev.to/swyx/using-datasette-for-an-elt-personal-twitter-data-warehouse-h6i</guid>
      <description>&lt;p&gt;Twitter publishes some analytics for you, and I check them about &lt;a href="https://twitter.com/swyx/status/1356247268236488709" rel="noopener noreferrer"&gt;once a month&lt;/a&gt;, but they are extremely limited when it comes to the important things, like exploring your follower graph.&lt;/p&gt;

&lt;p&gt;This is where Datasette comes in. Datasette is a great EDA (exploratory data analysis) tool for any arbitrary SQLite dataset. You can use it to build your own Twitter analytics!&lt;/p&gt;

&lt;p&gt;Today, at Simon Willison's prompting, I decided to try to replicate &lt;a href="https://simonwillison.net/2018/Jan/28/analyzing-my-twitter-followers/" rel="noopener noreferrer"&gt;his 2018 work in analyzing his own Twitter network&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;You can check out my own dataset here: &lt;a href="https://swyx-twitter-datasette.glitch.me/twitter/tweets?_sort_desc=favorite_count&amp;amp;_facet=retweeted&amp;amp;_facet=favorited&amp;amp;_facet=user&amp;amp;user=33521530&amp;amp;retweeted=1" rel="noopener noreferrer"&gt;https://swyx-twitter-datasette.glitch.me/twitter/tweets?_sort_desc=favorite_count&amp;amp;_facet=retweeted&amp;amp;_facet=favorited&amp;amp;_facet=user&amp;amp;user=33521530&amp;amp;retweeted=1&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Things to try:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;check out the selected facets&lt;/li&gt;
&lt;li&gt;sort/hide individual columns&lt;/li&gt;
&lt;li&gt;search&lt;/li&gt;
&lt;li&gt;build up sql filters (eg favorite_count &amp;gt; 100, retweet_count &amp;gt; 100)&lt;/li&gt;
&lt;/ul&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%2Fkk3myn3mja87oc6y33tj.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%2Fkk3myn3mja87oc6y33tj.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Keep in mind that all of this is automatically generated from a sqlite table (which itself was automatically generated by running &lt;a href="https://github.com/dogsheep/twitter-to-sqlite" rel="noopener noreferrer"&gt;twitter-to-sqlite&lt;/a&gt;), greatly reducing the bar for GUI-based, SQL-based EDA.&lt;/p&gt;

&lt;p&gt;And because it is web based and the SQL is serialized into the URL, you can easily share whatever you find with coworkers.&lt;/p&gt;

&lt;h2&gt;
  
  
  The How
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Optional: Get Credentials
&lt;/h3&gt;

&lt;p&gt;The most time consuming piece of this is going to be getting Twitter API credentials. The process is largely unchanged since 2018 - get a developer account on  &lt;a href="https://developer.twitter.com/" rel="noopener noreferrer"&gt;https://developer.twitter.com/&lt;/a&gt; (some approval time may be required... so get this out of the way ASAP), then create a new app &lt;a href="https://apps.twitter.com/" rel="noopener noreferrer"&gt;https://apps.twitter.com/&lt;/a&gt; and eventually get to the four "Keys and Tokens" you will need to do this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft11lhzhwf15r7kzb0vnf.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%2Ft11lhzhwf15r7kzb0vnf.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Optional: Test Your Credentials
&lt;/h3&gt;

&lt;p&gt;You can test your credentials by writing a little Python script locally (&lt;code&gt;pip install requests_oauthlib&lt;/code&gt;, then run &lt;code&gt;python script.py&lt;/code&gt;. I also like to use &lt;a href="https://docs.python.org/3/tutorial/venv.html" rel="noopener noreferrer"&gt;venv&lt;/a&gt;)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;requests_oauthlib&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;OAuth1Session&lt;/span&gt;
&lt;span class="n"&gt;twitter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;OAuth1Session&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;client_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;FILL_ME_IN&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;client_secret&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;FILL_ME_IN&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;resource_owner_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;FILL_ME_IN-FILL_ME_IN&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;resource_owner_secret&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;FILL_ME_IN&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;twitter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;   &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;https://api.twitter.com/2/users/by/username/swyx&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;twitter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;   &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;https://api.twitter.com/2/users/1465910794114134018/following/?user.fields=public_metrics&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By default you only have access to the Twitter API v2 endpoints, though you can apply for Elevated Access if you need Twitter API v1 as many libraries and blogposts are still on API v1.&lt;/p&gt;

&lt;p&gt;From here you can browse &lt;a href="https://developer.twitter.com/en/docs/twitter-api" rel="noopener noreferrer"&gt;the Twitter API reference&lt;/a&gt; for what data you'd like to extract... but you don't have to, if you agree enough with Simon's opinions in &lt;a href="https://github.com/dogsheep/twitter-to-sqlite" rel="noopener noreferrer"&gt;twitter-to-sqlite&lt;/a&gt;!&lt;/p&gt;

&lt;h3&gt;
  
  
  Scraping your data with twitter-to-sqlite
&lt;/h3&gt;

&lt;p&gt;One thing I love about how Simon has been building Datasette is &lt;a href="https://github.com/topics/datasette-tool" rel="noopener noreferrer"&gt;the rich ecosystem of data extraction tools&lt;/a&gt; he has also groomed. He's got standard things for extracting data from many sources he cares about into SQLite (and, as I've already said, Datasette takes it from there all the way to a viewable explorable product). &lt;/p&gt;

&lt;p&gt;So it is with &lt;a href="https://github.com/dogsheep/twitter-to-sqlite" rel="noopener noreferrer"&gt;twitter-to-sqlite&lt;/a&gt;:&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="nv"&gt;$ &lt;/span&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;twitter-to-sqlite

&lt;span class="nv"&gt;$ &lt;/span&gt;twitter-to-sqlite auth
Create an app here: https://developer.twitter.com/en/apps
Then navigate to &lt;span class="s1"&gt;'Keys and tokens'&lt;/span&gt; and &lt;span class="nb"&gt;paste &lt;/span&gt;&lt;span class="k"&gt;in &lt;/span&gt;the following:

API key: xxx
API secret key: xxx
Access token: xxx
Access token secret: xxx

&lt;span class="nv"&gt;$ &lt;/span&gt;twitter-to-sqlite user-timeline twitter.db
Importing tweets  &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="c"&gt;#####-------------------------------]  2799/17780  00:01:39&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This gives you a nice little &lt;code&gt;twitter.db&lt;/code&gt; file containing every tweet you've ever made or liked or retweeted that is still on your timeline (timeline gets cut off after 3,200 tweets). You can also crawl your followers with &lt;code&gt;twitter-to-sqlite followers twitter.db&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Deploying to Glitch
&lt;/h3&gt;

&lt;p&gt;Simon's old method relies on Zeit Now, which has since been deprecated. Datasette will work wherever serverful web hosting works, but its nice to host this somewhere free since it is just a personal public data tool. Render.com or Fly.io would probably work, but I felt like trying out Glitch since that is &lt;a href="https://simonwillison.net/2019/Apr/23/datasette-glitch/" rel="noopener noreferrer"&gt;Simon's preferred platform&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Unfortunately I &lt;a href="https://twitter.com/swyxio/status/1475170895488774146" rel="noopener noreferrer"&gt;ran into a bunch of problems here&lt;/a&gt; and took a couple of hours to fix them. TLDR; just remix my Glitch:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://glitch.com/edit/#!/remix/swyx-twitter-datasette" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.glitch.com%2F2703baf2-b643-4da7-ab91-7ee2a2d00b5b%252Fremix-button.svg" alt="Remix on Glitch"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The important unlock here is understanding how to upload your SQLite db to Glitch for it to run. You can't just drag and drop it into the "assets" folder as that is just for CDN hosting, and you need access to this file in the Glitch build process. &lt;/p&gt;

&lt;p&gt;The way I figured out how to do it was to &lt;a href="https://support.glitch.com/t/possible-to-code-locally-and-push-to-glitch-with-git/2704/3" rel="noopener noreferrer"&gt;clone the git repo locally&lt;/a&gt; (unlike that answer, there is no more Advanced Options menu in Glitch, you have to go Tools -&amp;gt; Import and Export -&amp;gt; copy your project's Git URL), and add the file to git (on a different branch, because of Glitch's idiosyncratic setup... read the instructions on the forum).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://&amp;lt;git access token&amp;gt;@api.glitch.com/git/swyx-twitter-datasette
&lt;span class="nb"&gt;cd &lt;/span&gt;swyx-twitter-datasette

git checkout &lt;span class="nt"&gt;-b&lt;/span&gt; addData

&lt;span class="c"&gt;###&lt;/span&gt;
&lt;span class="c"&gt;# add/replace your twitter.db file to this repo and commit it&lt;/span&gt;
&lt;span class="c"&gt;###&lt;/span&gt;

git push &lt;span class="nt"&gt;--set-upstream&lt;/span&gt; origin addData
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And then inside Glitch you'll need to open the Terminal and do:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git merge addData
refresh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the twitter.db should show up in there.&lt;/p&gt;

&lt;p&gt;If all goes well your project should be running like mine: &lt;a href="https://swyx-twitter-datasette.glitch.me/twitter/tweets" rel="noopener noreferrer"&gt;https://swyx-twitter-datasette.glitch.me/twitter/tweets&lt;/a&gt; (you can rename your Glitch to a custom vanity subdomain) and you can explore to your hearts content!&lt;/p&gt;

&lt;h2&gt;
  
  
  The Why
&lt;/h2&gt;

&lt;p&gt;So. That's a lot of work to navel-gaze at a bunch of Tweets. Why bother?&lt;/p&gt;

&lt;p&gt;It's not really about the tweets for me. It's about lowering the cost of EDA. I'm putting in my reps when I don't need it, so that I can be faster/more confident when I do.&lt;/p&gt;

&lt;p&gt;Everyone wants to operate on data-driven insights, but the cost of wrangling data is often too high and so data, especially your own data, doesn't get looked at.&lt;/p&gt;

&lt;p&gt;EDA tools like Datasette dramatically lower the cost of data analysis, with a surprisingly simple &lt;a href="https://www.guru99.com/etl-vs-elt.html" rel="noopener noreferrer"&gt;ELT&lt;/a&gt; contract: You handle the Extract phase, it handles the Load, and exposes a standard UI for you to do dynamic Transforms.&lt;/p&gt;

&lt;p&gt;Since the UI is constrained but has all the basic EDA features, it reduces the amount of custom coding you have to do, while giving you just enough flexibility to make queries and derive insights for any arbitrary need.&lt;/p&gt;

&lt;p&gt;And since can see all your data and all fields at a glance, you can come up with questions you didn't even think to ask. ELT with Datasette is the data-driven equivalent of &lt;a href="https://cardsmith.co/dumping-out-the-legos/" rel="noopener noreferrer"&gt;dumping out your legos&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.ytimg.com%2Fvi%2FAQT6uqktdOw%2Fmaxresdefault.jpg" 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%2Fi.ytimg.com%2Fvi%2FAQT6uqktdOw%2Fmaxresdefault.jpg" alt="https://i.ytimg.com/vi/AQT6uqktdOw/maxresdefault.jpg"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Further Reading
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://datasette.io/" rel="noopener noreferrer"&gt;Read the Datasette Docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://simonwillison.net/2021/Jul/22/small-data/" rel="noopener noreferrer"&gt;Simon's PyGotham 2020 talk on Datasette here&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://news.ycombinator.com/item?id=25090218" rel="noopener noreferrer"&gt;Personal Data Warehouses on Hacker News&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>datasette</category>
      <category>tech</category>
      <category>data</category>
      <category>python</category>
    </item>
    <item>
      <title>How to customize your Transistor.fm Website with JS and CSS</title>
      <dc:creator>swyx</dc:creator>
      <pubDate>Fri, 03 Dec 2021 07:58:15 +0000</pubDate>
      <link>https://dev.to/swyx/how-to-customize-your-transistorfm-website-with-js-and-css-3afc</link>
      <guid>https://dev.to/swyx/how-to-customize-your-transistorfm-website-with-js-and-css-3afc</guid>
      <description>&lt;p&gt;The default Transistor.fm website is kinda ugly. &lt;/p&gt;

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

&lt;p&gt;Here's mine after a bit of work: &lt;a href="https://swyx.transistor.fm/"&gt;https://swyx.transistor.fm/&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;Here's how to customize your Transistor.fm website if you use Transistor. But also it's a simple guide to do clientside customizations of almost any website whose code you don't control.&lt;/p&gt;

&lt;h2&gt;
  
  
  How To Video (3mins)
&lt;/h2&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/EyV6QHk63lg"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  Code
&lt;/h2&gt;

&lt;p&gt;Head HTML:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="p"&gt;()&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;el&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="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pathname&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/episodes&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;el&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;div&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nb"&gt;document&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getElementsByClassName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;site-content&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parentNode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;insertBefore&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getElementsByClassName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;site-content&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;0&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="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// assuming index page&lt;/span&gt;
    &lt;span class="nx"&gt;el&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getElementsByClassName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;site-first-episode&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerHTML&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`
&amp;lt;iframe title="swyx mixtape embed" width="100%" height="390" frameborder="no" scrolling="no" seamless src="https://share.transistor.fm/e/learn-in-podcast/playlist/dark"&amp;gt;&amp;lt;/iframe&amp;gt;
`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;swyxembed&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;function&lt;/span&gt; &lt;span class="nx"&gt;makeLive&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="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;swyxembed&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onload&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="nx"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;makeLive&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nx"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;makeLive&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;CSS:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.site-intro&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1.25rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;60ch&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="nb"&gt;auto&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.site-credits&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;fixed&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100vw&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;bottom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;#site-footer&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;hidden&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;#swyxembed&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nc"&gt;.site-featured-episodes&lt;/span&gt; &lt;span class="nc"&gt;.site-episode&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
&lt;span class="nc"&gt;.site-episode-detail&lt;/span&gt; &lt;span class="nc"&gt;.site-episode&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;block&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nc"&gt;.site-episode&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;grid&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;grid-template-areas&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;"date title"&lt;/span&gt;
                       &lt;span class="s1"&gt;"date desc"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;grid-template-rows&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;minmax&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1rem&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;auto&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="n"&gt;fr&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;grid-template-columns&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10ch&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="n"&gt;fr&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;grid-column-gap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;margin-bottom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nc"&gt;.site-episode&lt;/span&gt; &lt;span class="nt"&gt;time&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="py"&gt;grid-area&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;date&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nc"&gt;.site-episode&lt;/span&gt; &lt;span class="nt"&gt;time&lt;/span&gt; &lt;span class="nt"&gt;span&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;text-transform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;none&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nc"&gt;.site-episode&lt;/span&gt; &lt;span class="nt"&gt;h2&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="py"&gt;grid-area&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1.5rem&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nc"&gt;.site-episode&lt;/span&gt; &lt;span class="nc"&gt;.site-episode-summary&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="py"&gt;grid-area&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;desc&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nc"&gt;.site-episode&lt;/span&gt; &lt;span class="nt"&gt;nav&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;none&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;h2&gt;
  
  
  Other customization ideas
&lt;/h2&gt;

&lt;p&gt;Look at the css for &lt;a href="https://gretchen.transistor.fm/"&gt;https://gretchen.transistor.fm/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can also use custom fonts: &lt;a href="https://www.avillatheory.com/"&gt;https://www.avillatheory.com/&lt;/a&gt; &lt;a href="https://www.youtube.com/watch?v=MFQMczanAm4&amp;amp;feature=youtu.be"&gt;https://www.youtube.com/watch?v=MFQMczanAm4&amp;amp;feature=youtu.be&lt;/a&gt;&lt;/p&gt;

</description>
      <category>tips</category>
      <category>javascript</category>
      <category>css</category>
    </item>
    <item>
      <title>Fave New Podcasts in 2021</title>
      <dc:creator>swyx</dc:creator>
      <pubDate>Thu, 02 Dec 2021 07:25:13 +0000</pubDate>
      <link>https://dev.to/swyx/fave-new-podcasts-in-2021-40k1</link>
      <guid>https://dev.to/swyx/fave-new-podcasts-in-2021-40k1</guid>
      <description>&lt;p&gt;You probably already know what the top podcasts are in each domain, so my hope is to give some spotlight to the up-and-comers. (These podcasts may not have started in 2021, but they are new to me this year).&lt;/p&gt;

&lt;p&gt;Without further ado:&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Best New Tech Podcasts in 2021
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://corecursive.com/"&gt;Corecursive&lt;/a&gt; - Adam Gordon Bell&lt;/strong&gt;: really fascinating interviews with tech OG's. Many stories that have never been told. Regularly tops Hacker News. I still have the &lt;a href="https://corecursive.com/063-apple-2001/"&gt;Apple 2001&lt;/a&gt; episode saved.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://about.sourcegraph.com/podcast/"&gt;Sourcegraph&lt;/a&gt; - Beyang Liu&lt;/strong&gt;: In depth founder/engineer interviews from the CTO of Sourcegraph, exploring his own personal curiosity. Personal pick: &lt;a href="https://about.sourcegraph.com/podcast/david-cramer/"&gt;David Cramer of Sentry&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://www.youtube.com/playlist?list=PLZfuUWMTtMcC1DZF6HxJhqsGrBXu8Jzi7"&gt;Stevey's Tech Talk&lt;/a&gt; - Steve Yegge&lt;/strong&gt;: Truth bombs from our industry's best truthteller. Personal pick: &lt;a href="https://swyx.transistor.fm/episodes/xbox-one-and-bad-execs-steve-yegge"&gt;Why CEOs crash and burn&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://podrocket.logrocket.com/"&gt;Podrocket&lt;/a&gt; - LogRocket's Brian, Ben, and Kate&lt;/strong&gt;: Interviews of the frontend ecosystem, including &lt;a href="https://podrocket.logrocket.com/swyx"&gt;yours truly&lt;/a&gt; and &lt;a href="https://podrocket.logrocket.com/svelte"&gt;yours truly&lt;/a&gt;! Personal pick: &lt;a href="https://podrocket.logrocket.com/9"&gt;Kiwicopple of Supabase&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://devtools.fm/"&gt;Devtools.fm&lt;/a&gt; - Justin Bennett and Andrew Lisowski&lt;/strong&gt;: Interviews of the frontend ecosystem, including &lt;a href="https://devtools.fm/episode/13"&gt;yours truly&lt;/a&gt;! Personal pick: &lt;a href="https://devtools.fm/episode/9"&gt;Jason Laster of Replay.io&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Best New Business/Crypto Podcasts in 2021
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://www.listennotes.com/podcasts/business-breakdowns-colossus-MXUSbYila-6/"&gt;Business Breakdowns&lt;/a&gt; by Jesse Pujji&lt;/strong&gt;: Deeply researched breakdowns of big businesses. Personal pick: &lt;a href="https://www.listennotes.com/podcasts/business-breakdowns/moderna-the-software-of-life-V6FBhzbfMU3/"&gt;Moderna with Jason Kelly&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://www.coindesk.com/podcasts/the-breakdown-with-nlw/"&gt;The Breakdown&lt;/a&gt; with NLW&lt;/strong&gt;: daily news updates on Crypto.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://www.notboring.co/"&gt;Not Boring&lt;/a&gt; by Packy McCormick&lt;/strong&gt;: Technically a newsletter, but still counts because he reads every essay on audio. Personal Pick: &lt;a href="https://twitter.com/swyx/status/1431486611296964611"&gt;Ramp's Double Unicorn Rounds&lt;/a&gt;. &lt;a href="https://twitter.com/packyM/status/1379055262913466373"&gt;His personal journey&lt;/a&gt; is also inspiring.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="http://podcast.banklesshq.com/"&gt;Bankless&lt;/a&gt; podcast&lt;/strong&gt;: entertaining updates on crypto, ETH biased but not maximalist.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Best New Creator/General Podcasts in 2021
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://www.youtube.com/channel/UC3lSc9lP47q-_Wyp3LCWbYw"&gt;Creator Lab&lt;/a&gt; - Bilal Zaidi&lt;/strong&gt;: Generic Interview Successful People podcast, but well executed. Personal Pick: &lt;a href="https://twitter.com/swyx/status/1351472680415883268"&gt;Polina Marinova&lt;/a&gt; and &lt;a href="https://twitter.com/swyx/status/1350824207051997192"&gt;Tim Urban&lt;/a&gt; and &lt;a href="https://www.youtube.com/watch?v=t0HJTD_eyR8&amp;amp;t=47s"&gt;Rahul Vohra&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://pod.cmxhub.com/episodes/"&gt;Masters of Community&lt;/a&gt; - David Spinks&lt;/strong&gt;: Truly one of the top 3 best resources on learning to build/manage community on the planet. Personal picks: &lt;a href="https://twitter.com/swyx/status/1389389360433307650"&gt;Jon Levy&lt;/a&gt; and &lt;a href="https://pod.cmxhub.com/episodes/matthew-kobach-greatest-hits"&gt;Matthew Kobach&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://wistia.com/series/talking-too-loud"&gt;Talking Too Loud&lt;/a&gt; - Chris Savage&lt;/strong&gt;: Great rapport and banter with Sylvie. Personal Pick: &lt;a href="https://jayacunzo.com/3-clips?wchannelid=mkkamqxlvs&amp;amp;wmediaid=im2xaeptts"&gt;Jay Acunzo's picks of them&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://www.brainspodcast.com/"&gt;Brains&lt;/a&gt; - Courtland Allen and Julian Shapiro&lt;/strong&gt;: Still new, but great discussions from two of the most thoughtful creators out there, in an interesting 4-player format. Personal pick: &lt;a href="https://swyx.transistor.fm/episodes/writing-to-engage-and-writing-for-action-julian-shapiro-aella-sam-parr"&gt;Sam Parr and Aella&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://notoverthinking.com/"&gt;Not Overthinking&lt;/a&gt; - Abdaal Brothers&lt;/strong&gt;: Ali Abdaal getting the piss taken out of him by his surprisingly attractive older brother is just amazing.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Context
&lt;/h2&gt;

&lt;p&gt;In 2019 I published &lt;a href="https://www.swyx.io/fave-podcasts/"&gt;the list of podcasts I listen to&lt;/a&gt;, and &lt;a href="https://www.swyx.io/fave-podcasts-2020/"&gt;last year&lt;/a&gt; I updated it with newer podcasts, so I'm continuing that tradition this year!&lt;/p&gt;

&lt;p&gt;The big difference is that &lt;a href="https://www.swyx.io/personal-podcasting"&gt;I now run a daily mixtape&lt;/a&gt; which clips from all the podcasts I listen to, so you get a live "best of" feed from me if you &lt;a href="https://swyx.transistor.fm/"&gt;subscribe&lt;/a&gt;. I also run my own &lt;a href="https://www.youtube.com/playlist?list=PLH8IAbt5kqZOlrX34L93lFzOZ1k-ry_Ll"&gt;infrequent YouTube podcast&lt;/a&gt; and am a panelist on &lt;a href="https://www.svelteradio.com/episodes"&gt;Svelte Radio&lt;/a&gt;. &lt;/p&gt;

&lt;h2&gt;
  
  
  Good But Dead
&lt;/h2&gt;

&lt;p&gt;I had these podcasts lined up for my end of year lists, but they also died this year so I can't continue to recommend them. However, they have some backlogs worth digging through:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://3clipspodcast.com/"&gt;3 Clips when it was under Jay Acunzo&lt;/a&gt; - really good, inspiration for my own mixtape of podcasts&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://constine.substack.com/"&gt;Josh Constine's Press Club&lt;/a&gt; - had some good early essays on creator economy&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://underthehoodpod.robinhood.com/"&gt;Under the Hood&lt;/a&gt; - Robinhood's CEO Vlad Tenev trying to be a podcaster was both awkward and interesting.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://open.spotify.com/show/3L9tzrt0CthF6hNkxYIeSB"&gt;Spotify: A Product Story&lt;/a&gt; - Spotify's CRDO telling Spotify's own hagiography was still well told. &lt;a href="https://twitter.com/swyx/status/1373816614999658499"&gt;First episode on Metallica and Sean Parker is great&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Still Good
&lt;/h2&gt;

&lt;p&gt;I love good picks that last! Here are some special highlights and shoutouts from &lt;a href="https://www.swyx.io/fave-podcasts-2020/"&gt;my 2020 picks&lt;/a&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://soundcloud.com/clublifebytiesto"&gt;Clublife&lt;/a&gt;&lt;/strong&gt;: Tiesto is still banging out the hits for free, amazing&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://www.earwolf.com/show/conan-obrien/"&gt;Conan&lt;/a&gt;&lt;/strong&gt;: Conan, Sona and gang are still hilarious and great company. &lt;a href="https://www.earwolf.com/episode/live-with-will-arnett-at-the-wiltern-theatre/"&gt;Their recent live show was INCREDIBLE&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://softwaresocial.dev/"&gt;Software Social&lt;/a&gt;&lt;/strong&gt;: Michele has now pulled off a book launch (&lt;a href="https://twitter.com/mjwhansen/status/1437414398184808451"&gt;I liked it&lt;/a&gt;) and Colleen now has a job! It has been fun to follow along.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://writingexcuses.com/"&gt;Writing Excuses&lt;/a&gt;&lt;/strong&gt;: Brandon Sanderson hasn't swung by in forever, but the tips have continued flowing! I liked the episode on &lt;a href="https://writingexcuses.com/2020/10/25/15-43-audiobook-narration-with-bruce-d-richardson/"&gt;Audiobooks&lt;/a&gt; and they had some good stuff on MICE and Game/Worldbuilding.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>reflections</category>
      <category>podcast</category>
    </item>
    <item>
      <title>Community Heat, or Why You Should Get Good at Events</title>
      <dc:creator>swyx</dc:creator>
      <pubDate>Mon, 08 Nov 2021 01:32:56 +0000</pubDate>
      <link>https://dev.to/swyx/community-annealing-or-why-you-should-get-good-at-events-adg</link>
      <guid>https://dev.to/swyx/community-annealing-or-why-you-should-get-good-at-events-adg</guid>
      <description>&lt;p&gt;When Apple launches a product, it doesn't come right out and say "we're launching our new MacBook next week". &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;They make you guess.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Predict. &lt;/p&gt;

&lt;p&gt;Watch. &lt;/p&gt;

&lt;p&gt;Review.&lt;/p&gt;

&lt;p&gt;They invite people to a "Special Event", and throw up a logo or video that juuuust so slightly hints at what they're releasing. Journalists know to clear their schedule, other companies defer their own releases. Fanboys and the media lap it up, giving millions of dollars worth of earned attention.&lt;/p&gt;

&lt;p&gt;Apple is of course a special, secretive, historic outlier, but I think even open source companies can take a leaf from their book by understanding how to shape an event, galvanize community, and become a launching pad. This was my reflection after observing Next.js Conf for the last few years, as I covered in my chat with Lee Robinson of Vercel, the "Apple" of web developer platforms:&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/mlsTlFW7BSo"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  Community Heat
&lt;/h2&gt;

&lt;p&gt;Community building is all the rage, but all too often, it takes the form of Discourse forums, Discord channels, Slack rooms, Twitter hashtags. The sheer amount of always-on, background-persistent community is bewildering (and slightly stressful, to be honest). And the quality of interaction is very hit and miss; the older I get, the less patience I have to sit around in a chat room waiting for a typing indicator to resolve to "haha". Yet I stick around because sometimes it upgrades into a full-blown conversation between knowledgeable individuals you can't get anywhere else.&lt;/p&gt;

&lt;p&gt;The difference here we are observing is "&lt;strong&gt;community heat&lt;/strong&gt;", a term I've borrowed from McLuhan:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"Cold" community is async. "Hot" community is sync.&lt;/li&gt;
&lt;li&gt;Forums and Mailing lists tend to be cold: nobody ever expects a reply the same day, if at all (exceptions exist)&lt;/li&gt;
&lt;li&gt;Chats are a chimera; cold by default, but randomly upgrading to hot when both sides happen to have the time and mood to chat.&lt;/li&gt;
&lt;li&gt;In-person meetups and conferences are white-hot: if someone is talking to you and there's even 1-2 second gap between responses, it would be uncomfortably awkward.&lt;/li&gt;
&lt;li&gt;Cold communities tend to be much larger, yet far less engaged, than hot communities, for obvious reasons&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Some communities exist purely cold - I'm thinking Reddit forums and the &lt;a href="https://twitter.com/swyx/status/1390863292847951876"&gt;comments section of YouTube videos&lt;/a&gt; and others are hot-only (I'm thinking the ephemeral communities that spring up around conferences, that then disappear for the rest of the year).&lt;/p&gt;

&lt;p&gt;My assertion is that communities are better with an annealing process, alternating hot and cold (&lt;a href="https://www.swyx.io/blogpost-annealing/"&gt;like writing blogposts&lt;/a&gt;!). The "hot" event spills over into the cold community and keeps it warm, and the cold community passively builds excitement for the next "hot" event.&lt;/p&gt;

&lt;p&gt;This is why I'm always slightly bemused when people ask me about the "Slack or Discord?" for building community. Most people know how to run a Slack or Discourse or Discord or whatever. I don't much care. If you have no events strategy there's only so much community you can build.&lt;/p&gt;

&lt;h2&gt;
  
  
  Get Good at Events
&lt;/h2&gt;

&lt;p&gt;Below are my thoughts in an unordered list, as I do not regard myself an expert.&lt;/p&gt;

&lt;h3&gt;
  
  
  Venue
&lt;/h3&gt;

&lt;p&gt;The days of offline-only conferences are basically over; the secret is out that content wants to be free and accessible to all. You have to be successful with online events if you want to be successful period, particularly in tech. Yet an online-only strategy is ill-advised - people are spoiling for in person connection, and you can find out so much about the customer face to face. &lt;/p&gt;

&lt;p&gt;I think the right split of online to offline is probably 90/10, or 80/20. The overall goal should be to default to online stuff, but to identify 5-10 IRL opportunities and make each absolutely unforgettable and worthwhile; a real event rather than just an appearance.&lt;/p&gt;

&lt;p&gt;For online confs: Hopin seems popular, but you could also roll your own with the Vercel Event Starter Kit, React Conf recently did. We run Svelte Summit purely on YouTube. I also enjoy Gather.town for the novelty element although I wonder if it will get old fast.&lt;/p&gt;

&lt;p&gt;For online meetups &amp;lt;100 people, Zoom is fine; above 100, you should probably use Zoom's Webinars feature where you can more tightly control attendees and manage Q&amp;amp;A.&lt;/p&gt;

&lt;p&gt;IRL venues should be planned and booked at least 3-6months out, for a combination of venue timelines and personal travel plannin.&lt;/p&gt;

&lt;h3&gt;
  
  
  Frequency
&lt;/h3&gt;

&lt;p&gt;The ideal cadence for companies running community/marketing/devrel (yes I'm intentionally being obtuse about the difference) varies. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Yearly&lt;/strong&gt;: Cloudflare does one &lt;a href="https://www.swyx.io/cloudflare-go/"&gt;Birthday Week&lt;/a&gt; a year, where the goal is to dominate the news cycle for an entire week, impressing people more than if the same news were dripped out over a year. However, large news may crowd out smaller news, and the arbitrarily chunky deadline forces premature launches. AWS has resorted to "dumping" smaller announcements pre-Re:invent just to keep Re:invent announcements to a few hundred truly top ones.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Quarterly&lt;/strong&gt;: If you ship on a semi frequent basis, you may prefer a quarterly event with all your product updates, &lt;a href="https://www.youtube.com/channel/UCISzRlSAjy9SnLT6N5jc7pA"&gt;as WorkOS does&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Monthly&lt;/strong&gt;: At Temporal, we keep it simple - one "community meetup" a month (&lt;a href="https://temporal.io/meetup"&gt;signup&lt;/a&gt;, &lt;a href="https://www.youtube.com/playlist?list=PLl9kRkvFJrlSM-s-s-8WbgjN-R3Y4L7WG"&gt;watch&lt;/a&gt;). It's a "meetup" because we hope it is just the first of many meetups, run by us but someday hopefully entirely run by our users. We are also seeding a mailing list of hundreds of people who have come to our meetups, so that we can get them to come to a conference when we hold one in future (I call this the GitNation strategy, because that is how they lay groundwork for conferences)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Weekly&lt;/strong&gt;: I like this for regular livestreams and hangouts, like Netlify's &lt;a href="https://www.learnwithjason.dev/"&gt;Learn with Jason&lt;/a&gt; or AWS' &lt;a href="https://www.youtube.com/containersfromthecouch"&gt;Containers from the Couch&lt;/a&gt;. I think deeper relationships can only be built by showing up at least weekly; any less frequent than that, and reaching out to someone feels like "a whole thing".&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Naming
&lt;/h3&gt;

&lt;p&gt;I do think that naming matters. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We've already talked about the "Special Event" thing. &lt;/li&gt;
&lt;li&gt;I would never be caught dead attending a "webinar", but I've shown up at many a "workshop" with basically the same content (although inauthentic marketer types who give any webinar vibes have me reaching for the red X as soon as they open their mouth). &lt;/li&gt;
&lt;li&gt;We've also done "teach-ins" and "office hours" with similar effect.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Curation
&lt;/h3&gt;

&lt;p&gt;When conferences first panic-moved online during Covid, they lifted-and-shifted the schedule and curation as well - 8 hour days, CFP process, the whole lot.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Then we realized nobody sits at their computer watching a livestream for 8 hours.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This happens with every platform shift - first the Skeuomorphism, because it's familiar, then the Nativism, taking advantage of the inherent qualities of the platform from first principles with total disregard to legacy. When Microsoft &lt;a href="https://mobiforge.com/timeline/windows-phone-history"&gt;first ported Windows to mobile&lt;/a&gt; it moved everything, start menu, keyboard and all. Took us 10 years to find the interface native to the form factor.&lt;/p&gt;

&lt;p&gt;With Svelte Society we did the same thing, lifting and shifting the format, for our Svelte Summit conferences in 2020. This time around we are going "native" - instead of going thru an agonizing CFP selection process, we accepted everyone and moved the point of curation down to the produced video rather than just going by title and abstract (together with optional speaker training for this new burden). We curate the best mix for our 4 hour main stage and use the infinite shelf space of YouTube to release the videos in mini events the rest of the year.&lt;/p&gt;

&lt;p&gt;I'm certainly not holding ourselves out as exemplary; I'm just trying to give you ideas. For example I think that "purely meritocratic" selection processes are also not ideal - many great speakers have gone invite-only and will never apply to your blind CFP process even though they have the perfect topic. A good curator would proactively reach out as part of their service to conference attendees. Putting out a "request for topics" would also help.&lt;/p&gt;

&lt;h3&gt;
  
  
  Marketing
&lt;/h3&gt;

&lt;p&gt;Only one number matters for conference marketing:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Registrations.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I'll say it louder for the people in the back.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;GO FOR REGISTRATIONS.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It's the biggest number. Market the shit out of it. It's incredible. Nobody is incentivized to question it. Everyone knows the flaws but play along anyway in wonderful kayfabe. Speakers can brag about it. Sponsors can justify bigger budgets. You don't have a physical venue so you don't actually need to worry about pesky things like fire safety and refreshments logistics. Pump it up shamelessly and often.&lt;/p&gt;

&lt;p&gt;What's incredible is the &lt;a href="https://www.swyx.io/mimicry-reflexivity/"&gt;reflexivity&lt;/a&gt; of the whole thing. Humans are semi-herd creatures — an event will be big because it says it is big. &lt;/p&gt;

&lt;p&gt;Marketing matters for async much more than it does for sync. Particularly when I hear that an event has 70,000 people signed up, I'm more likely to catch up on the video async. A shitty event will have like a 0.5:1 async to live view ratio, a well marketed event is more like 10:1. (It takes actually legendary talks to breach 1000:1 and beyond).&lt;/p&gt;

&lt;p&gt;Oh and don't forget to keep them around to market the &lt;em&gt;next&lt;/em&gt; event.&lt;/p&gt;

&lt;h3&gt;
  
  
  Post Production
&lt;/h3&gt;

&lt;p&gt;Dumping the conference video straight to YouTube is economical, but undersells the huge amount of opportunity available to brush up the quality after the event. If your async to live ratio is 10:1 then the vast majority of people will be watching your event async, and it serves as marketing for your next event. Make it awesome and cut out all the boring. Some organizers go to the extent of shooting trailers; I don't know how yet but I'll gladly welcome recommendations I can feature here.&lt;/p&gt;

&lt;p&gt;Any other event advice?&lt;/p&gt;

</description>
      <category>reflections</category>
      <category>community</category>
      <category>devrel</category>
    </item>
    <item>
      <title>100 Bytes of CSS to look great everywhere</title>
      <dc:creator>swyx</dc:creator>
      <pubDate>Sat, 16 Oct 2021 21:05:53 +0000</pubDate>
      <link>https://dev.to/swyx/100-bytes-of-css-to-look-great-everywhere-19pd</link>
      <guid>https://dev.to/swyx/100-bytes-of-css-to-look-great-everywhere-19pd</guid>
      <description>&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;html&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;max-width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;70ch&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3em&lt;/span&gt; &lt;span class="m"&gt;1em&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;auto&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;line-height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1.75&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1.25em&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 previously tweeted out an old version of this and accidentally got a line by line code review from 100k people:&lt;/p&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-1449472712720601088-849" src="https://platform.twitter.com/embed/Tweet.html?id=1449472712720601088"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1449472712720601088-849');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1449472712720601088&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;p&gt;This article is the most up to date version with everyone's feedback.&lt;/p&gt;

&lt;h2&gt;
  
  
  Context
&lt;/h2&gt;

&lt;p&gt;Dan Luu always writes fascinating posts, but &lt;a href="https://danluu.com/" rel="noopener noreferrer"&gt;the design&lt;/a&gt; makes it very painful to read:&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%2Fpbs.twimg.com%2Fmedia%2FFB2PmLXVEAYu7GN%3Fformat%3Djpg%26name%3Dlarge" 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%2Fpbs.twimg.com%2Fmedia%2FFB2PmLXVEAYu7GN%3Fformat%3Djpg%26name%3Dlarge" alt="https://pbs.twimg.com/media/FB2PmLXVEAYu7GN?format=jpg&amp;amp;name=large"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A couple years ago, &lt;a href="https://news.ycombinator.com/item?id=19607169" rel="noopener noreferrer"&gt;this post on HN&lt;/a&gt; was fairly popular, and I saved it on my &lt;a href="https://github.com/sw-yx/spark-joy/blob/master/README.md#drop-in-css-frameworks" rel="noopener noreferrer"&gt;spark-joy repo&lt;/a&gt;, which is a &lt;a href="https://twitter.com/swyx/status/1434206569881694210" rel="noopener noreferrer"&gt;swipe file&lt;/a&gt; of design tips I've collected over the past few years. &lt;/p&gt;

&lt;p&gt;However, I noticed that the original website link is dead. So I thought I would refresh it with what I have now implemented for Dan's site:&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%2Fpbs.twimg.com%2Fmedia%2FFB2Po1tVIAA-i3O%3Fformat%3Djpg%26name%3Dlarge" 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%2Fpbs.twimg.com%2Fmedia%2FFB2Po1tVIAA-i3O%3Fformat%3Djpg%26name%3Dlarge" alt="https://pbs.twimg.com/media/FB2Po1tVIAA-i3O?format=jpg&amp;amp;name=large"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  100 bytes of css to look great nearly everywhere
&lt;/h1&gt;

&lt;p&gt;This should be simple drop-in css to look good on most displays:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;html&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;max-width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;70ch&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3em&lt;/span&gt; &lt;span class="m"&gt;1em&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;auto&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;line-height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1.75&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1.25em&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;Let's break this down. I've adapted the original text with my own commentary.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;max-width: 70ch&lt;/code&gt;: the "readable range" is usually 60-80 character widths, and CSS lets you express that directly with the &lt;code&gt;ch&lt;/code&gt; unit. &lt;a href="https://twitter.com/swyx/status/1223025553986347009" rel="noopener noreferrer"&gt;I blogged more on line lengths last year&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;padding: 3em 1em&lt;/code&gt;: If the display's width goes under the &lt;code&gt;max-width&lt;/code&gt; set above, then this padding prevents edge-to-edge text on mobile. We use &lt;code&gt;3em&lt;/code&gt; to provide top/bottom whitespace.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;margin: auto&lt;/code&gt;: This is really all that is needed to center the page - applied on &lt;code&gt;html&lt;/code&gt;, because Dan's site doesnt have a semantic &lt;code&gt;&amp;lt;main&amp;gt;&lt;/code&gt; tag and &lt;code&gt;&amp;lt;html&amp;gt;&lt;/code&gt; is more likely to exist in most sites (no judgment pls, i've heard enough semantic HTML preaching). That the top tag centers itself relative to nothing is unintuitive, but thats how browsers do.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;line-height: 1.75&lt;/code&gt;: Spacing between the lines to help increase visual clarity. Always leave line height unitless because &lt;a href="https://twitter.com/JayGilmore/status/1449731397585879040" rel="noopener noreferrer"&gt;reasons&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;font-size: 1.5em&lt;/code&gt;: I've noticed that recent design trends and screen sizes have tended toward bigger font sizes. Or maybe I'm getting old. Prefer &lt;code&gt;em&lt;/code&gt; or &lt;code&gt;rem&lt;/code&gt; over &lt;code&gt;px&lt;/code&gt; if you want to let users scale it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://dev.to/tusharsadhwani/comment/1j1a0"&gt;Tushar points out&lt;/a&gt; that you can use &lt;code&gt;:root&lt;/code&gt; instead of &lt;code&gt;&amp;lt;html&amp;gt;&lt;/code&gt; to guarantee that there is some selector present, but its a touch too fancy for me and uses an extra character :)&lt;/p&gt;

&lt;h2&gt;
  
  
  Optional 100 more bytes
&lt;/h2&gt;

&lt;p&gt;If you can spare a few extra bytes of CSS, I'd also recommend margins for headers, paragraphs, and lists, and softening body text:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="nt"&gt;h2&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="nt"&gt;h3&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="nt"&gt;h4&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="nt"&gt;h5&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="nt"&gt;h6&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3em&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;1em&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="nt"&gt;ul&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="nt"&gt;ol&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;margin-bottom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2em&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#1d1d1d&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;font-family&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;sans-serif&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;&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%2F7eyghddgt0v5hu0i6f9n.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%2F7eyghddgt0v5hu0i6f9n.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Applying Styles on Pages You Don't Control
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;You can auto apply these styles on to pages with &lt;a href="https://github.com/ankit/stylebot" rel="noopener noreferrer"&gt;https://github.com/ankit/stylebot&lt;/a&gt;. I can't vouch for their security as I only just started using it.&lt;/li&gt;
&lt;li&gt;WaterCSS is a bookmarklet or extension or script with some nice presets: &lt;a href="https://watercss.kognise.dev/" rel="noopener noreferrer"&gt;https://watercss.kognise.dev/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;SakuraCSS is a bookmarklet that does the same &lt;a href="https://oxal.org/projects/sakura/bookmark/" rel="noopener noreferrer"&gt;https://oxal.org/projects/sakura/bookmark/&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>css</category>
    </item>
    <item>
      <title>Eating the Cloud from Outside In</title>
      <dc:creator>swyx</dc:creator>
      <pubDate>Mon, 04 Oct 2021 02:23:15 +0000</pubDate>
      <link>https://dev.to/swyx/eating-the-cloud-from-outside-in-1n2d</link>
      <guid>https://dev.to/swyx/eating-the-cloud-from-outside-in-1n2d</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;See discussions on &lt;a href="https://news.ycombinator.com/item?id=28903982" rel="noopener noreferrer"&gt;Hacker News&lt;/a&gt; and &lt;a href="https://www.infoq.com/news/2021/10/cloudflare-r2-egress-aws/" rel="noopener noreferrer"&gt;InfoQ&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Cloudflare launched on September 27, 2010, and every year since, it has made it a point to celebrate "&lt;a href="https://blog.cloudflare.com/tag/birthday-week/" rel="noopener noreferrer"&gt;Birthday Week&lt;/a&gt;" with a raft of launches. By far, the show-stopper this year was the announcement of &lt;a href="https://blog.cloudflare.com/introducing-r2-object-storage/" rel="noopener noreferrer"&gt;R2 Storage&lt;/a&gt;, an S3-compatible Object Storage service that directly takes aim at &lt;a href="https://blog.cloudflare.com/aws-egregious-egress/" rel="noopener noreferrer"&gt;AWS' "Hotel California" business model&lt;/a&gt;. This has been extremely well received, going by the response on &lt;a href="https://news.ycombinator.com/item?id=28682237" rel="noopener noreferrer"&gt;HN&lt;/a&gt; and &lt;a href="https://twitter.com/QuinnyPig/status/1443028078196711426" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;. In its past 5 birthdays, Cloudflare has gone from world-class CDN to offering:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;2017: &lt;a href="https://workers.cloudflare.com/" rel="noopener noreferrer"&gt;serverless compute&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;2018: &lt;a href="https://developers.cloudflare.com/workers/learning/how-kv-works" rel="noopener noreferrer"&gt;eventually consistent datastore&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;2019: &lt;a href="https://blog.cloudflare.com/extending-the-workers-platform/" rel="noopener noreferrer"&gt;website hosting&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;2020: &lt;a href="https://blog.cloudflare.com/introducing-workers-durable-objects" rel="noopener noreferrer"&gt;strongly consistent datastore&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;2021: object storage&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is an &lt;a href="https://blog.cloudflare.com/the-secret-to-cloudflare-pace-of-innovation/" rel="noopener noreferrer"&gt;impressive pace&lt;/a&gt; and they are now declaring that they will be "&lt;a href="https://www.protocol.com/enterprise/cloudflare-r2-storage-aws" rel="noopener noreferrer"&gt;the fourth major public cloud&lt;/a&gt;". When your market cap is $36 billion and your next biggest competitor is worth $1.6 &lt;em&gt;trillion&lt;/em&gt; (~45x larger, albeit not pure-play), this is a bold statement. Many startups are trying by offering specialized &lt;a href="https://www.swyx.io/cloud-distros/" rel="noopener noreferrer"&gt;Cloud Distros&lt;/a&gt;, but all building with AWS as the presumptive winner of the "first layer cloud" rather than trying to compete.&lt;/p&gt;

&lt;p&gt;What's Cloudflare's strategy here?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;My realization: The big 3 clouds are playing Chess, but &lt;em&gt;Cloudflare is playing Go&lt;/em&gt;.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdojvxo0i0u5v7cmm1d4p.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%2Fdojvxo0i0u5v7cmm1d4p.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Two Kinds of Disruption
&lt;/h2&gt;

&lt;p&gt;The canonical thoughtleader of Disruption theory is the legendary HBS Professor Clayton Christensen, and a lot has been made of &lt;a href="https://stratechery.com/2021/cloudflares-disruption/" rel="noopener noreferrer"&gt;Cloudflare's Disruption&lt;/a&gt; of AWS; in fact, Cloudflare cofounders Matthew Prince and Michelle Zatlyn were &lt;a href="https://harbus.org/2011/cloudflare/" rel="noopener noreferrer"&gt;students of him&lt;/a&gt; at HBS. James Allworth, their Head of Innovation, studied and &lt;a href="https://claytonchristensen.com/books/how-will-you-measure-your-life/" rel="noopener noreferrer"&gt;co-authored a book&lt;/a&gt; with him. Prince &lt;a href="https://youtu.be/XeKWeBw1R5A?t=646" rel="noopener noreferrer"&gt;namechecked the Innovator's Dilemma at Cloudflare's launch&lt;/a&gt; in 2010, and in 2021 is still proudly showing off a &lt;a href="https://news.ycombinator.com/item?id=28708636" rel="noopener noreferrer"&gt;handwritten note from him&lt;/a&gt;; in short, you can be sure his lessons are never far from Cloudflare's minds.&lt;/p&gt;

&lt;p&gt;As Ben Thompson noted in &lt;a href="https://stratechery.com/2013/clayton-christensen-got-wrong/" rel="noopener noreferrer"&gt;his now-famous takedown of Christensen on the iPhone&lt;/a&gt;, people often miss that he had &lt;em&gt;two&lt;/em&gt; theories of disruption:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;New Market Disruption&lt;/strong&gt;: When incumbents ignore new technologies until it is too late.

&lt;ul&gt;
&lt;li&gt;Allworth recently wrote an instant-classic application of this on &lt;a href="https://jamesallworth.medium.com/intels-disruption-is-now-complete-d4fa771f0f2c" rel="noopener noreferrer"&gt;Intel (x86/CISC) vs Apple Silicon (ARM/RISC)&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Low-End Disruption&lt;/strong&gt;: When vertically integrated premium incumbents get disrupted by "cheap and good enough" modular providers.&lt;/li&gt;

&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;A third model of disruption comes from Kevin Kwok's &lt;a href="https://kwokchain.com/2021/02/05/atomic-concepts/" rel="noopener noreferrer"&gt;Atomic Concepts&lt;/a&gt;, but that is a closer fit for the &lt;a href="https://www.swyx.io/cloud-distros/" rel="noopener noreferrer"&gt;Cloud Distros&lt;/a&gt; thesis than Cloudflare.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;On the first theory: Cloudflare has some excellent technologists — &lt;a href="https://en.wikipedia.org/wiki/John_Graham-Cumming" rel="noopener noreferrer"&gt;John Graham-Cumming&lt;/a&gt;,  &lt;a href="https://www.linkedin.com/in/kenton-varda-5b96a2a4/" rel="noopener noreferrer"&gt;Kenton Varda&lt;/a&gt; and &lt;a href="https://ritakozlov.com/about/" rel="noopener noreferrer"&gt;Rita Kozlov&lt;/a&gt; come to mind — and it is doing some cool things with V8 isolates and dynamic routing, but it doesn't (in my mind) have a clear claim on the overall new technology angle, since AWS created the modern serverless paradigm, &lt;a href="https://www.amazon.science/blog/how-awss-firecracker-virtual-machines-work" rel="noopener noreferrer"&gt;open-sourced Firecracker&lt;/a&gt;, and &lt;a href="https://www.protocol.com/newsletters/protocol-enterprise/serverless-container-aws" rel="noopener noreferrer"&gt;is using Lambda for half of all new applications&lt;/a&gt; (though Cloudflare is also &lt;a href="https://blog.cloudflare.com/the-secret-to-cloudflare-pace-of-innovation/" rel="noopener noreferrer"&gt;aggressively dogfooding Workers&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Most of the disruption discussion focuses on the second model of disruption, and it rings true. Cloudflare took a part of the cloud nobody valued, gave away an insanely good free offering, and quietly accumulated an &lt;a href="https://w3techs.com/technologies/cross/proxy/content_delivery" rel="noopener noreferrer"&gt;80% market share&lt;/a&gt;.  Meanwhile, when people think of "Tier 1" AWS services, its Cloudflare equivalent, Amazon CloudFront, rarely gets any love, and the official AWS Twitter account &lt;a href="https://twitter.com/search?q=from%3Aawscloud%20cloudfront&amp;amp;src=typed_query&amp;amp;f=live" rel="noopener noreferrer"&gt;hasn't tweeted about it in almost a year&lt;/a&gt;. Cloudflare leveraged their foothold into selling premium security services, and now is expanding into other value added pieces by leaning into a fundamentally different (high fixed cost, near zero marginal cost) business model the larger incumbents structurally cannot follow.&lt;/p&gt;

&lt;p&gt;This, at least, is how Ben Thompson framed it in &lt;a href="https://stratechery.com/2021/cloudflares-disruption/" rel="noopener noreferrer"&gt;his writeup on Cloudflare's Disruption&lt;/a&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://stratechery.com/2021/cloudflares-disruption/" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7258tf9irknumz5db2vg.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But this diagram is a little too neat. It imagines the two clouds as worlds apart. Yet R2 is explicitly designed as S3-compatible; in Prince's words, you can &lt;a href="https://news.ycombinator.com/item?id=28703464" rel="noopener noreferrer"&gt;set it to "slurp" mode&lt;/a&gt; and you magically have a S3 interface with egress that is &lt;a href="https://twitter.com/QuinnyPig/status/1443076111651401731?s=20" rel="noopener noreferrer"&gt;six orders of magnitude cheaper&lt;/a&gt;. Similarly, the original Cloudflare service could always be used together with EC2, and Cloudflare Workers have different enough usecases and limitations from AWS Lambda and Lambda@Edge that you could conceivably have a stack using all of them. &lt;/p&gt;

&lt;p&gt;This isn't Apple vs Android; premium and vertically integrated vs cheap and modular; incompatible ecosystems, and never the twain shall meet. &lt;/p&gt;

&lt;p&gt;This is something else.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cloudflare and the Rules of Go
&lt;/h2&gt;

&lt;p&gt;In the classic game of Go, you &lt;a href="https://www.pandanet.co.jp/English/learning_go/learning_go_6.html" rel="noopener noreferrer"&gt;capture pieces&lt;/a&gt; by surrounding your opponents, instead of directly replacing their spot.&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%2F7idppjskwr10z817xo42.gif" 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%2F7idppjskwr10z817xo42.gif" alt="Basic Go Capture gif"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Storage Capture
&lt;/h3&gt;

&lt;p&gt;You could view Cloudflare's Bandwidth Alliance and R2 as an "encircling" move around AWS' previously secure market position with S3. By promising to be API compatible (including offering S3's eleven-nines durability guarantee and free infrequent access), Cloudflare has cut off nearly all of AWS' remaining "liberties", putting it in "&lt;a href="https://en.wikipedia.org/wiki/List_of_Go_terms#Atari" rel="noopener noreferrer"&gt;atari&lt;/a&gt;". If Cloudflare's offering matures enough to be seen as a strict superset, it places the final stone, capturing the "cloud storage" territory. &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%2Fo4kk6ucmg8mbqd0t0yzw.gif" 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%2Fo4kk6ucmg8mbqd0t0yzw.gif" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Product Strategy
&lt;/h3&gt;

&lt;p&gt;In Chess, pieces have different values and capabilities. Bishops are worth 3 points and move diagonally, Rooks are worth 5 and move in straight lines, and so on. Pieces are best deployed in a sequence chain where higher value pieces support lower value ones.&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%2Fbuwls1amzbahfv0q30d6.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%2Fbuwls1amzbahfv0q30d6.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In Go, each piece is indistinguishable from the other; it is the &lt;em&gt;network position&lt;/em&gt; that counts, not any individual piece. Support doesn't matter so much as adjacent territory claimed; in the picture below, the four white pieces on the left do far less than the four black pieces on the right.&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%2Fpwwiby0brxdq3d7mzt6r.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%2Fpwwiby0brxdq3d7mzt6r.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Compare this to public statements about how Cloudflare works. &lt;a href="https://news.ycombinator.com/item?id=28703194" rel="noopener noreferrer"&gt;From Prince&lt;/a&gt;: &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Since every server in our network runs every service&lt;/strong&gt;, once we’re in for one thing means everything we do in the region gets better and less expensive to operate. This means, counter intuitively, as we add more locations to our network our costs generally go down, not up.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So while AWS has &lt;a href="https://www.lastweekinaws.com/blog/the-17-ways-to-run-containers-on-aws/" rel="noopener noreferrer"&gt;17 ways to run containers&lt;/a&gt; and &lt;a href="https://serverlessfirst.com/aws-async-message-services/" rel="noopener noreferrer"&gt;7 ways to do async message processing&lt;/a&gt;, all overlapping and reinforcing and supporting each other, Cloudflare will tend toward introducing singular primitives, &lt;a href="https://blog.cloudflare.com/cloudflare-for-offices/#built-for-purpose" rel="noopener noreferrer"&gt;stuff them in a box&lt;/a&gt;, and try to ship those boxes to &lt;a href="https://blog.cloudflare.com/cloudflare-for-offices/" rel="noopener noreferrer"&gt;as many places as will possibly take them&lt;/a&gt;. If they could install Cloudflare on your mobile phone, they would (this gets them dangerously close to being a &lt;a href="https://www.youtube.com/watch?v=5JM8bkJLLjM" rel="noopener noreferrer"&gt;real life Pied Piper&lt;/a&gt;).&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Update: I have been informed that you can in fact &lt;a href="https://blog.cloudflare.com/1111-warp-better-vpn/" rel="noopener noreferrer"&gt;install Cloudflare on your mobile phone&lt;/a&gt;...&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Territory Wins
&lt;/h3&gt;

&lt;p&gt;In Chess, you only win when you checkmate the King, which in effect has infinite point value, winner-take-all. &lt;/p&gt;

&lt;p&gt;In Go, you win by amount of territory claimed, and it is near impossible for one side to end up with zero territory. Perhaps this is more true to real life.&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%2Fq8ne0yo5vzoytu04qdkv.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%2Fq8ne0yo5vzoytu04qdkv.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;While AWS boasts an impressive &lt;a href="https://aws.amazon.com/about-aws/global-infrastructure/" rel="noopener noreferrer"&gt;230+ points of presence&lt;/a&gt;, Cloudflare has &lt;a href="https://www.cloudflare.com/network/" rel="noopener noreferrer"&gt;interconnects with 10,000 networks&lt;/a&gt; including "every major ISP, cloud provider, and enterprise". These aren't the same thing, but it reflects the substantially different game that Cloudflare is playing. From the point of view of users, Cloudflare can be much easier to use and is much more of a painkiller than other big cloud services in their stack, despite perhaps having a single digit % of mindshare and wallet share. This puts Cloudflare within a stone's throw of Ben Thompson's other big claim to fame in Tech Strategy, &lt;a href="https://stratechery.com/aggregation-theory/" rel="noopener noreferrer"&gt;Aggregation Theory&lt;/a&gt; (the Intro to Tech Strategy chapter in &lt;a href="https://www.learninpublic.org/" rel="noopener noreferrer"&gt;my book&lt;/a&gt; is free if you want my take on it).&lt;/p&gt;

&lt;p&gt;Strategically, "Territory over Positioning" happens to be exactly the right call. In a zero-sum market that isn't growing, you want to jockey for position and take out enemies. In a positive-sum, infinitely expanding market like Cloud, you want to encircle them.&lt;/p&gt;

&lt;h3&gt;
  
  
  Watch for Sente
&lt;/h3&gt;

&lt;p&gt;To &lt;a href="https://en.wikipedia.org/wiki/List_of_Go_terms#Gote,_sente_and_tenuki" rel="noopener noreferrer"&gt;quote Wikipedia&lt;/a&gt;, a move that overwhelmingly compels a player into a particular follow-up move is said to have "sente" (先手), or "initiative". In most games, the player who maintains "sente" most of the time will win.&lt;/p&gt;

&lt;p&gt;There is a lot of speculation that AWS will have to respond somehow to Cloudflare's provocations:&lt;/p&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-1444016487304364036-383" src="https://platform.twitter.com/embed/Tweet.html?id=1444016487304364036"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1444016487304364036-383');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1444016487304364036&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;p&gt;But beyond a &lt;a href="https://www.infoworld.com/article/3634406/cloudflare-hopes-lack-of-outbound-data-fees-will-convert-aws-s3-users-to-its-r2-storage-service.html" rel="noopener noreferrer"&gt;standard PR response&lt;/a&gt;, I doubt AWS will respond to mere noise - S3 data egress revenues have to take a significant downturn before AWS will be compelled to act. But when it does, every future move of Cloudflare's will be taken increasingly seriously. Cloudflare acts and talks like it has "sente" now, but it isn't real until AWS (or the other big clouds for that matter) feel forced to specifically respond.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion: Establish, Envelop, Expand
&lt;/h2&gt;

&lt;p&gt;While the tech industry is used to come-from-below disruption, and the software industry is increasingly grasping class-for-the-masses atomic concepts, I believe Cloudflare is writing a new playbook that is the little-guy counterpart of the &lt;a href="https://en.wikipedia.org/wiki/Embrace,_extend,_and_extinguish" rel="noopener noreferrer"&gt;embrace, extend, extinguish&lt;/a&gt; model used by Microsoft.&lt;/p&gt;

&lt;p&gt;Because it involves API compatibility, this playbook is particularly relevant to developer tools, and is protected by &lt;a href="https://twitter.com/swyx/status/1379091545102503937" rel="noopener noreferrer"&gt;the Supreme Court ruling in Google v Oracle&lt;/a&gt;. If I were to summarize it in three words, looking over Cloudflare's history and &lt;a href="https://d18rn0p25nwr6d.cloudfront.net/CIK-0001477333/fa0c28c8-a883-45cd-aba8-0b9c3249cc14.pdf" rel="noopener noreferrer"&gt;annual report&lt;/a&gt;, I might call it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Establish&lt;/strong&gt;: Establish a foothold in something incumbents don't care enough about&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Envelop&lt;/strong&gt;: Reverse-proxy something that incumbents don't serve customers well on&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Expand&lt;/strong&gt;: cross-sell other premium products and services until they are more customers of you than they are customers of the incumbent.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Given Cloudflare's fundamentally less-centralized approach to growing its cloud, it is no surprise that it &lt;a href="https://blog.cloudflare.com/announcing-web3-gateways/" rel="noopener noreferrer"&gt;announced its first Ethereum product&lt;/a&gt; this Birthday Week; although it remains to be seen if a Web2-native company can really drop enough of its assumptions to handle Web3 threats or opportunities. If we are truly in the "early Internet" days of Web3, only the paranoid might survive here. Fortunately, Prince seems to be a &lt;a href="https://twitter.com/search?q=from%3Aeastdakota%20grove&amp;amp;src=typed_query&amp;amp;f=top" rel="noopener noreferrer"&gt;vocal fan of Andy Grove&lt;/a&gt; as well.&lt;/p&gt;

</description>
      <category>cloud</category>
      <category>aws</category>
      <category>strategy</category>
    </item>
    <item>
      <title>Why do Webdevs keep trying to kill REST? </title>
      <dc:creator>swyx</dc:creator>
      <pubDate>Mon, 20 Sep 2021 14:46:38 +0000</pubDate>
      <link>https://dev.to/swyx/why-do-webdevs-keep-trying-to-kill-rest-j2j</link>
      <guid>https://dev.to/swyx/why-do-webdevs-keep-trying-to-kill-rest-j2j</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Translations: &lt;a href="https://t.me/super_oleg_dev/27" rel="noopener noreferrer"&gt;Russian&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&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%2Fuqbmd7yo8iwhmttuq0np.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%2Fuqbmd7yo8iwhmttuq0np.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;a href="https://excalidraw.com/#json=6042109563895808,4Powv_4gGx0kX9ZZmg6l7w" rel="noopener noreferrer"&gt;Edit this chart&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Watching recent trends in client-server paradigms, from Apollo GraphQL to React Server Components to Rails Hotwire, I've had a revelation that helped me make sense of it all: They're all abstractions over REST!&lt;/p&gt;

&lt;p&gt;There are two schools of thought:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Smart Client&lt;/strong&gt;: State updates are rendered clientside first, then sent back to the server.

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;You can roll your own&lt;/strong&gt;: Use a state management solution like &lt;strong&gt;Redux&lt;/strong&gt; or &lt;strong&gt;Svelte Stores&lt;/strong&gt; and handwrite every piece of the client-server coordination logic.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You can use libraries that combine state and data fetching&lt;/strong&gt;: &lt;strong&gt;Apollo Client&lt;/strong&gt;, &lt;strong&gt;React Query&lt;/strong&gt;, &lt;a href="https://github.com/pubkey/rxdb" rel="noopener noreferrer"&gt;RxDB&lt;/a&gt;, &lt;a href="https://gun.eco/" rel="noopener noreferrer"&gt;GunDB&lt;/a&gt;, &lt;a href="https://nozbe.github.io/WatermelonDB/" rel="noopener noreferrer"&gt;WatermelonDB&lt;/a&gt; and &lt;a href="https://jlongster.com/future-sql-web" rel="noopener noreferrer"&gt;Absurd-SQL&lt;/a&gt; all do dual jobs of fetching data and storing related state. (you can see &lt;a href="https://github.com/pubkey/client-side-databases" rel="noopener noreferrer"&gt;parallel implementations here&lt;/a&gt; if evaluating)&lt;/li&gt;
&lt;li&gt;You can use frameworks that abstract it away for you: &lt;strong&gt;Blitz.js&lt;/strong&gt; and &lt;strong&gt;Next.js&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Or you can take it off the shelf&lt;/strong&gt;: &lt;strong&gt;Google's Firebase&lt;/strong&gt; and &lt;strong&gt;AWS' Amplify/AppSync&lt;/strong&gt; are fully vendor provided and vertically integrated with backend resources like auth, database, and storage (arguably &lt;a href="https://www.mongodb.com/realm" rel="noopener noreferrer"&gt;MongoDB Realm&lt;/a&gt; and Meteor's &lt;a href="https://www.npmjs.com/package/minimongo" rel="noopener noreferrer"&gt;minimongo&lt;/a&gt; before it)&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Smart Server&lt;/strong&gt;: State updates are sent to the server first, which then sends rerenders to the client (whether in HTML chunks, serialized React components, or XML).

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://fly.io/blog/how-we-got-to-liveview/" rel="noopener noreferrer"&gt;Phoenix Liveview&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hotwired/hotwire-rails" rel="noopener noreferrer"&gt;Rails Hotwire&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.swyx.io/react-server-components-demo/" rel="noopener noreferrer"&gt;React Server Components&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.microsoft.com/en-us/previous-versions/bb386448(v=vs.140)?redirectedfrom=MSDN" rel="noopener noreferrer"&gt;ASP.NET Web Forms&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;Of course the "Smart Server" paradigm isn't wholly new. It has a historical predecessor — let's call it the "Traditional Server" paradigm. The Wordpress, Django, Laravel type frameworks would fill out HTML templates and the browser's only job is to render them and send the next requests. We gradually left that behind for more persistent interactive experiences with client-side JS (nee AJAX). For a long time we were happy with just pinging REST endpoints from the client, ensuring a clean separation of concerns between frontend and backend. &lt;/p&gt;

&lt;p&gt;So why are we tearing up the old client-server paradigm? And which side will win?&lt;/p&gt;

&lt;h2&gt;
  
  
  It's about User Experience
&lt;/h2&gt;

&lt;p&gt;Ironically, the two sides have very different goals in UX and would probably argue that the other is less performant.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Smart clients enable offline-first apps and optimistic updates so your app can keep working without internet and &lt;em&gt;feels&lt;/em&gt; instant because you are doing CRUD against a local cache of remote data (I wrote about this in &lt;a href="https://www.swyx.io/svelte-amplify-datastore" rel="noopener noreferrer"&gt;Optimistic, Offline-First Apps&lt;/a&gt; and RxDB has a &lt;a href="https://rxdb.info/offline-first.html" rel="noopener noreferrer"&gt;good writeup here&lt;/a&gt;).

&lt;ul&gt;
&lt;li&gt;This improves &lt;strong&gt;perceived performance for apps&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;However their downside is tend to come with large JS bundles upfront: Firebase adds as much as 1mb to your bundle, Amplify got it down to 230kb after a lot of modularization effort, Realm stands at 42kb.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Smart servers directly cut JS weight by doing work serverside rather than clientside, yet seamlessly patching in updates &lt;em&gt;as though they were done clientside&lt;/em&gt;. Facebook has reported as high as &lt;a href="https://twitter.com/swyx/status/1341151070743982080" rel="noopener noreferrer"&gt;29% bundle reduction&lt;/a&gt;.

&lt;ul&gt;
&lt;li&gt;This improves &lt;strong&gt;first-load performance for sites&lt;/strong&gt; and reduces total JavaScript sent throughout the session.&lt;/li&gt;
&lt;li&gt;However their downside is that every single user of yours is doing their rendering on &lt;em&gt;your&lt;/em&gt; server, not their browser. &lt;strong&gt;This is bound to be more resource intensive and inserts a full network roundtrip for every user interaction&lt;/strong&gt;. The problem is mitigated if you can autoscale compute AND storage at the edge (eg with serverless rendering on &lt;a href="https://twitter.com/threepointone/status/1376248810003955716" rel="noopener noreferrer"&gt;Cloudflare Workers&lt;/a&gt; or &lt;a href="https://twitter.com/swyx/status/1341951585396506624" rel="noopener noreferrer"&gt;AWS Lambda&lt;/a&gt;). There are also real security concerns that should get ironed out over time.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;The "winner" here, if there is such, will depend on usecase - if you are writing a web app where any delay in response will be felt by users, then you want the smart client approach, but if you are writing an ecommerce site, then your need for speed will favor smart servers.&lt;/p&gt;

&lt;h2&gt;
  
  
  It's about Developer Experience
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Platform SDKs&lt;/strong&gt;. For the Frontend-Platform-as-a-Service vendors like Firebase and AWS Amplify, their clients are transparently just platform SDKs — since they have total knowledge of your backend, they can offer you a better DX on the frontend with idiomatic language SDKs.&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Reducing Boilerplate&lt;/strong&gt;. Instead of a 2 stage process of writing a backend handler/resolver and then the corresponding frontend API call/optimistic update, you can write the backend once and codegen a custom client, or offer what feels like direct database manipulation on the frontend (with authorization and syncing rules).&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The Smart Server boilerplate reduction is &lt;em&gt;extreme&lt;/em&gt;, since the syncing protocol eliminates all need to coordinate client-server interactions. &lt;a href="https://news.ycombinator.com/item?id=28621830" rel="noopener noreferrer"&gt;Quote from a LiveView user&lt;/a&gt;: &lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;"LiveView is absurd. It's the biggest change I've experienced in web development since Rails v1. I've been able to build rich, interactive, games without a single line of Javascript. It takes complicated server / api / front-end build projects and results in literally 1/10th the amount of code for the same result."&lt;/p&gt;
&lt;/blockquote&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;Offline.&lt;/strong&gt; Both Firebase Firestore and Amplify AppSync also support offline persistence. Since they know your database schema, it's easy to offer a local replica and conflict resolution. There are vendor agnostic alternatives like &lt;a href="https://github.com/pubkey/rxdb" rel="noopener noreferrer"&gt;RxDB&lt;/a&gt; or &lt;a href="https://github.com/redux-offline/redux-offline" rel="noopener noreferrer"&gt;Redux Offline&lt;/a&gt; that take more glue work.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Being Offline-first requires you to have a local replica of your data, which means that doing CRUD against your local replica can be much simpler (see below).&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;Reducing Boilerplate for Optimistic Updates&lt;/strong&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;When you do normal optimistic updates, you have to do 4 things:

&lt;ol&gt;
&lt;li&gt;send update to server, &lt;/li&gt;
&lt;li&gt;optimistically update local state, &lt;/li&gt;
&lt;li&gt;complete the optimistic update on server success,&lt;/li&gt;
&lt;li&gt;undo the optimistic update on server fail&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;With a local database replica, you do 1 thing: write your update to the local DB and wait for it to sync up. The local DB should expose the status of the update (which you can reflect in UI) as well as let you centrally handle failures.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;People&lt;/strong&gt;. This is an organizational, rather than a technological, argument. How many times have your frontend developers been "blocked by backend" on something and now have to wait 2-3 sprints for someone else to deliver something they need? It is hugely disruptive to workflow. Give the developer full stack access to whatever they need to ship features, whether it is serverless functions, database access or something else. Smart Clients/Servers can solve &lt;em&gt;people&lt;/em&gt; problems as much as UX problems.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;This is why I am a big champion of shifting the industry divide from "frontend vs backend" to "&lt;a href="https://twitter.com/swyx/status/1097334440169107456?s=20" rel="noopener noreferrer"&gt;product vs platform&lt;/a&gt;". Chris Coyier's term for this is &lt;a href="https://css-tricks.com/the-all-powerful-front-end-developer/" rel="noopener noreferrer"&gt;The All-Powerful Frontend Developer&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;GraphQL is also secretly a "people technology" because it decouples frontend data requirements from a finite set of backend endpoints.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;Both smart clients and smart servers greatly improve the DX on all these fronts.&lt;/p&gt;

&lt;h2&gt;
  
  
  It's about Protocols
&lt;/h2&gt;

&lt;p&gt;Better protocols lead to improved UX (eliminating user-facing errors and offering faster updates) and DX (shifting errors left) and they're so relevant to the "why are you avoiding REST" debate that I split them out to their own category. Technically of course, whatever protocol you use may be a layer atop of REST - if you have a separate layer (like &lt;a href="https://github.com/jlongster/crdt-example-app" rel="noopener noreferrer"&gt;CRDTs&lt;/a&gt;) that handles syncing/conflict resolution, then that is the protocol you are really using.&lt;/p&gt;

&lt;p&gt;A lot of these comments will feature GraphQL, because it is the non-REST protocol I have the most familiarity with; but please feel free to tell me where other protocols may fit in or differ.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Type Safety&lt;/strong&gt;: GraphQL validates every request at runtime. &lt;a href="https://trpc.io/docs/rpc" rel="noopener noreferrer"&gt;trpc&lt;/a&gt; does it at compile time.

&lt;ul&gt;
&lt;li&gt;Increased type annotation offers better codegen of client SDKs that you would otherwise have to hand-write. This is a much more established norm in gRPC than GraphQL and I'm not sure why.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Bandwidth&lt;/strong&gt;: Sending less data (or data in a format that improves UX) over the wire

&lt;ul&gt;
&lt;li&gt;GraphQL helps solve the &lt;a href="https://stackoverflow.com/questions/44564905/what-is-over-fetching-or-under-fetching" rel="noopener noreferrer"&gt;overfetching&lt;/a&gt; problem. In practice, I think the importance of this is overhyped unless you are Facebook or Airbnb. However the usefulness of &lt;a href="https://www.swyx.io/why-graphql-variables/" rel="noopener noreferrer"&gt;persisted queries&lt;/a&gt; for solving &lt;em&gt;upload&lt;/em&gt; bandwidth problems is underrated.&lt;/li&gt;
&lt;li&gt;Hotwire sends &lt;a href="https://hotwired.dev/" rel="noopener noreferrer"&gt;literal HTML Over The wire&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;React Server Components sends &lt;a href="https://www.youtube.com/watch?v=VA3TFymZlW4" rel="noopener noreferrer"&gt;serialized component data&lt;/a&gt; over the wire; more compact because it can assume React, and smoothly coordinated with on-screen loading states&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Real-time&lt;/strong&gt;: offering "live" and "collaborative" experiences on the web

&lt;ul&gt;
&lt;li&gt;This is doable with periodic polling and long-polling, but more native protocols like UDP, WebRTC and WebSockets are probably a better solution&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://replicache.dev/" rel="noopener noreferrer"&gt;Replicache&lt;/a&gt; (used for &lt;a href="https://nextjs.org/live" rel="noopener noreferrer"&gt;Next.js Live&lt;/a&gt;) and &lt;a href="https://www.croquet.io/" rel="noopener noreferrer"&gt;Croquet&lt;/a&gt; look interesting here&lt;/li&gt;
&lt;li&gt;UDP itself seems like a foundation that is ripe for much more protocol innovation; even &lt;a href="https://www.stackscale.com/blog/http3/" rel="noopener noreferrer"&gt;HTTP/3&lt;/a&gt; will be built atop it&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;There remain some areas for growth that I don't think are adequately answered yet:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Performance&lt;/strong&gt;: One nightmare of every backend developer is unwittingly letting a given user kick off an expensive query that could choke up system resources. Complexity budgets are not a solved problem in GraphQL. It's a touchy subject, but new protocols can at least open up a more interesting dance between performance and flexibility.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security&lt;/strong&gt;: allowing frontend developers direct database access requires much more guard rails around security. Vendors with integrated auth solutions can help somewhat, but the evangelists for a new protocol need to be as loud about their security requirements as they are the developer experience upsides.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Not Everyone is Anti-REST
&lt;/h2&gt;

&lt;p&gt;Yes of course my title is a little clickbaity; REST is perfectly fine for the vast majority of webdevs. There are even people pushing boundaries within the REST paradigm. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://remix.run/" rel="noopener noreferrer"&gt;Remix&lt;/a&gt;, the soon-to-be-launched React metaframework from the creators of React Router, embraces native browser standards so you get progressive enhancement "for free", for example requiring that you &lt;a href="https://remix.run/features" rel="noopener noreferrer"&gt;POST from a HTML form&lt;/a&gt; (they have clarified that &lt;a href="https://twitter.com/remix_run/status/1439976471796203520?s=20" rel="noopener noreferrer"&gt;anything but GET is fine&lt;/a&gt;, and they are pro-HTTP, and neutral REST)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://supabase.io/" rel="noopener noreferrer"&gt;Supabase&lt;/a&gt; (where I am an investor) is a "smart client" solution that works equally well on the server, which invests heavily in the &lt;a href="https://postgrest.org/en/v8.0/" rel="noopener noreferrer"&gt;open source PostgREST project&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Followups
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Transitional Apps
&lt;/h3&gt;

&lt;p&gt;Rich Harris recently gave a keynote at Jamstack conf that framed his take on this issue (&lt;a href="https://twitter.com/Rich_Harris/status/1446601175197880325" rel="noopener noreferrer"&gt;TLDR here&lt;/a&gt;):&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/860d8usGC0o"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h3&gt;
  
  
  Reader Feedback
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Jonathan W: "The framing of the issue got my brain percolating a bit. The entire situation feels very similar to the first time a developer recognizes object-relational impedance mismatch—all the subtle differences that start to crop up as you layer an Application Framework on top of an ORM on top of an RDBMS on top of your business domain (you know, that kind of important topic). Each layer of abstraction is acceptable by itself, but the effects compound at each level and over time."&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://twitter.com/thdxr" rel="noopener noreferrer"&gt;@thxdr&lt;/a&gt;: Other format efforts worth exploring: &lt;a href="https://jsonapi.org/" rel="noopener noreferrer"&gt;JSONAPI&lt;/a&gt; is a JSON REST spec, and the &lt;a href="https://relay.dev/docs/guides/graphql-server-specification/" rel="noopener noreferrer"&gt;Relay spec&lt;/a&gt; is essentially a GraphQL superset spec&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>react</category>
      <category>graphql</category>
      <category>rest</category>
      <category>supabase</category>
    </item>
    <item>
      <title>September 11th from Singapore</title>
      <dc:creator>swyx</dc:creator>
      <pubDate>Sat, 11 Sep 2021 22:26:53 +0000</pubDate>
      <link>https://dev.to/swyx/september-11th-from-singapore-1a6l</link>
      <guid>https://dev.to/swyx/september-11th-from-singapore-1a6l</guid>
      <description>&lt;p&gt;I was on the other side of the world when it happened.&lt;/p&gt;

&lt;p&gt;My memory is curiously spotty about this major event I lived through. I remember exactly what I was doing at the time; yet I remember almost nothing else after.&lt;/p&gt;

&lt;p&gt;Singapore is ~12 hours ahead of New York, so I was just finishing up my night class at home, walking out to our living room TV telegraphing that the world had forever changed. I remember thinking how it was a horrific accident, and then seeing footage of the second plane and realizing it was no accident. I remember the third plane crashing into Pentagon. Then the fourth. Drip-fed to us minute by minute through our local TV news, by scared reporters who barely knew any more than we did.&lt;/p&gt;

&lt;p&gt;I don't know how I went to sleep that night. I remember looking at my baby sister, playing in her pen. Totally oblivious that the world she was born into had changed forever. (I would get that feeling again 2 years later, as &lt;a href="https://en.wikipedia.org/wiki/2002%E2%80%932004_SARS_outbreak"&gt;SARS&lt;/a&gt; threatened to lock down Singapore in the first coronavirus outbreak of our lifetimes.)&lt;/p&gt;

&lt;p&gt;I didn't have any American friends at the time so it never got personal. But we watched with horror and confusion as the stories trickled out; &lt;a href="https://en.wikipedia.org/wiki/The_Falling_Man"&gt;the Falling Man&lt;/a&gt;, &lt;a href="https://www.nps.gov/flni/learn/historyculture/phone-calls-from-flight-93.htm"&gt;the Flight 93 phone calls&lt;/a&gt;, &lt;a href="https://en.wikipedia.org/wiki/Raising_the_Flag_at_Ground_Zero"&gt;the firefighters&lt;/a&gt;. I have no memory of how my parents or teachers dealt with it or moved on from it. I was myself unaware of how the military response moved from Afghanistan to Iraq and the intense debate prior and the quagmire after.&lt;/p&gt;

&lt;p&gt;As a country with a 22% Muslim population, surrounded by other majority Muslim countries, tensions definitely were tight. First, copycat terrorists had to be preempted. Second, relationships needed to be rebuilt between faiths. &lt;a href="https://www.straitstimes.com/singapore/politics/timeline-20-years-of-securing-singapore-following-911-attacks"&gt;We would spend the next 1-7 years doing this.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Travel didn't happen for me until about 4 years later when I went on my first international trip for a choir competition. By then almost all the security measures you have today were in place, including the &lt;a href="https://en.wikipedia.org/wiki/2001_shoe_bomb_attempt"&gt;shoe requirement&lt;/a&gt; (which now seems to have faded), although they had yet to &lt;a href="https://www.mentalfloss.com/article/565368/tsa-airport-liquids-rule-water-bottles-explained"&gt;ban water&lt;/a&gt;, and the X Ray machines weren't as fancy. For sure the days of &lt;a href="https://www.youtube.com/watch?v=g1q4w4wq8Cw"&gt;Rachel casually walking up to a flight&lt;/a&gt; 30 minutes before departure were gone.&lt;/p&gt;

&lt;p&gt;Later when I learned more about geopolitics we would debate &lt;a href="https://en.wikipedia.org/wiki/The_End_of_History_and_the_Last_Man"&gt;The End of History&lt;/a&gt; vs &lt;a href="https://en.wikipedia.org/wiki/Clash_of_Civilizations"&gt;The Clash of Civilizations&lt;/a&gt; at school and I recall my classmates having very strong opinions without having really read either book. It's still not clear to me that any of that was at all useful or relevant. I remember reading dire essays about the disruptive threat of nonstate actors, yet, &lt;a href="https://noahpinion.substack.com/p/the-911-era-is-over-good"&gt;as Noah Smith notes&lt;/a&gt;, state power won, once citizens gave up their privacy.&lt;/p&gt;

&lt;p&gt;I wish I had journaled more back then. I wish I had told myself how it felt living through what I knew to be a special time. I wish I had close Muslim friends to process it with. And I wish I appreciated the difference between sudden tragedy and chronic, endemic disasters.&lt;/p&gt;

</description>
      <category>reflections</category>
    </item>
    <item>
      <title>The Swipe Files Strategy for Part Time Creators</title>
      <dc:creator>swyx</dc:creator>
      <pubDate>Fri, 03 Sep 2021 20:59:46 +0000</pubDate>
      <link>https://dev.to/swyx/the-swipe-files-strategy-for-part-time-creators-45bd</link>
      <guid>https://dev.to/swyx/the-swipe-files-strategy-for-part-time-creators-45bd</guid>
      <description>&lt;p&gt;What if I told you there was a simple strategy that can help you slow-cook a $300,000 a year side project and kickstart your indie hacker career, on a few hours a week?&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%2Feoqzqrmmx3riez0ly7gi.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%2Feoqzqrmmx3riez0ly7gi.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you didn't care about money, how about &amp;gt;100k Twitter follows over 2 years?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftlsvvck7t6wl74v3dq50.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%2Ftlsvvck7t6wl74v3dq50.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Or email subscribers?&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%2Fnenqwuk4glkok456gc3f.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%2Fnenqwuk4glkok456gc3f.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Build a swipe file
&lt;/h2&gt;

&lt;p&gt;A "swipe file" is a collection of examples to copy for inspiration. The above screenshots are all real life examples from &lt;a href="https://really-good-emails.notion.site/Really-Good-Emails-CEO-Hunt-60b87550816149719e166e15d22b9094" rel="noopener noreferrer"&gt;ReallyGoodEmails&lt;/a&gt; and &lt;a href="https://www.indieldn.com/post/how-harry-dry-grew-marketing-examples-to-40k-newsletter-subscribers" rel="noopener noreferrer"&gt;MarketingExamples&lt;/a&gt;, both (extraordinarily well executed) swipe files.&lt;/p&gt;

&lt;p&gt;I think it is one of the most underrated ways for &lt;a href="https://www.swyx.io/part-time-creator-manifesto/" rel="noopener noreferrer"&gt;Part Time Creators&lt;/a&gt; to build a newsletter, build a brand, and get good at their craft, without reputation, expertise, or large upfront expenditure in time or money.&lt;/p&gt;

&lt;p&gt;Here is a Swipe File Swipe File for creators getting started.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Strategy
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Start a Swipe File&lt;/li&gt;
&lt;li&gt;Collect examples&lt;/li&gt;
&lt;li&gt;Offer newsletter signup&lt;/li&gt;
&lt;li&gt;Offer login + faving&lt;/li&gt;
&lt;li&gt;Build a Community&lt;/li&gt;
&lt;li&gt;Hit the Media Circuit&lt;/li&gt;
&lt;li&gt;Make Generators&lt;/li&gt;
&lt;li&gt;Monetize

&lt;ol&gt;
&lt;li&gt;Sell Sponsor ads&lt;/li&gt;
&lt;li&gt;Do a live workshop&lt;/li&gt;
&lt;li&gt;Write a book&lt;/li&gt;
&lt;li&gt;Build a course&lt;/li&gt;
&lt;/ol&gt;


&lt;/li&gt;

&lt;/ol&gt;

&lt;p&gt;I add some more details below but that's the essence of the progression.&lt;/p&gt;

&lt;p&gt;I recorded everything below as a 14 minute video if that's your thing.&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/2r7fSeMCcK8"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  A Simple Directory
&lt;/h2&gt;

&lt;p&gt;Good swipe files focus in on a niche but important problem, that nobody specializes in. Here is &lt;a href="https://www.ogimage.gallery/" rel="noopener noreferrer"&gt;a site that only features og:image inspiration&lt;/a&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv6299vhagfkz9k1fq2vd.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%2Fv6299vhagfkz9k1fq2vd.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It is pretty simple; the design comes from others, but the categorization comes from you. Clicking on each just sends you to the site it came from; no commentary or analysis needed. &lt;/p&gt;

&lt;h2&gt;
  
  
  In-App Flows
&lt;/h2&gt;

&lt;p&gt;Swipe files are usually static snapshots of public marketing design and copywriting, but there's no reason why you can't document the logged-in experience as well. Here's &lt;a href="https://designvault.io/app/clubhouse/" rel="noopener noreferrer"&gt;DesignVault&lt;/a&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhzbrowvearqmspetpz8k.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%2Fhzbrowvearqmspetpz8k.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Add Commentary
&lt;/h2&gt;

&lt;p&gt;Swipe files lend themselves to marketing and design, because by their very nature they are visual and public. Here is &lt;a href="https://swipefile.com/" rel="noopener noreferrer"&gt;swipefile.com&lt;/a&gt;, with just a couple paragraphs of commentary on each example:&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%2F2nmnfl3k05grws3x7szn.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%2F2nmnfl3k05grws3x7szn.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Use a Tool&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;The work gets a lot simpler and faster when you use a tool that is designed for capturing swipe files. Here's &lt;a href="https://klart.io/" rel="noopener noreferrer"&gt;Klart&lt;/a&gt;: &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F44tsvlvhkgbcw3g1t1wt.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%2F44tsvlvhkgbcw3g1t1wt.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Build up Teardowns
&lt;/h2&gt;

&lt;p&gt;Corey Haines' &lt;a href="http://swipefiles.com" rel="noopener noreferrer"&gt;SwipeFiles.com&lt;/a&gt; (note the plural, different from SwipeFile.com) adds step by step commentary with generalizable lessons (&lt;a href="https://www.swipefiles.com/teardowns/baremetrics-recover-landing-page" rel="noopener noreferrer"&gt;scroll through this to understand&lt;/a&gt;):&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzy6okzdsn5ygbae3b154.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%2Fzy6okzdsn5ygbae3b154.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Compelling How Tos
&lt;/h2&gt;

&lt;p&gt;Switch the focus from a random assortment of "What I like about X" to a more directed "How X does IMPORTANT_THING_EVERYONE_WANTS". Title game is everything. Can you generate the "instant click impulse?"&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%2F6jjooukovhklowhannwd.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%2F6jjooukovhklowhannwd.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Offer a Newsletter
&lt;/h2&gt;

&lt;p&gt;Yes, people hate popups. Yes, they work. Yes, you can blanket it with social proof, like Harry Dry does on &lt;a href="https://marketingexamples.com/" rel="noopener noreferrer"&gt;MarketingExamples.com&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fiooeq2s17haf3c8k93af.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%2Fiooeq2s17haf3c8k93af.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Build an Auth &amp;amp; Storage layer
&lt;/h2&gt;

&lt;p&gt;Since you have people's emails, you might as well give them a way to log in. As your examples pile up, people are going to want to save them to their collections, and you will want to use their faves to indicate popularity. See &lt;strong&gt;&lt;a href="https://reallygoodemails.com/" rel="noopener noreferrer"&gt;ReallyGoodEmails&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe1ijsjsplr2ssofeo78f.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%2Fe1ijsjsplr2ssofeo78f.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Build a Community
&lt;/h2&gt;

&lt;p&gt;Since people log in and heart stuff, it'd be nice if they could chat. &lt;a href="https://jayclouse.com/how-to-build-an-online-community/" rel="noopener noreferrer"&gt;Circle is the best platform for that&lt;/a&gt;. Here's the one I maintain for Learning in Public: &lt;a href="https://codingcareer.circle.so/c/learn-in-public" rel="noopener noreferrer"&gt;https://codingcareer.circle.so/c/learn-in-public&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4kk7hsqbjb95z5tz7a1d.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%2F4kk7hsqbjb95z5tz7a1d.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Borrow a Platform
&lt;/h2&gt;

&lt;p&gt;Wow, look at you! Email newsletter, Auth, Storage, Community... you're starting to need some serious software now around your swipe file, and you STILL need to figure out your social media distribution. You don't HAVE to have a separate platform — I often use my own Twitter as a flexible social Swipe File, and &lt;a href="https://littlebigdetails.com/" rel="noopener noreferrer"&gt;Little Big Details&lt;/a&gt; is a Swipe File hosted on Tumblr with distribution, storage, and social features built in.&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%2Fz90evhfpic26cnh5v860.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%2Fz90evhfpic26cnh5v860.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Just keep in mind that you're &lt;em&gt;borrowing&lt;/em&gt; that platform; if you didn't build it, it's not yours, and can be taken away at any time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Make Generators and Prompts
&lt;/h2&gt;

&lt;p&gt;If you CAN code though, you can level up your swipe file by letting people play mad libs with it (aka templatize your swipe file). Here's &lt;a href="https://whattotweet.com/#" rel="noopener noreferrer"&gt;What To Tweet&lt;/a&gt; from &lt;a href="https://joshspector.com/what-to-tweet/" rel="noopener noreferrer"&gt;Josh Spector&lt;/a&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0p0qfrkstnh32h3sddo0.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%2F0p0qfrkstnh32h3sddo0.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Or &lt;a href="https://copywritingcourse.com/blog-post-idea-generator/" rel="noopener noreferrer"&gt;the Blog Post Idea Generator&lt;/a&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3waei0hyg9pxvuw0ci49.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%2F3waei0hyg9pxvuw0ci49.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And if you get really, really lucky, you can turn these &lt;a href="https://headlime.com/blog/headlime-and-conversionai-join-forces-in-artificial-copywriting" rel="noopener noreferrer"&gt;templates into a 7 figure business&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Hit the Media Circuit
&lt;/h2&gt;

&lt;p&gt;With some initial success, it's time to hit the media circuit to get the attention up for the work you've already done. &lt;a href="https://www.youtube.com/watch?v=vAE1Ssc56Nc" rel="noopener noreferrer"&gt;Conferences&lt;/a&gt;, &lt;a href="https://www.listennotes.com/search/?q=marketing%20examples%20harry%20dry&amp;amp;sort_by_date=0&amp;amp;scope=episode&amp;amp;offset=0&amp;amp;language=Any%20language&amp;amp;len_min=0" rel="noopener noreferrer"&gt;Podcasts&lt;/a&gt;, Contributed articles.&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%2F4oxrtexujye3f8p2646d.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%2F4oxrtexujye3f8p2646d.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Time to Monetize
&lt;/h2&gt;

&lt;p&gt;Sponsored Ads:&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%2Fmcp701x0bzwy6saebxfs.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%2Fmcp701x0bzwy6saebxfs.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Rob Hope &lt;a href="https://www.swyx.io/rob_hope_hot_tips/" rel="noopener noreferrer"&gt;turned One Page Love into a book&lt;/a&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flxr7k0g7ijbyj60qtif8.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%2Flxr7k0g7ijbyj60qtif8.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And a &lt;a href="https://onepagelove.com/workshop" rel="noopener noreferrer"&gt;workshop&lt;/a&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fccrjgui1slyce6nbnocq.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%2Fccrjgui1slyce6nbnocq.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Or a recurring membership or SaaS, as &lt;a href="https://really-good-emails.notion.site/Really-Good-Emails-CEO-Hunt-60b87550816149719e166e15d22b9094" rel="noopener noreferrer"&gt;ReallyGoodEmail&lt;/a&gt; have done:&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%2F5fvu1efknm63zc0se96q.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%2F5fvu1efknm63zc0se96q.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  But I Don't Do Marketing Or Design
&lt;/h2&gt;

&lt;p&gt;Don't be so literal about what a swipe file is — pay attention to the &lt;em&gt;spirit&lt;/em&gt; of the thing. &lt;a href="https://hn.algolia.com/?q=mental+models" rel="noopener noreferrer"&gt;Mental Models&lt;/a&gt; are swipe files of perspectives to analyze given situations. A &lt;a href="https://twitter.com/shreyas/status/1399042778613485578" rel="noopener noreferrer"&gt;tweet thread of frameworks&lt;/a&gt; is a swipe file of playbooks. &lt;a href="https://github.com/typescript-cheatsheets/react" rel="noopener noreferrer"&gt;My Cheatsheets&lt;/a&gt; are swipe files of code, as is &lt;a href="https://usehooks.com/" rel="noopener noreferrer"&gt;useHooks&lt;/a&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftz98f00820zl1cno34ke.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%2Ftz98f00820zl1cno34ke.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;All enormously useful, passively compounding sources of personal leverage for your creator journey.&lt;/p&gt;

</description>
      <category>creators</category>
    </item>
    <item>
      <title>3 Reasons to Upgrade Git For The First Time Ever</title>
      <dc:creator>swyx</dc:creator>
      <pubDate>Mon, 30 Aug 2021 22:29:07 +0000</pubDate>
      <link>https://dev.to/swyx/3-reasons-to-upgrade-git-2bj3</link>
      <guid>https://dev.to/swyx/3-reasons-to-upgrade-git-2bj3</guid>
      <description>&lt;p&gt;Git moves at a surprisingly fast pace - as of Aug 2021, the current version of Git (&lt;code&gt;git --version&lt;/code&gt;) is 2.33.&lt;/p&gt;

&lt;p&gt;I don't normally know or care what my Git version is, but over the past year this changed. The Git team must be doing something right, because I've noticed a few things trickle down to my relatively light usage:&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;init.defaultBranch&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;The default branch in Git is named &lt;code&gt;master&lt;/code&gt;. As of Git 2.28, you can now configure it to anything else. Of course, &lt;a href="https://mooseyanon.medium.com/github-f-ck-your-name-change-de599033bbbe"&gt;this is merely symbolic&lt;/a&gt; but is an appreciated signal.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git config &lt;span class="nt"&gt;--global&lt;/span&gt; init.defaultBranch main
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This setting will work for new repos. For existing repos, and git aliases that work nicely with BOTH &lt;code&gt;master&lt;/code&gt; and &lt;code&gt;main&lt;/code&gt;, you can see my &lt;a href="https://dev.to/swyx/move-primary-git-branch-from-master-to-main-m7c"&gt;Cheatsheet for moving from Master to Main&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;git sparse-checkout&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;You probably know about &lt;code&gt;git clone -–depth 1 [remote-url]&lt;/code&gt; for repos where you don't want to download the full history. Git 2.25 introduced an additional command for giant repos, where even the current version is too big. So sparse checkout is like &lt;code&gt;git clone --depth 1&lt;/code&gt; but for files:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone &lt;span class="nt"&gt;--no-checkout&lt;/span&gt; https://github.com/my/big-repo
&lt;span class="nb"&gt;cd &lt;/span&gt;big-repo/
&lt;span class="c"&gt;# only files at the root&lt;/span&gt;
git sparse-checkout init &lt;span class="nt"&gt;--cone&lt;/span&gt; 
&lt;span class="c"&gt;# only files in specific directories (recursive)&lt;/span&gt;
git sparse-checkout &lt;span class="nb"&gt;set&lt;/span&gt; &amp;lt;dir1&amp;gt; &amp;lt;dir2&amp;gt; ...

&lt;span class="c"&gt;# do it&lt;/span&gt;
git checkout main
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://github.blog/2020-01-17-bring-your-monorepo-down-to-size-with-sparse-checkout/"&gt;More on the GitHub blog&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;git restore&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;I needed this one today. Basically if you screw up a file and need to bring it back from Git, you can use the new &lt;code&gt;git restore&lt;/code&gt; command in v2.23:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git restore path/to/file &lt;span class="c"&gt;# discard current changes&lt;/span&gt;
git restore &lt;span class="nt"&gt;--staged&lt;/span&gt; path/to/file &lt;span class="c"&gt;# unstage staged file&lt;/span&gt;
git restore &lt;span class="nt"&gt;--source&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;hash&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; path/to/file  &lt;span class="c"&gt;## restore from specific commit hash&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;⚠️ Be careful - once you run this command you cannot get back the changes you discard.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Yes, there are confusingly similar commands between &lt;code&gt;checkout&lt;/code&gt;, &lt;code&gt;restore&lt;/code&gt;, &lt;code&gt;reset&lt;/code&gt;, and even &lt;code&gt;switch&lt;/code&gt;. &lt;a href="https://stackoverflow.com/a/58003889/1106414"&gt;This StackOverflow answer&lt;/a&gt; clarifies pretty authoritatively:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;git-revert&lt;/code&gt; is about making a new commit that reverts the changes made by other commits.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;git-restore&lt;/code&gt; is about restoring files in the working tree from either the index or another commit.
This command does not update your branch.
The command can also be used to restore files in the index from another commit.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;git-reset&lt;/code&gt; is about updating your branch, moving the tip in order to add or remove commits from the branch. This operation changes the commit history.
git reset can also be used to restore the index, overlapping with git restore.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>git</category>
      <category>tips</category>
    </item>
  </channel>
</rss>
