<?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: The Gnar Company</title>
    <description>The latest articles on DEV Community by The Gnar Company (@thegnarco).</description>
    <link>https://dev.to/thegnarco</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%2Forganization%2Fprofile_image%2F3787%2F32d6c975-830b-4966-9c05-9c726be93f41.png</url>
      <title>DEV Community: The Gnar Company</title>
      <link>https://dev.to/thegnarco</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/thegnarco"/>
    <language>en</language>
    <item>
      <title>React Server Components Example with Next.js</title>
      <dc:creator>Royce Threadgill</dc:creator>
      <pubDate>Tue, 16 Apr 2024 19:17:12 +0000</pubDate>
      <link>https://dev.to/thegnarco/react-server-components-example-with-nextjs-14i0</link>
      <guid>https://dev.to/thegnarco/react-server-components-example-with-nextjs-14i0</guid>
      <description>&lt;p&gt;React Server Components (oftentimes shortened to &lt;strong&gt;RSC&lt;/strong&gt;) provide us with a fascinating new approach to building React applications.&lt;/p&gt;

&lt;p&gt;That said, my experience while researching this article was &lt;strong&gt;far less rosy&lt;/strong&gt; and &lt;strong&gt;far more expletive laden &lt;/strong&gt;than I expected.&lt;/p&gt;

&lt;p&gt;But now that I have been one acquainted with the RSC, I’d like to share my findings with you so that hopefully your learning journey won’t be quite as painful or confusing.&lt;/p&gt;

&lt;p&gt;In this article, we’ll discuss a range of topics, including:&lt;/p&gt;

&lt;ul&gt;
    &lt;li&gt;What are React Server Components?&lt;/li&gt;
    &lt;li&gt;Can you use React Server Components without Next.js?&lt;/li&gt;
    &lt;li&gt;How do React Server Components work in Next.js?&lt;/li&gt;
    &lt;li&gt;What are the advantages and disadvantages of React Server Components?&lt;/li&gt;
    &lt;li&gt;Are React Server Components production ready?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We’ll also go through a React Server Components example using the Next.js app router. That example will discuss RSC &lt;strong&gt;streaming behavior&lt;/strong&gt; and &lt;strong&gt;client and server component nesting&lt;/strong&gt;. If you’re only interested in the code example, just scroll to the bottom of the article.&lt;/p&gt;

&lt;p&gt;Let’s begin!&lt;/p&gt;

&lt;h2&gt;
  
  
  What are React Server Components?
&lt;/h2&gt;

&lt;p&gt;My favorite definition of RSC comes from &lt;a href="https://github.com/markdalgleish"&gt;Mark Dalgleish&lt;/a&gt; in his 2023 React Advanced conference talk, “&lt;a href="https://youtu.be/36uY-c0E_EQ?si=SipZ6fLWkd0B2pCc"&gt;Simplifying Server Components&lt;/a&gt;”. The way he puts it is that “server components are just virtual DOM over the network”.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F76iasv9he2begq06pppc.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F76iasv9he2begq06pppc.jpg" alt="Mark Dalgleish provides RSC definition" width="800" height="394"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What does this mean?&lt;/p&gt;

&lt;p&gt;Let’s say we have a React component called &lt;code&gt;Basic&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Basic&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Hello&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;World&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/p&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/main&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On the server, that component would be serialized into what’s known as an &lt;strong&gt;RSC payload&lt;/strong&gt;. That would look &lt;em&gt;something &lt;/em&gt;like this in the Next.js server component implementation.&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="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;...complicated&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Next.js&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;stuff&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;up&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;here&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;children:&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="s2"&gt;"__PAGE__"&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="s2"&gt;"$L1"&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="s2"&gt;"$"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"main"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="err"&gt;children:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"$"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"p"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;children:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Hello, World"&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="kc"&gt;null&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="err"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;...more&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;complicated&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Next.js&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;stuff&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;down&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;here&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That RSC payload would then be streamed down to the client and rendered exactly like any other JSX.&lt;/p&gt;

&lt;p&gt;ℹ️ &lt;em&gt;Sidenote: This explanation gets at the core of React Server Components, but is a little simplified. If you want to learn more about what’s happening under the hood, I highly recommend watching to &lt;a href="https://github.com/dai-shi"&gt;Daishi Kato&lt;/a&gt;’s “&lt;a href="https://youtu.be/_SLh2g5N3mk?si=fSg8799Qm79nsbm3"&gt;Exploring React Server Component Fundamentals&lt;/a&gt;” talk at React Day Berlin 2023.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Seems pretty simple, right? &lt;strong&gt;So why bother with this RSC payload nonsense?&lt;/strong&gt; Why not just render the component to HTML and stream &lt;em&gt;that&lt;/em&gt; down to the client?&lt;/p&gt;

&lt;p&gt;Well, we can! Typically, we would call this &lt;strong&gt;server side rendering&lt;/strong&gt; (SSR).&lt;/p&gt;

&lt;h2&gt;
  
  
  React Server Components vs SSR
&lt;/h2&gt;

&lt;p&gt;We’ve actually been able to serialize React components to HTML for several years now.&lt;/p&gt;

&lt;p&gt;You can find a solid &lt;a href="https://vitejs.dev/guide/ssr"&gt;definition of SSR in the Vite documentation&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;SSR specifically refers to front-end frameworks (for example React, Preact, Vue, and Svelte) that support running the same application in Node.js, pre-rendering it to HTML, and finally hydrating it on the client.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Put simply, &lt;strong&gt;SSR pre-renders a React component to HTML&lt;/strong&gt;, while &lt;strong&gt;RSC serializes a React component into an RSC payload&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Server components have a couple advantages over traditional SSR. For one, SSR requires a hydration step once the HTML has been sent to the client; the initially loaded page isn’t interactive until the entire JS bundle is downloaded and the entire page is hydrated.&lt;/p&gt;

&lt;p&gt;By comparison, server components require no hydration step, allowing pages to &lt;strong&gt;load faster&lt;/strong&gt; &lt;strong&gt;and become interactive more quickly&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The page loading issues raised by SSR can be mitigated by chunking your JavaScript bundle and streaming it to the client, but eventually the entire bundle needs to be downloaded. And that bundle tends to get larger as your application grows.&lt;/p&gt;

&lt;p&gt;Server components, meanwhile, add no additional JavaScript to your bundle. As your application grows, your JavaScript bundle size does not grow with it (assuming you &lt;em&gt;only&lt;/em&gt; use server components).&lt;/p&gt;

&lt;p&gt;ℹ️ &lt;em&gt;Sidenote: It’s important to note that RSC and SSR aren’t mutually exclusive. You can learn a bit more about this by reading “&lt;a href="https://www.joshwcomeau.com/react/server-components/"&gt;Making Sense of React Server Components&lt;/a&gt;” by Josh Comeau.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Server Components vs. Client Components
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;Basic&lt;/code&gt; component above was a simple example. What happens when we add interactivity to our component?&lt;/p&gt;

&lt;p&gt;Let’s say we add a &lt;code&gt;Counter&lt;/code&gt; to our &lt;code&gt;Basic&lt;/code&gt; component:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Basic&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Counter&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Hello&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;World&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/p&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/main&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When we interact with the &lt;code&gt;Counter&lt;/code&gt;, we would expect the value to increment. In other words, &lt;strong&gt;that component has state&lt;/strong&gt; and must be declared as a client component in our React code.&lt;/p&gt;

&lt;p&gt;How do we represent such a component in a format that can be generated on the server and sent to the client? The answer, as you may have guessed, lies in the &lt;strong&gt;RSC payload&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;When a bundler supporting React Server Components detects a client component, it adds a reference to that component in the RSC payload – &lt;code&gt;react.client.reference&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyiu8w02aedgtmkppu457.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyiu8w02aedgtmkppu457.jpg" alt="Mark Dalgleish explains RSC client references" width="800" height="415"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The bundler also generates a manifest that maps the reference ID to the actual client component, which determines what JavaScript must be sent to the client (meaning that, unlike server components, &lt;strong&gt;client components&lt;/strong&gt; &lt;strong&gt;will contribute to your bundle size&lt;/strong&gt;).&lt;/p&gt;

&lt;p&gt;In &lt;a href="https://changelog.com/jsparty/311"&gt;Episode #311 of the Changelog podcast&lt;/a&gt;, &lt;a href="https://github.com/gaearon"&gt;Dan Abramov&lt;/a&gt; said (and I’m paraphrasing a bit here) the RSC payload resulting from such scenarios can be thought of as having a &lt;code&gt;script&lt;/code&gt; tag in place of the client component.&lt;/p&gt;

&lt;p&gt;ℹ️ &lt;em&gt;Sidenote:&lt;/em&gt; &lt;em&gt;If you’re interested in learning a bit more about how client components are serialized, take a look at “&lt;a href="https://timtech.blog/posts/react-server-components-rsc-no-framework/"&gt;React Server Components, without a framework?&lt;/a&gt;” by &lt;a href="https://github.com/ziir"&gt;Tim Pillard&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;As the late Billy Mays would say, “But wait, there’s more!”&lt;/p&gt;

&lt;p&gt;What happens when you nest a server component &lt;em&gt;inside&lt;/em&gt; that client component? As wacky as it sounds, &lt;strong&gt;your bundler recognizes that as a server component&lt;/strong&gt; and serializes it as such in the RSC payload.&lt;/p&gt;

&lt;p&gt;Don’t worry, we’ll get more into server components vs client components in our code example. But first, let’s address the elephant in the room….&lt;/p&gt;

&lt;h2&gt;
  
  
  React Server Components Without Next.js…?
&lt;/h2&gt;

&lt;p&gt;Daishi Kato, creator of &lt;a href="https://github.com/pmndrs/zustand"&gt;Zustand&lt;/a&gt; and &lt;a href="https://github.com/pmndrs/jotai"&gt;Jotai&lt;/a&gt;, says there are a few things we need to support RSC serialization:&lt;/p&gt;

&lt;ul&gt;
    &lt;li&gt;
&lt;strong&gt;Bundler&lt;/strong&gt; capable of recognizing client components, generating a manifest, and resolving client references on the client&lt;/li&gt;
    &lt;li&gt;
&lt;strong&gt;Server&lt;/strong&gt; capable of accepting HTTP requests and responding with (dynamic) RSC payloads&lt;/li&gt;
    &lt;li&gt;
&lt;strong&gt;Router&lt;/strong&gt; capable of intelligently serving resources (e.g., don’t render client components that are not useful for a given route)&lt;/li&gt;
    &lt;li&gt;
&lt;strong&gt;SSR&lt;/strong&gt; that works in tandem with React Server Components, largely for initial page load performance&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So while the RSC concept is fairly straightforward in theory, there are a number of moving parts necessary for implementing RSCs in practice.&lt;/p&gt;

&lt;p&gt;Currently, &lt;strong&gt;Next.js is the only production framework that fully supports RSCs&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This isn’t an accident; when &lt;a href="https://react.dev/blog/2020/12/21/data-fetching-with-react-server-components"&gt;Meta introduced React Server Components&lt;/a&gt;, Dan Abramov explicitly stated that they collaborated with the Next.js team to develop the &lt;a href="https://github.com/facebook/react/tree/main/packages/react-server-dom-webpack"&gt;RSC webpack plugin&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;That means you’re &lt;em&gt;mostly&lt;/em&gt; limited to Next.js if you’d like to build an app with RSCs.&lt;/p&gt;

&lt;p&gt;📣 &lt;strong&gt;Important&lt;/strong&gt;: This article was originally written in March 2024. At the very end of that month, &lt;a href="https://redwoodjs.com/blog/rsc-now-in-redwoodjs"&gt;RedwoodJS announced full support for server components&lt;/a&gt;! While there still appear to be some implementation issues, RedwoodJS &lt;em&gt;does&lt;/em&gt; now offer a viable alternative for implementing React Server Components in production.&lt;/p&gt;

&lt;h3&gt;
  
  
  But What If I Really Don’t Like Next.js?
&lt;/h3&gt;

&lt;p&gt;If you’re really against using Next.js, your options are limited and largely experimental.&lt;/p&gt;

&lt;p&gt;The moderators at &lt;a href="https://www.reactiflux.com/"&gt;Reactiflux&lt;/a&gt; pointed me toward &lt;a href="https://waku.gg/"&gt;Waku&lt;/a&gt; (also developed by Daishi Kato) as a potential alternative. However, Daishi explicitly recommends using the framework on non-production projects.&lt;/p&gt;

&lt;p&gt;At Node Conference 2023, Jarred Sumner (creator of &lt;a href="https://bun.sh/"&gt;Bun&lt;/a&gt;) showed a &lt;a href="https://portal.gitnation.org/contents/server-components-with-bun"&gt;demo of server components in Bun&lt;/a&gt;, so there is at least partial support in that ecosystem. The Bun repo provides &lt;a href="https://github.com/oven-sh/bun/blob/7f16164b25a4bebb25532285ee3f25746857c5cf/packages/bun-plugin-server-components/README.md"&gt;bun-plugin-server-components&lt;/a&gt; as the official plugin for server components. And while I haven’t looked at it in-depth, &lt;a href="https://github.com/hex2f/marz"&gt;Marz&lt;/a&gt; claims to be a “React Server Components Framework for Bun”.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmzxbt62culxpktr5qmea.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmzxbt62culxpktr5qmea.jpg" alt="Jarred Sumner explains server components with Bun" width="800" height="419"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the Changelog Podcast episode referenced above, Dan Abramov alluded to &lt;a href="https://parceljs.org/"&gt;Parcel&lt;/a&gt; working on RSC support as well. I couldn’t find much to back up that claim aside from a &lt;a href="https://github.com/parcel-bundler/parcel/issues/9050"&gt;GitHub issue discussing directives&lt;/a&gt; and a &lt;a href="https://twitter.com/devongovett/status/1749629092914172388"&gt;social media post by Devon Govett&lt;/a&gt; (creator of Parcel), so I can’t say for sure if Parcel is currently a viable option for developing with RSCs.&lt;/p&gt;

&lt;p&gt;And you can’t use React Server Components with Vite &lt;em&gt;right now&lt;/em&gt;, but &lt;a href="https://github.com/facebook/react/pull/26926"&gt;support is being worked on&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  React Server Components Example with Next.js
&lt;/h2&gt;

&lt;p&gt;Alright, with that out of the way, let’s get to the code example. Note that we’re using Tailwind for styling just to make the example a bit less ugly; Tailwind has no other bearing on the example.&lt;/p&gt;

&lt;p&gt;For reference, here are our dependencies:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"dependencies"&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;"react"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^18"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"react-dom"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^18"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"next"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"14.1.3"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"devDependencies"&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;"typescript"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^5"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"@types/node"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^20"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"@types/react"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^18"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"@types/react-dom"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^18"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"autoprefixer"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^10.0.1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"postcss"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^8"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"tailwindcss"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^3.3.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"eslint"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^8"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"eslint-config-next"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"14.1.3"&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;We’re going to cover a few concepts here, but we’ll start with a React Server Components &lt;code&gt;Suspense&lt;/code&gt; example to demonstrate how payload streaming is handled.&lt;/p&gt;

&lt;h3&gt;
  
  
  React Server Components and &lt;code&gt;Suspense&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;React &lt;code&gt;Suspense&lt;/code&gt; can intelligently display parts of the RSC payload as they are streamed down to the client. If the component is not yet available, &lt;code&gt;Suspense&lt;/code&gt; will fall back to a loading state.&lt;/p&gt;

&lt;p&gt;Let’s say we have a component &lt;code&gt;Multiple&lt;/code&gt;, which has multiple server components that take varying amounts of time to load:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/app/multiple/page.tsx&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Multiple&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;flex min-h-screen flex-col items-center justify-between p-24&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;flex w-1/2&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;flex flex-col gap-8&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;h1&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text-4xl&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Multiple&lt;/span&gt; &lt;span class="nx"&gt;long&lt;/span&gt; &lt;span class="nx"&gt;loading&lt;/span&gt; &lt;span class="nx"&gt;things&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/h1&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;
          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Suspense&lt;/span&gt; &lt;span class="nx"&gt;fallback&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Loading&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/p&amp;gt;}&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;LongLoadingComponent&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Suspense&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;
          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Suspense&lt;/span&gt; &lt;span class="nx"&gt;fallback&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Loading&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/p&amp;gt;}&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;LongerLoadingComponent&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Suspense&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;
          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Suspense&lt;/span&gt; &lt;span class="nx"&gt;fallback&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Loading&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/p&amp;gt;}&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;LongestLoadingComponent&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Suspense&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/main&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;Multiple&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each server component is wrapped within a &lt;code&gt;Suspense&lt;/code&gt; component, which falls back to “Loading…” text until the server component is available.&lt;/p&gt;

&lt;p&gt;For the purposes of this example, we’ll mock each long running query with a &lt;code&gt;Promise&lt;/code&gt; that resolves after a defined timeout. So each of the server components will look something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;longLoadingFetch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;promise&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;reject&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Vegeta, what does the scouter say about his power level?&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="mi"&gt;2000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;promise&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;LongLoadingComponent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;longLoadingFetch&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text-white border border-white p-4&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;LongLoadingComponent&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each component will have a different timeout value and string to simulate different server components with different query durations.&lt;/p&gt;

&lt;p&gt;Initially, each server component is unavailable and all display the “Loading…” text. Then the quickest component renders, then the second quickest, then the third quickest.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft5nykmk73sndvs3ti8xw.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft5nykmk73sndvs3ti8xw.gif" alt="React Suspense example" width="1070" height="742"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In &lt;a href="https://syntax.fm/show/718/react-server-components"&gt;Episode #718 of the Syntax podcast&lt;/a&gt;, &lt;a href="https://github.com/wesbos"&gt;Wes Bos&lt;/a&gt; pointed out a handy &lt;a href="https://chromewebstore.google.com/detail/rsc-devtools/jcejahepddjnppkhomnidalpnnnemomn"&gt;RSC Devtools Chrome extension&lt;/a&gt; that we can use to better understand what’s going on here.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjhrpsenhl2etdxd19j72.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjhrpsenhl2etdxd19j72.png" alt="RSC Devtools tree" width="269" height="633"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We can see that we have three lazily loaded components, which lines up with what we would expect. If we click on the first node, we see the RSC payload and the rendered content.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa7jl4fz9ipcvz34fs0dw.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa7jl4fz9ipcvz34fs0dw.jpg" alt="RSC Devtools component" width="800" height="317"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Also note that the page header has already been rendered! The remainder of the RSC payload is streamed down to the client as it becomes available. Importantly, this means &lt;strong&gt;the long-running queries are not blocking page rendering&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;ℹ️ &lt;em&gt;Sidenote: If you’d like to learn more about how the RSC payload is streamed to the client, check out “&lt;a href="https://www.builder.io/blog/why-react-server-components#react-server-components-rendering-lifecycle"&gt;Why React Server Components Are Breaking Builds to Win Tomorrow&lt;/a&gt;”.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;But you may have noticed that this example only deals with server components. Let’s examine a more complex example that involves both client and server components.&lt;/p&gt;

&lt;h3&gt;
  
  
  Interleaving Client and Server Components
&lt;/h3&gt;

&lt;p&gt;This is where the fun begins. In this example, we’ll be looking at how to implement some basic search functionality. Let’s take a look at the &lt;code&gt;Search&lt;/code&gt; component:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/app/search/page.tsx&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Search&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="nx"&gt;searchParams&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="nl"&gt;searchParams&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="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;flex min-h-screen flex-col items-center justify-between p-24&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;flex w-1/2&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;flex flex-col gap-8&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;h1&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text-4xl&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Search&lt;/span&gt; &lt;span class="nx"&gt;example&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/h1&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;
          &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/* client component */&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;CounterProvider&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;

            &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/* client component */&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;SearcheableToDoList&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;

              &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/* client component */&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
              &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;SearchField&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;

              &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Suspense&lt;/span&gt; &lt;span class="nx"&gt;fallback&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Loading&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/p&amp;gt;}&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;
                &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/* server component */&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
                &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ToDoList&lt;/span&gt; &lt;span class="nx"&gt;searchParams&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;searchParams&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;

                  &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/* client component */&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
                  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;CounterDisplay&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
                &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/ToDoList&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;              &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Suspense&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/SearcheableToDoList&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/CounterProvider&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/main&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;Search&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I’ve added comments to indicate where there are server and client components. Note that the &lt;code&gt;searchParams&lt;/code&gt; value passed into the &lt;code&gt;Search&lt;/code&gt; component is a &lt;a href="https://nextjs.org/docs/app/api-reference/file-conventions/page#searchparams-optional"&gt;Next-specific property&lt;/a&gt; containing the query parameters for the page.&lt;/p&gt;

&lt;p&gt;Let’s briefly discuss what the individual components are doing:&lt;/p&gt;

&lt;ul&gt;
    &lt;li&gt;
&lt;code&gt;CounterProvider&lt;/code&gt; is a &lt;strong&gt;client component &lt;/strong&gt;providing a context to its children components; the context value is just &lt;code&gt;counterVal&lt;/code&gt; and &lt;code&gt;setCounterVal&lt;/code&gt;.&lt;/li&gt;
    &lt;li&gt;
&lt;code&gt;SearcheableToDoList&lt;/code&gt; is a &lt;strong&gt;client component &lt;/strong&gt;wrapper that uses the above context to increment the counter value on clicking a button; it displays the &lt;code&gt;counterVal&lt;/code&gt;, a button to increment the counter, &lt;em&gt;and its children.&lt;/em&gt;
&lt;/li&gt;
    &lt;li&gt;
&lt;code&gt;SearchField&lt;/code&gt; is a &lt;strong&gt;client component&lt;/strong&gt; input that updates the URL query parameters&lt;/li&gt;
    &lt;li&gt;
&lt;code&gt;ToDoList&lt;/code&gt; is a &lt;strong&gt;server component&lt;/strong&gt; that reads data from a &lt;code&gt;data.json&lt;/code&gt; file, then filters that data based on the &lt;code&gt;searchParams&lt;/code&gt; retrieved in the &lt;code&gt;Search&lt;/code&gt; parent; it displays the resulting data &lt;em&gt;and its children.&lt;/em&gt;
&lt;/li&gt;
    &lt;li&gt;
&lt;code&gt;CounterDisplay&lt;/code&gt; is a &lt;strong&gt;client component&lt;/strong&gt; that displays the counter value from the &lt;code&gt;CounterProviderContext.&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The Next.js documentation refers to this nested pattern as &lt;a href="https://nextjs.org/docs/app/building-your-application/rendering/composition-patterns#interleaving-server-and-client-components"&gt;“interleaving” server and client components&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;But how is it that we can nest a server component within our client components?&lt;/p&gt;

&lt;p&gt;Recall that React Server Components are just &lt;em&gt;serialized vDOM sent over the network&lt;/em&gt;. So as long as the content in the React tree is serializable, it can be handled without issue by your bundler.&lt;/p&gt;

&lt;p&gt;In this case, the &lt;code&gt;ToDoList&lt;/code&gt; server component is serializable specifically because it is a child of its client component wrapper. Children of a React component are really just props – and &lt;strong&gt;props are serializable&lt;/strong&gt;!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.thegnar.com/wp-content/uploads/2024/04/interleaved-components-example.gif"&gt;Here’s what the rendered components look like&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;There are a couple other little tidbits we can glean from the interleaved components.&lt;/p&gt;

&lt;h4&gt;
  
  
  You Can Still Use Contexts (Carefully)
&lt;/h4&gt;

&lt;p&gt;In Next.js v13+, all components are server components by default and &lt;strong&gt;cannot &lt;/strong&gt;use state hooks like &lt;code&gt;useState&lt;/code&gt;, &lt;code&gt;useEffect&lt;/code&gt;, or &lt;code&gt;useContext&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Client components operate exactly the same as before and &lt;strong&gt;can&lt;/strong&gt; use state hooks. For example:&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;use client&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;CounterProviderValue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;counterVal&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;setCounterVal&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Dispatch&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;SetStateAction&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;CounterProviderContext&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Context&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;CounterProviderValue&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="nx"&gt;createContext&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;CounterProviderValue&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;counterVal&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="na"&gt;setCounterVal&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;CounterProvider&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;FC&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;PropsWithChildren&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;children&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;counterVal&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setCounterVal&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&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="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;CounterProviderContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Provider&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt; &lt;span class="nx"&gt;counterVal&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setCounterVal&lt;/span&gt; &lt;span class="p"&gt;}}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/CounterProviderContext.Provider&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We declare &lt;code&gt;CounterProvider&lt;/code&gt; as a client component with the &lt;code&gt;use client&lt;/code&gt; directive at the top of the file. This directive is how the bundler identifies &lt;code&gt;CounterProvider&lt;/code&gt; as a client component and treats it accordingly in the RSC payload.&lt;/p&gt;

&lt;p&gt;We can use &lt;code&gt;useState&lt;/code&gt; in &lt;code&gt;CounterProvider&lt;/code&gt; only because we’ve declared it as a client component.&lt;/p&gt;

&lt;p&gt;Similarly, we can access the &lt;code&gt;counterVal&lt;/code&gt; and &lt;code&gt;setCounterVal&lt;/code&gt; in child components, &lt;strong&gt;but only if those children are client components&lt;/strong&gt;.&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;use client&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;SearcheableToDoList&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;FC&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;PropsWithChildren&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;children&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;counterVal&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setCounterVal&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;CounterProviderContext&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;flex flex-col gap-2&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;flex gap-2&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;button&lt;/span&gt;
          &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;bg-white text-black&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
          &lt;span class="nx"&gt;onClick&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;setCounterVal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;counterVal&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;
        &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="nx"&gt;Counter&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/button&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;CounterDisplay&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;flex flex-col gap-2&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;SearcheableToDoList&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Because &lt;code&gt;SearcheableToDoList&lt;/code&gt; is a client component, we can access &lt;code&gt;counterVal&lt;/code&gt; and &lt;code&gt;setCounterVal&lt;/code&gt; via &lt;code&gt;useContext&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The same can be said of &lt;code&gt;CounterDisplay&lt;/code&gt;. As long as it is declared as a client component, we can use &lt;code&gt;counterVal&lt;/code&gt; and &lt;code&gt;setCounterVal&lt;/code&gt;. That’s true even when &lt;code&gt;CounterDisplay&lt;/code&gt; is a child of the &lt;code&gt;ToDoList&lt;/code&gt; server component.&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;use client&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;CounterDisplay&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;counterVal&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;CounterProviderContext&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;span&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Count&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;counterVal&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/span&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;;
&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;CounterDisplay&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  You Can Use Server Code in Server Components
&lt;/h4&gt;

&lt;p&gt;Server components are rendered on the server, so we can use any server side code we'd like! For instance, we can read from the file system in &lt;code&gt;ToDoList&lt;/code&gt; to return the list of filtered to-dos.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;promises&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;fs&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// import included for clarity&lt;/span&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;ToDoListProps&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;PropsWithChildren&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;searchParams&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="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;ToDo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;heading&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;isFavorite&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fetchData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;searchParams&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;readFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cwd&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;&lt;span class="s2"&gt;/src/app/data.json`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;utf8&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;parsedJson&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;parsedJson&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;headingSearch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;searchParams&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;heading&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;ToDo&lt;/span&gt;&lt;span class="p"&gt;[]).&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;heading&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;headingSearch&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ToDoList&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;FC&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ToDoListProps&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="nx"&gt;searchParams&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetchData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;searchParams&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;flex gap-4&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;flex flex-col gap-4&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="na"&gt;item&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ToDo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;border border-white p-4&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;h2&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text-2xl&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;heading&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/h2&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/p&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="p"&gt;))}&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;ToDoList&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When &lt;code&gt;SearchField&lt;/code&gt; updates the query parameters, the &lt;code&gt;searchParams&lt;/code&gt; passed to &lt;code&gt;Search&lt;/code&gt; are updated and passed to &lt;code&gt;ToDoList&lt;/code&gt;. We then search the file system using &lt;code&gt;fs.readFile&lt;/code&gt; and filter out the results based on the provided query parameters.&lt;/p&gt;

&lt;p&gt;ℹ️ &lt;em&gt;Sidenote: This works for our example, but there’s a bit of nuance here with Next.js caching behavior that may cause issues if you try something similar in production. Check out this &lt;a href="https://github.com/vercel/next.js/discussions/47227#discussioncomment-5342500"&gt;GitHub discussion&lt;/a&gt; and the &lt;a href="https://nextjs.org/docs/app/api-reference/file-conventions/route-segment-config#dynamic"&gt;Next.js docs&lt;/a&gt; for more information.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;After we’ve fetched our data and populated the component, the resulting RSC payload is streamed down to the client. Notably, this means that &lt;strong&gt;JavaScript used within your server component (including imports) does not get added to the bundle on your front-end&lt;/strong&gt;!&lt;/p&gt;

&lt;p&gt;For completeness, here’s the &lt;code&gt;SearchField&lt;/code&gt; component code:&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;SearchField&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;router&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useRouter&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pathname&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;usePathname&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;searchText&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setSearchText&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="na"&gt;searchTextTimeout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;ReturnType&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;setTimeout&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;searchTextTimeoutRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useRef&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;ReturnType&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;setTimeout&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;searchTextTimeout&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;clearTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;searchTextTimeoutRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// this is just a debounce so we don't update query params on every keystroke&lt;/span&gt;
    &lt;span class="nx"&gt;searchTextTimeoutRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;router&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;!!&lt;/span&gt;&lt;span class="nx"&gt;searchText&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;pathname&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/?heading=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;searchText&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;pathname&lt;/span&gt;
      &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;searchText&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;searchTextTimeoutRef&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;router&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;pathname&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;
        &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text-black&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
        &lt;span class="nx"&gt;onChange&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;setSearchText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;
      &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What Are the Advantages of React Server Components?
&lt;/h2&gt;

&lt;p&gt;Using server components means your JavaScript bundle size will not scale with the size of your codebase. Couple this with the fact that server components don’t require hydration and you get a shortened time-to-interactive and a reduction in page load times.&lt;/p&gt;

&lt;p&gt;That reduced bundle size means client machines don’t need to download as much JavaScript. Additionally, executing JavaScript on the server is often more efficient for the end-user than executing it on the client (especially for mobile devices). Both of these points lead to improved user experiences for Next.js v13 and up.&lt;/p&gt;

&lt;p&gt;Another interesting point is that executing fetches on the server can allow developers to more easily leverage caching. Next.js already &lt;a href="https://nextjs.org/docs/app/building-your-application/data-fetching/fetching-caching-and-revalidating"&gt;handles caching out-of-the-box&lt;/a&gt; and I’m curious to see if the wider adoption of RSC reduces the need to combine React with solutions like &lt;a href="https://www.apollographql.com/docs/apollo-server/"&gt;Apollo Server&lt;/a&gt; and &lt;a href="https://www.apollographql.com/docs/react/"&gt;Apollo Client&lt;/a&gt;. While there are other benefits to these tools, RSC could provide similar caching behavior without the need to invest in a GraphQL solution.&lt;/p&gt;

&lt;p&gt;Combining React Server Components and &lt;code&gt;Suspense&lt;/code&gt; (along with traditional SSR) should also go a long way toward &lt;a href="https://web.dev/case-studies/economic-times-inp"&gt;improving interaction to next paint (INP) for Next.js applications&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;📣 &lt;strong&gt;Important&lt;/strong&gt;: As state above, RedwoodJS very recently added RSC support. That team also emphasized the overlap in functionality between RSC and GraphQL.&lt;/p&gt;

&lt;h2&gt;
  
  
  What are the Disadvantages of React Server Components?
&lt;/h2&gt;

&lt;p&gt;The main disadvantage of React Server Components, in my mind, is the significant increase in complexity.&lt;/p&gt;

&lt;p&gt;A portion of this complexity should fade as React developers get accustomed to the new patterns necessitated by RSC. But as currently implemented, I &lt;em&gt;do&lt;/em&gt; think RSC reduces the overall ergonomics of React – &lt;strong&gt;especially on any page with multiple nested client/server components and contexts.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Another disadvantage lies with RSC support. Since Next.js is the only framework supporting React Server Components, &lt;strong&gt;you’re stuck with it for now&lt;/strong&gt;. And since Next.js works best when deployed to Vercel, some might also argue that narrows your choice of hosting providers.&lt;/p&gt;

&lt;p&gt;Outside of JavaScript frameworks, there is a known &lt;a href="https://nextjs.org/docs/app/building-your-application/styling/css-in-js"&gt;issue with CSS-in-JS&lt;/a&gt; libraries including &lt;code&gt;emotion&lt;/code&gt; and &lt;code&gt;styled-components&lt;/code&gt;. That problem isn’t insurmountable, but will complicate incremental adoption for large, established code bases. Poor server component support by other libraries will also have a similar effect.&lt;/p&gt;

&lt;h2&gt;
  
  
  Are React Server Components Production Ready?
&lt;/h2&gt;

&lt;p&gt;My answer here would be a solid &lt;strong&gt;“yes, but…”&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;If you’re using Next.js or plan to use Next.js, then &lt;strong&gt;React Server Components are production ready&lt;/strong&gt;. There are definitely rough edges in the Next.js implementation, but I don’t think that’s a show stopper.&lt;/p&gt;

&lt;p&gt;That being said, I suspect that greenfield projects will have an easier time adopting RSC since they can pick and choose compatible libraries. Established codebases may find it very time consuming to update their existing code to cooperate with RSCs, especially given the dearth of libraries supporting it.&lt;/p&gt;

&lt;p&gt;ℹ️ &lt;em&gt;Sidenote: If you want more insight on this, check out &lt;a href="https://nickmccurdy.com/arewerscyet/"&gt;Are we RSC yet?&lt;/a&gt;.&lt;br&gt;
&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;If you are not using Next.js and do not plan to, I would personally advise against implementing RSC into production code until support has improved.&lt;/p&gt;

&lt;h2&gt;
  
  
  Open Questions About React Server Components
&lt;/h2&gt;

&lt;p&gt;In writing this article, I began forming some questions that I don’t yet have the answer to. These include:&lt;/p&gt;

&lt;ul&gt;
    &lt;li&gt;How much will the combination of &lt;a href="https://youtu.be/qOQClO3g8-Y?si=PkSs-h0D9stWjfvO"&gt;React Forget&lt;/a&gt; and React Server Components improve React performance overall? Will they impact or compliment one another at all?&lt;/li&gt;
    &lt;li&gt;How will React Server Components work with &lt;a href="https://expo.dev/"&gt;Expo&lt;/a&gt; and React Native?&lt;/li&gt;
    &lt;li&gt;How does &lt;a href="https://turbo.build/pack"&gt;Turbopack&lt;/a&gt; fit into this? Will it bring RSC functionality to other frameworks?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you know anything about these topics, please feel free to let me know! I’d love to learn from you.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;h2&gt;Further Resources and Special Thanks&lt;/h2&gt;
&lt;/h2&gt;

&lt;p&gt;Thanks for sticking with me until the end of a long article! Here are a few additional resources I came across if you’d like to learn more about React Server Components:&lt;/p&gt;

&lt;ul&gt;
    &lt;li&gt;
&lt;a href="https://remix.run/blog/remix-v2"&gt;Remix v2&lt;/a&gt; (blog post)&lt;/li&gt;
    &lt;li&gt;&lt;a href="https://vercel.com/blog/understanding-react-server-components"&gt;Understanding React Server Components&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href="https://youtu.be/tJY1zrW35q8?si=T5BfTwMXuEo921NQ"&gt;Qwik vs. React Server Components w/ Miško Hevery: Servers, Signals, and Performance&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href="https://www.mux.com/blog/what-are-react-server-components"&gt;Everything I wish I knew before moving 50,000 lines of code to React Server Components&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href="https://app-router.vercel.app/context"&gt;Next.js client context demo&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href="https://www.contentful.com/blog/react-server-components-concepts-and-patterns/"&gt;React Server Components: Concepts and Patterns&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I’d also like to extend my thanks to multiple people who provided feedback and pointed me toward resources while writing this article. Thank you for the help and I hope this was an enjoyable read!&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Learn more about how The Gnar &lt;a href="https://www.thegnar.com/software-development/react-development-js"&gt;builds React applications&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>react</category>
      <category>nextjs</category>
      <category>typescript</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Creating Passkey Authentication in a Rails 7 Application</title>
      <dc:creator>Royce Threadgill</dc:creator>
      <pubDate>Wed, 27 Mar 2024 17:39:10 +0000</pubDate>
      <link>https://dev.to/thegnarco/creating-passkey-authentication-in-a-rails-7-application-4a15</link>
      <guid>https://dev.to/thegnarco/creating-passkey-authentication-in-a-rails-7-application-4a15</guid>
      <description>&lt;p&gt;If you're looking to allow your users to log into your Rails 7 app with passkeys, well searched.&lt;/p&gt;

&lt;p&gt;This article is specifically about passkeys, which are a form of passwordless login that replaces passwords with secure, per-device keys (the eponymous passkeys) which are handled by the device's operating system.&lt;/p&gt;

&lt;p&gt;As of this writing, up-to-date Windows, macOS, iOS, and Android all support passkeys. Linux users with the correct browser or browser extension can also use passkeys. &lt;/p&gt;

&lt;p&gt;This widespread support is due to the standard called FIDO2, which covers two components: WebAuthn and CTAP2. WebAuthn handles communication between a relying party (your app) and a credential provider (we'll use Bitwarden's Passwordless.dev), while CTAP2 handles key management and communication between the browser and the operating system. &lt;/p&gt;

&lt;p&gt;We won't need to worry about either of these standards, since using Bitwarden as a passkey provider means we can use their open-source library to handle the details.&lt;/p&gt;

&lt;h2&gt;
  
  
  Rails 7 Passkey Authentication Example
&lt;/h2&gt;

&lt;p&gt;Now that we have a concept for what passkey auth is and how it works, let's implement it in a Rails app. You can find the repo for our example at &lt;a href="https://github.com/JackVCurtis/devise-passkeys-example" rel="noopener noreferrer"&gt;https://github.com/JackVCurtis/devise-passkeys-example&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;We are going to implement a passwordless strategy using &lt;a href="https://github.com/heartcombo/devise" rel="noopener noreferrer"&gt;Devise&lt;/a&gt;. For the uninitiated: Devise is an authentication gem commonly used in Rails applications. It provides several off-the-shelf &lt;em&gt;strategies&lt;/em&gt; which abstract the core concepts typically encountered in conventional web authentication. &lt;/p&gt;

&lt;p&gt;The core functionality of Devise focuses on the username/password authentication paradigm. Here we will add a custom strategy to Devise using passkeys rather than username/password.&lt;/p&gt;

&lt;p&gt;As we mentioned earlier, Bitwarden (specifically Passwordless.dev) is a passkey provider which manages both sides of the passkey management flow (WebAuthn and CTAP2). The first step of our process is to create an account and an application with Passwordless.dev. &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%2F0xcvlw3masd0sn4mse0q.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%2F0xcvlw3masd0sn4mse0q.png" alt="Passwordless.dev setup screen"&gt;&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%2Fsytrmg5luij06qgtcxoj.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%2Fsytrmg5luij06qgtcxoj.png" alt="Passwordless.dev dashboard"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You will be given three values: An API URL, an API private key, and an API public key. Create a &lt;code&gt;.env&lt;/code&gt; file and provide these values:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

BITWARDEN_PASSWORDLESS_API_URL=
BITWARDEN_PASSWORDLESS_API_PRIVATE_KEY=
BITWARDEN_PASSWORDLESS_API_PUBLIC_KEY=


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Set up your database and run the app locally:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

rails db:create
rails db:migrate
rails s


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Install Devise and associate it with a &lt;code&gt;User&lt;/code&gt; model. I recommend removing the &lt;code&gt;password&lt;/code&gt; field before running the migration; otherwise Devise will create an automatic presence validation on the password, which we will not be using.&lt;/p&gt;

&lt;p&gt;The migration file that Devise generates, after we have removed the &lt;code&gt;password&lt;/code&gt; field, should look something like this:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

# frozen_string_literal: true

class DeviseCreateUsers &amp;lt; ActiveRecord::Migration[7.1]
  def change
    create_table :users, id: :uuid do |t|
   ## Passkey authenticatable
      t.string :email,              null: false, default: ""

   ## Rememberable
      t.datetime :remember_created_at

      ## Trackable
      t.integer  :sign_in_count, default: 0, null: false
      t.datetime :current_sign_in_at
      t.datetime :last_sign_in_at
      t.string   :current_sign_in_ip
      t.string   :last_sign_in_ip

      ## Confirmable
      t.string :unconfirmed_email
      t.string   :confirmation_token
      t.datetime :confirmed_at
      t.datetime :confirmation_sent_at

      t.timestamps null: false
    end

    add_index :users, :email,                unique: true
    add_index :users, :confirmation_token,   unique: true
  end
end


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Install the Bitwarden &lt;a href="https://docs.passwordless.dev/guide/frontend/javascript.html" rel="noopener noreferrer"&gt;Passwordless.dev JS client&lt;/a&gt; using your choice of package manager. The example uses import-maps.&lt;/p&gt;

&lt;p&gt;Add our custom passkey Devise module and strategy under the &lt;code&gt;lib/devise&lt;/code&gt; directory.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;lib/devise/models/passkey_authenticatable.rb&lt;/code&gt;:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

require Rails.root.join('lib/devise/strategies/passkey_authenticatable')

module Devise  
    module Models  
        module PasskeyAuthenticatable  
            extend ActiveSupport::Concern  
        end  
    end
end


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;code&gt;lib/devise/strategies/passkey_authenticatable.rb&lt;/code&gt;:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

module Devise
  module Strategies
    class PasskeyAuthenticatable &amp;amp;lt; Authenticatable
      def valid?
        params[:token]
      end

      def authenticate!
        token = params[:token]
        res = Excon.post(ENV['BITWARDEN_PASSWORDLESS_API_URL'] + '/signin/verify', 
          body: JSON.generate({
            token: token
          }),
          headers: {
            "ApiSecret" =&amp;amp;gt; ENV["BITWARDEN_PASSWORDLESS_API_PRIVATE_KEY"],
            "Content-Type" =&amp;amp;gt; "application/json"
          }
        )

      json = JSON.parse(res.body)
      if json["success"]
        success!(User.find(json["userId"]))
      else
        fail!(:invalid_login)
      end
    end
  end
end

Warden::Strategies.add(:passkey_authenticatable, Devise::Strategies::PasskeyAuthenticatable)


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Generate the Devise controller for sessions and configure your routes.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;config/routes.rb&lt;/code&gt;:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

devise_for :users, controllers: {
  sessions: "users/sessions"
}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Replace &lt;code&gt;app/controllers/users/sessions.rb&lt;/code&gt; with the following:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

# frozen_string_literal: true

class Users::SessionsController &amp;lt; Devise::SessionsController
  before_action :configure_sign_in_params, only: [:create]

  protected

  def configure_sign_in_params
    devise_parameter_sanitizer.permit(:sign_in, keys: [:token])
  end
end


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Create a Stimulus controller at &lt;code&gt;app/javascript/controllers/passwordless_controller.js&lt;/code&gt;:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

import { Controller } from "@hotwired/stimulus"
import { Client } from '@passwordlessdev/passwordless-client';


export default class extends Controller {
  static targets = [ "email" ]

  connect() {
    this.client = new Client({ apiKey: window.VAULT_ENV.BITWARDEN_PASSWORDLESS_API_PUBLIC_KEY });
    this.csrf_token = document.querySelector('meta[name="csrf-token"]').content
  }

  async register() {
    const email = this.emailTarget.value
    const { token: registerToken } = await fetch('/api/registrations', {
      method: 'post',
      headers: {
        'content-type': 'application/json'
      },
      body: JSON.stringify({
        email: email,
      })
    }).then(r =&amp;gt; r.json())

    const { token, error } = await this.client.register(registerToken)

    if (token) {
      await this.verifyUser(token)
    }

    if (error) {
      console.log(error)
    }
  }

  async login() {
    // Generate a verification token for the user.
    const { token, error } = await this.client.signinWithAlias(this.emailTarget.value);

    if (token) {
      await this.verifyUser(token)
    }
  }

  async verifyUser(token) {
    const verifiedUser = await fetch('/users/sign_in', {
      method: 'post',
      headers: {
        'content-type': 'application/json'
      },
      body: JSON.stringify({
        token,
        authenticity_token: this.csrf_token
      })
    }).then((r) =&amp;gt; r.json());

    if (verifiedUser.id) {
      window.location.reload()
    }
  }
}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;And insert the controller in the HTML at &lt;code&gt;app/views/users/new.html.erb&lt;/code&gt;:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

&amp;lt;h1&amp;gt;Sign Up&amp;lt;/h1&amp;gt;

&amp;lt;% if current_user %&amp;gt;
  &amp;lt;h2&amp;gt;You're logged in!&amp;lt;/h2&amp;gt;

  &amp;lt;%= button_to(
          "Log Out",
          destroy_user_session_path,
          method: :delete
        ) %&amp;gt;
&amp;lt;% else %&amp;gt;
  &amp;lt;div data-controller="passwordless"&amp;gt;
    &amp;lt;input data-passwordless-target="email" type="email"&amp;gt;

    &amp;lt;button data-action="click-&amp;gt;passwordless#register"&amp;gt;
      Create Account
    &amp;lt;/button&amp;gt;

    &amp;lt;button data-action="click-&amp;gt;passwordless#login"&amp;gt;
      Log In
    &amp;lt;/button&amp;gt;
  &amp;lt;/div&amp;gt;
&amp;lt;% end %&amp;gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Navigate to your new combined signup/login page and test it out!&lt;/p&gt;

&lt;h2&gt;Contributors:&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/JackVCurtis" rel="noopener noreferrer"&gt;Jack Curtis&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/taylorkearns" rel="noopener noreferrer"&gt;Taylor Kearns&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/jrthreadgill" rel="noopener noreferrer"&gt;Royce Threadgill&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;span&gt;Learn more about how The Gnar&lt;/span&gt; &lt;a href="https://www.thegnar.com/software-development/ruby-on-rails-development" rel="noopener noreferrer"&gt;&lt;span&gt;builds Ruby on Rails applications&lt;/span&gt;&lt;/a&gt;&lt;span&gt;.&lt;/span&gt;&lt;/p&gt;

</description>
      <category>ruby</category>
      <category>rails</category>
      <category>security</category>
      <category>learning</category>
    </item>
    <item>
      <title>DevSecOps 101: What is DevSecOps?</title>
      <dc:creator>Royce Threadgill</dc:creator>
      <pubDate>Sat, 27 Jan 2024 05:49:10 +0000</pubDate>
      <link>https://dev.to/thegnarco/devsecops-101-what-is-devsecops-31il</link>
      <guid>https://dev.to/thegnarco/devsecops-101-what-is-devsecops-31il</guid>
      <description>&lt;h2&gt;
  
  
  DevSecOps vs. DevOps
&lt;/h2&gt;

&lt;p&gt;Though there is overlap between Development Security Operations (DevSecOps) and Development Operations (DevOps), they are two distinct topics.&lt;/p&gt;

&lt;p&gt;So, what is the difference between DevOps and DevSecOps?&lt;/p&gt;

&lt;p&gt;Let's first start by defining DevOps. Roughly speaking, I see DevOps as &lt;strong&gt;the practice of using automation to turn code (“development”) into deployments on real infrastructure (“operations”) automatically&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;On the other hand, we can define Dev_Sec_Ops as the &lt;strong&gt;integration of security tooling into the CI/CD pipeline to enforce security at each stage of the development and release process&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;If you have a CI/CD pipeline, you’re implementing &lt;strong&gt;DevOps&lt;/strong&gt;. If you run Brakeman during CI/CD, you’re implementing &lt;strong&gt;DevSecOps&lt;/strong&gt;. &lt;/p&gt;

&lt;h2&gt;
  
  
  DevSecOps as Company Culture
&lt;/h2&gt;

&lt;p&gt;For a deeper dive, I really like &lt;a href="https://snyk.io/series/devsecops/"&gt;this description of DevSecOps by Snyk&lt;/a&gt;. It's worth noting that Snyk’s DevSecOps definition  along with &lt;a href="https://www.redhat.com/en/topics/devops/what-is-devsecops"&gt;the one from RedHat&lt;/a&gt; both use the word "culture" in characterizing the practice. &lt;/p&gt;

&lt;p&gt;It's not a mistake or a hyperbole; embracing DevSecOps demands moving beyond the old "set it and forget it" mentality. You are probably &lt;strong&gt;already adopting DevSecOps at some level&lt;/strong&gt;; you just may not be doing a satisfactory job yet.&lt;/p&gt;

&lt;p&gt;Adopting DevSecOps means &lt;strong&gt;embracing the additional responsibility that software security requires&lt;/strong&gt;. It means using - and understanding - the relevant DevSecOps tools for each stage of the security lifecycle.&lt;/p&gt;

&lt;p&gt;It's a bit of a paradigm shift; many software devs still consider security outside of their job description, and fairly so. But as security moves more and more into the software development lifecycle (which is now fairly married to the deployment lifecycle), it is necessary that all parties involved in that lifecycle be conversant with the security implications of their work. Christophe Limpalair at Spacelift &lt;a href="https://spacelift.io/blog/what-is-devsecops"&gt;puts it nicely&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"...most people view security as an annoyance and as something that adds extra friction to the development and deployment process. Instead of accepting that and putting security on the back burner, DevSecOps brings security back to the forefront by saying “you know what, security is critical and must be part of our process, so let’s make it fit in as painlessly as possible."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It's an elephant, but once we break it down we should see that each bite is manageable.&lt;/p&gt;

&lt;p&gt;The US Department of Defense (DoD) does a very nice job of visualizing the DevSecOps lifecycle:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0h51b6r6cnoc922fmatp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0h51b6r6cnoc922fmatp.png" alt="DevSecOps Distinct Lifecycle Phases and Philosophies" width="800" height="393"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Read more in the &lt;a href="https://dodcio.defense.gov/Portals/0/Documents/Library/DoDEnterpriseDevSecOpsStrategyGuide.pdf"&gt;DoD Enterprise DevSecOps Strategy Guide&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;There are many similar diagrams online, but I find the DoD's to be the most clear and consumable.&lt;/p&gt;

&lt;p&gt;So what do each of these steps mean? Let's break them down a bit.&lt;/p&gt;

&lt;h2&gt;
  
  
  Plan: Threat Analysis
&lt;/h2&gt;

&lt;p&gt;List all assets that need to be protected (user devices, servers, developer devices), the actors they need to be protected from (ransomware gangs, “hacktivists”, advanced persistent threats like North Korea’s Lazarus Group), and the vulnerabilities those actors could exploit (unauthorized data access, server access, cross-site scripting, compromised SSL certificates). &lt;/p&gt;

&lt;p&gt;A threat analysis plan is often articulated in a &lt;a href="https://www.sciencedirect.com/topics/computer-science/threat-analysis"&gt;threat analysis chart&lt;/a&gt; like this one from &lt;a href="https://www.sciencedirect.com/book/9780123704801/network-analysis-architecture-and-design"&gt;Network Analysis, Architecture, and Design&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr1dylqp1i6zdtvkzxm7e.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr1dylqp1i6zdtvkzxm7e.jpg" alt="Thread analysis table example" width="535" height="388"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Develop: Secure Coding; Security as Code
&lt;/h2&gt;

&lt;p&gt;Ensure that developers know the best practices around their language and framework (Ruby on Rails, React Native, etc.). Automate your security tooling using infrastructure as code.&lt;/p&gt;

&lt;p&gt;Learn more by reading Snyk’s article on &lt;a href="https://snyk.io/learn/secure-coding-practices/"&gt;secure coding practices&lt;/a&gt; and GitLab’s &lt;a href="https://about.gitlab.com/blog/2020/03/12/how-to-security-as-code/"&gt;security-as-code guide&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Build: Static Application Security Testing (SAST)
&lt;/h2&gt;

&lt;p&gt;Analyze your source code for any recognizable weaknesses with &lt;a href="https://snyk.io/learn/application-security/static-application-security-testing/"&gt;SAST tools&lt;/a&gt;. This is language- and framework-specific. SAST runs as part of your build pipeline. Examples:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/rubysec/bundler-audit"&gt;Bundler Audit&lt;/a&gt; (ruby)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://brakemanscanner.org/"&gt;Brakeman&lt;/a&gt; (Ruby on Rails)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.npmjs.com/auditing-package-dependencies-for-security-vulnerabilities"&gt;NPM Audit&lt;/a&gt; (JavaScript)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://sobelow.io/"&gt;Sobelow&lt;/a&gt; (Phoenix Framework)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Test: Dynamic Application Security Testing (DAST)
&lt;/h2&gt;

&lt;p&gt;Actively scan for exploitable weaknesses. &lt;a href="https://snyk.io/learn/application-security/dast-dynamic-application-security-testing/"&gt;DAST&lt;/a&gt; software runs against a test environment. This is language-agnostic testing, usually for web applications. &lt;a href="https://www.zaproxy.org"&gt;ZAP&lt;/a&gt; is a good example of DAST software.&lt;/p&gt;

&lt;h2&gt;
  
  
  Test: Penetration ("Pen") Testing
&lt;/h2&gt;

&lt;p&gt;Bring on dedicated specialists to attempt to hack a test environment with a variety of tools, and provide a report of vulnerabilities after doing so. There are many consulting firms which provide pen test services. &lt;/p&gt;

&lt;p&gt;Snyk’s &lt;a href="https://snyk.io/blog/penetration-testing-for-developers/"&gt;penetration testing guide&lt;/a&gt; is a good introduction to penetration testing concepts.&lt;/p&gt;

&lt;h2&gt;
  
  
  Release: Digitally Sign
&lt;/h2&gt;

&lt;p&gt;Use &lt;a href="https://en.wikipedia.org/wiki/Code_signing"&gt;cryptographic signatures&lt;/a&gt; to verify that source code is &lt;strong&gt;unmodified from the version published&lt;/strong&gt; by the source domain/package maintainer. If someone were to manipulate the file containing your program to include malicious code, this would be detected by a signature verification tool.&lt;/p&gt;

&lt;h2&gt;
  
  
  Deliver: Secure Transfer
&lt;/h2&gt;

&lt;p&gt;Ensure that your code is deployed via an &lt;strong&gt;encrypted and secure connection&lt;/strong&gt;. Most cloud platforms will do this automatically. &lt;/p&gt;

&lt;p&gt;If using containers, ensure that the container registry and container image is secure using tools like &lt;a href="https://github.com/quay/clair"&gt;Clair&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Deploy: Security Config, Security Scan
&lt;/h2&gt;

&lt;p&gt;Define and configure your infrastructure using infrastructure as code tools like AWS CDK.&lt;/p&gt;

&lt;p&gt;Scan that infrastructure with a tool like &lt;a href="https://github.com/aquasecurity/cloudsploit"&gt;Cloudsploit&lt;/a&gt; (Multicloud), &lt;a href="https://github.com/cyberark/KubiScan"&gt;KubiScan&lt;/a&gt; (Kubernetes), or &lt;a href="https://aws.amazon.com/inspector/"&gt;Amazon Inspector&lt;/a&gt; (AWS). Like SAST tools, the tools you choose will be specific to your platform.&lt;/p&gt;

&lt;h2&gt;
  
  
  Operate: Security Patch
&lt;/h2&gt;

&lt;p&gt;Continually patch your code to the latest version. Use tools like &lt;a href="https://docs.github.com/en/code-security/dependabot/dependabot-version-updates"&gt;dependabot&lt;/a&gt; and &lt;a href="https://github.com/rubysec/bundler-audit"&gt;bundler-audit&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Monitor: Security Audit, Security Monitor
&lt;/h2&gt;

&lt;p&gt;Continuously assess the security of your application's data. This step is best considered &lt;strong&gt;after you understand what it is you are monitoring&lt;/strong&gt; (see everything above). This sounds obvious, but plenty of devs hook up a monitoring system without really knowing what is being monitored.&lt;/p&gt;

&lt;p&gt;In addition to monitoring the software processes, a security audit should also review the user processes, i.e. how users access and share data within and outside of the application.&lt;/p&gt;

&lt;p&gt;Shout out to &lt;a href="https://wazuh.com/"&gt;Wazuh&lt;/a&gt; as a comprehensive monitoring/dashboard tool, while AWS has &lt;a href="https://aws.amazon.com/security-hub/"&gt;Amazon Security Hub&lt;/a&gt;. For auditing there’s a wide range of options, but TechTarget’s &lt;a href="https://www.techtarget.com/searchcio/definition/security-audit"&gt;security audit guide&lt;/a&gt; is a good place to start.&lt;/p&gt;

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

&lt;p&gt;Investing in a legitimate DevSecOps strategy at your organization is certainly no small investment, in time or resources. But it is a very surmountable challenge. &lt;/p&gt;

&lt;p&gt;If we break the process down as above and become comfortable with each step of the process, we can &lt;strong&gt;weave these security practices into the existing fabric of our software lifecycle&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;And if there is any question about whether this venture is worth the time or resources it demands, I would point you to research into the &lt;a href="https://thehackernews.com/2023/12/cost-of-data-breach-report-2023.html?m=1#key-finding-2-using-a-devsecops-approach-deploying-incident-response-teams-and-using-security-and-ai-automation-produced-large-savings"&gt;savings a company will realize&lt;/a&gt; by investing in a DevSecOps strategy.&lt;/p&gt;

&lt;p&gt;If you're interested in jumping into DevOps or DevSecOps, or if you are already on your way and would like some feedback, &lt;a href="https://www.thegnar.com/contact"&gt;shoot us a line&lt;/a&gt;. We'd love to hear from you.&lt;/p&gt;

&lt;p&gt;Contributors:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/JackVCurtis"&gt;Jack Curtis&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/taylorkearns"&gt;Taylor Kearns&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/jrthreadgill"&gt;Royce Threadgill&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Learn more about how The Gnar &lt;a href="https://www.thegnar.com/software-development"&gt;builds software&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>security</category>
      <category>devops</category>
      <category>cybersecurity</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Gnarly Learnings from December 2023</title>
      <dc:creator>Royce Threadgill</dc:creator>
      <pubDate>Thu, 11 Jan 2024 06:01:14 +0000</pubDate>
      <link>https://dev.to/thegnarco/gnarly-learnings-from-december-2023-3mga</link>
      <guid>https://dev.to/thegnarco/gnarly-learnings-from-december-2023-3mga</guid>
      <description>&lt;p&gt;&lt;em&gt;At The Gnar, we’re always reading, watching, and listening in order to keep our skills sharp and our perspectives fresh. Every month, we round up our learnings and publish them in a Gnarly Learnings blog post.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;In December 2023, we learned about &lt;strong&gt;AI applications in finance roles&lt;/strong&gt;, strategies for &lt;strong&gt;top converting landing pages&lt;/strong&gt;, and &lt;strong&gt;Static Hermes&lt;/strong&gt;. We also pondered how the combination of &lt;strong&gt;Rails and Hotwire&lt;/strong&gt; can be used to replace some of the functionality previously provided only by &lt;strong&gt;React&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Business Applications of AI
&lt;/h2&gt;

&lt;p&gt;McKinsey recently published a &lt;a href="https://www.mckinsey.com/capabilities/strategy-and-corporate-finance/our-insights/gen-ai-a-guide-for-cfos"&gt;brief generative AI guide for CFOs&lt;/a&gt; on how and where to integrate gen AI solutions into finance roles.&lt;/p&gt;

&lt;p&gt;The writers posit that generative AI can help automate tedious tasks, augment productivity, and accelerate data extraction, potentially increasing the velocity of innovation.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Gen AI can be an important tool for value creation. CFOs should strive to be gen AI enablers, not gatekeepers, and make sure that strategically critical initiatives rapidly and continually receive necessary resources.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Meg McFarlane recently published a Think With Google article discussing the benefits of &lt;a href="https://www.thinkwithgoogle.com/intl/en-emea/marketing-strategies/video/youtube-ai-solutions-oak-furnitureland/"&gt;AI-based advertising strategies&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In a single case study, they found a 36% increase in unique reach, 45% drop in cost, and 30% higher average impression frequency per user by switching YouTube ad strategy to lean more heavily on Google’s AI ad optimization.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Where in the past we’d have to do a lot of guesswork to determine the ideal rotation between in-stream and bumper ads, we now let the Google AI decide how often each ad was shown and to whom.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Ecommerce Web Design
&lt;/h2&gt;

&lt;p&gt;In a February 2023 podcast, Logan Grasby discusses the design of &lt;a href="https://unofficialshopifypodcast.com/episodes/logan-grasby-landing-pages-v_ph6SOc"&gt;top converting landing pages&lt;/a&gt;. He goes into detail about the anatomy of a landing page, discussing the purpose of each section and tips for improving marketing performance.&lt;/p&gt;

&lt;p&gt;You can find a summarization of his key points starting around &lt;a href="https://unofficialshopifypodcast.com/episodes/logan-grasby-landing-pages-v_ph6SOc/transcript?t=25m30s"&gt;25:30 in the podcast&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Russ Maschmeyer, product lead for spatial commerce at Shopify, discusses a suite of AI tools called &lt;a href="https://help.shopify.com/en/manual/shopify-admin/productivity-tools/shopify-magic"&gt;Shopify Magic&lt;/a&gt; in a &lt;a href="https://practicalai.fm/246#t=11:20"&gt;recent episode of Practical AI&lt;/a&gt;. The tools can help you with a variety of tasks, from generating product descriptions to &lt;a href="https://huggingface.co/spaces/Shopify/background-replacement"&gt;generating product imagery&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Design
&lt;/h2&gt;

&lt;p&gt;In December of 2023, we came across two sites – &lt;a href="https://www.addifico.com/"&gt;Addifico&lt;/a&gt; and &lt;a href="https://ai-chat.it/en/"&gt;ai-chat&lt;/a&gt; – that present their business and value proposition with eye-catching, interactive websites.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6blvdes6lpdvs6s1ytcr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6blvdes6lpdvs6s1ytcr.png" alt="AI-Chat Screenshots" width="800" height="208"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flpf9pi03inxprjy7y9n9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flpf9pi03inxprjy7y9n9.png" alt="Addifico Screenshots" width="800" height="209"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Mobile App Development
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://hermesengine.dev/"&gt;Hermes&lt;/a&gt;, the JavaScript engine built and maintained by Meta, is already fine-tuned for running fast React Native applications. But in the presentation “&lt;a href="https://youtu.be/q-xKYA0EO-c?si=YcOAE16xz5iycaZa"&gt;Static Hermes: The Next Generation of Hermes&lt;/a&gt;”, Tzvetan Mikov explains how JavaScript can be compiled to achieve huge leaps in performance.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;[&lt;a href="https://youtu.be/q-xKYA0EO-c?si=G_jQWjjURdTcnsPD&amp;amp;t=901"&gt;15:00&lt;/a&gt;] When we started compiling it to native initially, it was the same performance as Hermes…but now as you can see, it is 10x faster when compiled natively…plus we’re certain that there is more headroom. We can probably get to 20x times faster than Hermes.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Tzvetan reports &lt;a href="https://youtu.be/q-xKYA0EO-c?si=G_jQWjjURdTcnsPD&amp;amp;t=901"&gt;10x faster performance over traditional Hermes&lt;/a&gt; in one benchmark.&lt;/p&gt;

&lt;p&gt;He also emphasizes that native compilation unlocks “the possibility of very efficient native platform integration”, estimating that call overhead could be between &lt;a href="https://youtu.be/q-xKYA0EO-c?si=tuksL6QgWJHnb7fr&amp;amp;t=1155"&gt;15x and 18x lower than JSI when calling a native function&lt;/a&gt;. This “zero-cost FFI” ultimately opens up the possibility to implement platform integrations “entirely in JavaScript” with “practically no cost, just as if we were a C program”.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe4vqt8fm2kl6axr7rlq9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe4vqt8fm2kl6axr7rlq9.png" alt="Screenshot from Static Hermes: The Next Generation of Hermes" width="800" height="519"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you’re interested in learning more about Static Hermes, Tzvetan created a blog post discussing &lt;a href="https://tmikov.blogspot.com/2023/09/how-to-speed-up-micro-benchmark-300x.html"&gt;how to speed up a micro-benchmark by 300x using Static Hermes&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;Gaurav Gautam also wrote a blog post about &lt;a href="https://medium.com/@gautam1168/compiling-typescript-to-native-code-0238d69ca582"&gt;compiling TypeScript to native code&lt;/a&gt;, which &lt;a href="https://github.com/facebook/hermes/discussions/1137#discussioncomment-7201539"&gt;Tzvetan discussed in a GitHub thread&lt;/a&gt;. That same GitHub thread yielded this remarkable result from the bunnymark benchmark:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
  &lt;tr&gt;
   &lt;td&gt;Runtime
   &lt;/td&gt;
   &lt;td&gt;FPS
   &lt;/td&gt;
   &lt;td&gt;CPU
   &lt;/td&gt;
   &lt;td&gt;GPU
   &lt;/td&gt;
   &lt;td&gt;Memory
   &lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;C
   &lt;/td&gt;
   &lt;td&gt;90
   &lt;/td&gt;
   &lt;td&gt;68%
   &lt;/td&gt;
   &lt;td&gt;84%
   &lt;/td&gt;
   &lt;td&gt;48 MB
   &lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;Static Hermes
   &lt;/td&gt;
   &lt;td&gt;90
   &lt;/td&gt;
   &lt;td&gt;70%
   &lt;/td&gt;
   &lt;td&gt;84%
   &lt;/td&gt;
   &lt;td&gt;53 MB
   &lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Tech Insights
&lt;/h2&gt;

&lt;p&gt;The tech world evolves incredibly fast, so it’s important to stay on top of new strategies for building applications.&lt;/p&gt;

&lt;p&gt;It’s our belief that the &lt;a href="https://hotwired.dev/"&gt;Hotwire&lt;/a&gt; family of products, including the well-known &lt;a href="https://turbo.hotwired.dev/"&gt;Turbo&lt;/a&gt; and &lt;a href="https://stimulus.hotwired.dev/"&gt;Stimulus&lt;/a&gt;, provide a compelling alternative to React for many types of applications.&lt;/p&gt;

&lt;p&gt;Learn more by reading our blog post, “&lt;a href="https://www.thegnar.com/blog/ruby-on-rails-vs-react-hotwire-turbo-change-the-equation"&gt;Ruby on Rails vs. React: Hotwire + Turbo Change the Equation&lt;/a&gt;”.&lt;/p&gt;

&lt;h2&gt;
  
  
  Gnarly Bytes
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;GraphQL&lt;/strong&gt;: This &lt;a href="https://cheatsheetseries.owasp.org/cheatsheets/GraphQL_Cheat_Sheet.html"&gt;OWASP Cheat Sheet for GraphQL&lt;/a&gt; is a great summary of best practices&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Elixir&lt;/strong&gt;: &lt;a href="https://notes.club/"&gt;Notesclub&lt;/a&gt; is a collection of Elixir Livebooks&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Artificial intelligence&lt;/strong&gt;: &lt;a href="https://ollama.ai/"&gt;Ollama&lt;/a&gt; provides a bunch of AI models you can run locally; Fly.io also created a blog post discussing &lt;a href="https://fly.io/blog/scaling-llm-ollama/"&gt;how to run Ollama models on Fly&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ruby&lt;/strong&gt;: &lt;a href="https://docs.rubyonjets.com/"&gt;Ruby on Jets&lt;/a&gt; is an interesting Ruby serverless framework for building and deploying APIs to AWS lambda&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Libraries&lt;/strong&gt;: We discovered &lt;a href="https://spotlightjs.com/"&gt;Spotlight&lt;/a&gt;, a library pitched as “Sentry for Development” (the library is created by &lt;a href="https://sentry.io/welcome/"&gt;Sentry&lt;/a&gt;), aims to provide a rich debugging experience during development&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Contributors:
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/ngmaloney"&gt;Nick Maloney&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/jrthreadgill"&gt;Royce Threadgill&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Learn more about how The Gnar &lt;a href="https://www.thegnar.com/software-development"&gt;builds software&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>reactnative</category>
      <category>react</category>
      <category>rails</category>
    </item>
    <item>
      <title>Bun JavaScript Runtime First Look</title>
      <dc:creator>Royce Threadgill</dc:creator>
      <pubDate>Tue, 17 Oct 2023 12:00:00 +0000</pubDate>
      <link>https://dev.to/thegnarco/bun-javascript-runtime-first-look-3eon</link>
      <guid>https://dev.to/thegnarco/bun-javascript-runtime-first-look-3eon</guid>
      <description>&lt;p&gt;When I first heard about the Bun project, I honestly thought it was overambitious to the point of obvious failure. You know the projects I’m talking about – the ones that promise the world but never reach fruition, leaving their early adopters to twist in the wind.&lt;/p&gt;

&lt;p&gt;But here we are: &lt;a href="https://bun.sh/blog/bun-v1.0"&gt;Bun 1.0&lt;/a&gt;. And by all accounts, the Bun JavaScript runtime has achieved its original goals.&lt;/p&gt;

&lt;p&gt;In this article, we’re going to discuss Bun’s features, as well as its history and future. Since the Bun team claims that their product is a drop-in replacement for Node.js, we’ll also examine a non-trivial example of replacing Node.js with Bun in a pre-existing project.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Is Bun?
&lt;/h2&gt;

&lt;p&gt;Bun is a new JavaScript runtime developed by a company called &lt;a href="https://github.com/oven-sh"&gt;Oven&lt;/a&gt; (yes, really). The mastermind behind the project is &lt;a href="https://twitter.com/jarredsumner"&gt;Jarred Sumner&lt;/a&gt;, although at this point the company appears to have attracted a small team of developers.&lt;/p&gt;

&lt;p&gt;On an &lt;a href="https://podrocket.logrocket.com/bun"&gt;episode of the PodRocket podcast&lt;/a&gt;, Jarred summarized Bun as:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;[&lt;a href="https://podrocket.logrocket.com/bun?t=130"&gt;2:10&lt;/a&gt;] …an all-in-one JavaScript Runtime. It combines a bundler, a transpiler, a package manager, a script runner, all-in-one in addition to being a runtime. And so it comes with a TypeScript transpiler, JSX transpiler. And all of this is quite a bit faster than anything that exists today.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  How Fast Is Bun?
&lt;/h2&gt;

&lt;p&gt;To be clear, when Jarred says “quite a bit faster”, he means &lt;strong&gt;many times faster&lt;/strong&gt; than other prominent JavaScript runtimes such as &lt;a href="https://nodejs.org/en"&gt;Node.js&lt;/a&gt; and &lt;a href="https://deno.com/"&gt;Deno&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;One benchmark on the &lt;a href="https://bun.sh/"&gt;Bun website&lt;/a&gt; compares the HTTP requests per second processed by Bun, deno.serve(), and Node.js. When comparing Bun vs. Node, Bun processed 5x as many requests per second; when comparing Bun vs. Deno, Bun processed 2x as many requests per second.&lt;/p&gt;

&lt;p&gt;Another benchmark asserts that &lt;code&gt;yarn install&lt;/code&gt; runs 33x slower than &lt;code&gt;bun install&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;Yet another benchmark asserts that tests in a Jest/Babel configuration would run 20x slower than Bun’s test runner. &lt;/p&gt;

&lt;p&gt;Of course, we all know that benchmarks can be misleading. I haven’t personally run the benchmarks provided by Bun, nor have I looked at the code to see if they make sense.&lt;/p&gt;

&lt;p&gt;That being said, these claims have been &lt;a href="https://snyk.io/blog/javascript-runtime-compare-node-deno-bun/"&gt;backed up by some third-party testing&lt;/a&gt;. On our end, one of our talented software engineers has already looked into Bun and was very impressed with its speed compared to Node.js (more on his experience later).&lt;/p&gt;

&lt;h2&gt;
  
  
  How Is Bun So Fast?
&lt;/h2&gt;

&lt;p&gt;According to Jarred Sumner, there are really two major contributors to Bun’s comparative speed: (1) use of the &lt;strong&gt;JavaScriptCore engine&lt;/strong&gt; over V8 and (2) use of the &lt;strong&gt;Zig language&lt;/strong&gt; for building the transpiler and runtime.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/javascriptcore"&gt;JavaScriptCore&lt;/a&gt; is the JavaScript engine built by Apple for Safari, while V8 is the engine built by Google for Chromium-based web browsers. I can’t speak much to their relative speed, nor have I heard Jarred or the Bun team discuss the benefits of JavaScriptCore in great detail.&lt;/p&gt;

&lt;p&gt;The choice to use &lt;a href="https://ziglang.org/"&gt;Zig&lt;/a&gt;, on the other hand, seems like a slam dunk for performance. Bun documentation claims the use of &lt;a href="https://bun.sh/docs/cli/run"&gt;Zig results in a 4x faster startup time for Bun vs. Node&lt;/a&gt; when running scripts.&lt;/p&gt;

&lt;p&gt;Frankly, I hadn’t heard of Zig prior to learning about Bun several months ago. It’s a relatively young language, but seems well-suited to building products like Bun based on Jarred’s statements:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;[&lt;a href="https://podrocket.logrocket.com/bun?t=252"&gt;4:12&lt;/a&gt;] Zig is really good for this type of systems programming to do it as quickly as possible while also not going crazy because it's a low level systems' language with manual memory management. By its nature, without garbage collection, things are a little bit harder, but Zig does a really good job of making it easier than it would be otherwise in C.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Is Bun Production Ready?
&lt;/h2&gt;

&lt;p&gt;By releasing version 1.0, the team is declaring that &lt;strong&gt;Bun is production ready&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://redcircle.com/shows/javascript-jabber/ep/254b0cc8-9629-4dcd-9866-6c554cd35ed9"&gt;Speaking on the JavaScript Jabber podcast&lt;/a&gt;, Jarred stated there are already “companies using it in production” with a “decent amount of traffic in some cases”.&lt;/p&gt;

&lt;p&gt;That’s not to say that Bun is flawless. The host of that podcast – which aired May 30, 2023, only three months before 1.0 – mentioned that he had encountered several issues when attempting to use Bun.&lt;/p&gt;

&lt;p&gt;Of course, Jarred assured the host that progress was being made on many known issues.&lt;/p&gt;

&lt;p&gt;My personal opinion? Bun is ready for production use, so long as you’re willing to tackle the weird compatibility issues and edge cases that inevitably arise when using bleeding edge technology. &lt;/p&gt;

&lt;p&gt;So the question really should be: do the pros of adopting Bun outweigh these inconveniences?&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Should You Switch to Bun?
&lt;/h2&gt;

&lt;p&gt;There are a few benefits to Bun that jump out at me:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Do you have a giant test suite that takes a long time to run?&lt;/strong&gt; Adopting Bun will reduce the time to run that test suite, which can save CI/CD dollars and developer time.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Do you run serverless JavaScript functions?&lt;/strong&gt; Switching over to Bun will reduce cold start and execution times.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Do you like TypeScript and want it to work out-of-the-box?&lt;/strong&gt; Bun can directly execute &lt;code&gt;.ts&lt;/code&gt; and &lt;code&gt;.tsx&lt;/code&gt; files with no additional configuration.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Do you want to reduce your dependencies and simplify bundling?&lt;/strong&gt; Bun can probably help. If you like languages such as Go that have rich standard libraries, Bun brings some of that ethos to the JavaScript world by including its own bundler and package manager.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Aside from directly reducing costs via faster execution time, Bun’s performance has the potential to &lt;strong&gt;increase developer productivity&lt;/strong&gt;. By reducing the amount of time that developers spend waiting on test suites to run, for instance, you also reduce the time needed to iterate on those tests.&lt;/p&gt;

&lt;p&gt;And lastly, the improved developer experience of having an all-in-one product shouldn’t be underestimated. How much time have you invested messing with &lt;code&gt;webpack&lt;/code&gt; configurations or troubleshooting a Jest upgrade? Grouping these disparate elements of JavaScript development within a single, batteries-included framework should help mitigate these sorts of problems.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Is the Future of Bun?
&lt;/h2&gt;

&lt;p&gt;Jarred envisions a future in which Bun facilitates fast server-side JavaScript rendering. This certainly agrees with the recent shift back to server-side rendering, as embodied by the changes introduced in &lt;a href="https://nextjs.org/blog/next-13#server-components"&gt;Next.js 13&lt;/a&gt; and the creation of &lt;a href="https://react.dev/blog/2023/03/22/react-labs-what-we-have-been-working-on-march-2023#react-server-components"&gt;React Server Components&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Jarred discusses this concept in his PodRocket interview:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;[&lt;a href="https://podrocket.logrocket.com/bun?t=420"&gt;7:00&lt;/a&gt;] Bun will eventually have a production transpiler API/bundling API. And I think what people will do with that is they will actually bundle and transpile in network requests. And so for that, you need it to be really fast. But the reason why I think people will do that is because it's going to enable a lot of new kinds of plugin systems where you have this dynamic code generation and you do server-side JavaScript rendering. Similarly you do server-side HTML rendering. But if you can embed the data…it'll let people delete a bunch of code.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This use-case for Bun seems supportive of the so-called &lt;a href="https://www.patterns.dev/posts/islands-architecture"&gt;islands architecture&lt;/a&gt; (a phrase popularized most recently by &lt;a href="https://astro.build/"&gt;Astro&lt;/a&gt;). If you’re unfamiliar with this concept, the main gist is: &lt;strong&gt;since web pages are largely static content, only ship JavaScript when and where you need it.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;More specifically, I can imagine a scenario in which a user submits data to a server and the server responds by creating data-specific JavaScript which is bundled and sent back to the client as part of an interactive page element. &lt;/p&gt;

&lt;p&gt;That sort of client/server interaction ensures you’re only sending the JavaScript you need to the client – and that the JavaScript you send has the smallest footprint possible.&lt;/p&gt;

&lt;p&gt;I don’t think the value proposition for this sort of system is lost on Jarred and Bun, either. In his interview with JavaScript Jabber, Jarred mentions that he aims to eventually offer “really fast edge hosting for Javascript specialized cloud” as a commercial product.&lt;/p&gt;

&lt;p&gt;And in his PodRocket interview, he also highlights that Oven wants to create a version of Bun specifically for edge computing:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;[&lt;a href="https://podrocket.logrocket.com/bun?t=967"&gt;16:07&lt;/a&gt;] …one of the things we'll focus on is specifically [a] version of Bun for the edge. It will have single file deploys. And it'll do this really efficiently because it'll use a binary bundling format to turn it all into one compact file.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So based on these comments, I anticipate we’ll see Oven create something like &lt;a href="https://deno.com/deploy"&gt;Deno Deploy&lt;/a&gt; for the Bun ecosystem in the not-too-distant future.&lt;/p&gt;

&lt;h2&gt;
  
  
  Is Bun a Drop-In Replacement for Node.js?
&lt;/h2&gt;

&lt;p&gt;Whenever a new project bills itself as a “drop-in replacement” for an existing tool, I’m immediately suspicious. In my experience, there’s usually some big issue that pops up soon after adopting these sorts of solutions.&lt;/p&gt;

&lt;p&gt;Luckily, one of our Gnarnians has already looked into this! &lt;a href="https://github.com/alxjrvs"&gt;Alex Jarvis&lt;/a&gt; recently converted his &lt;a href="https://github.com/RANDSUM/randsum-ts"&gt;randsum-ts&lt;/a&gt; library from Node.js to Bun and generously agreed to share his takeaways from the experience.&lt;/p&gt;

&lt;p&gt;Firstly, let’s establish that his library itself isn’t extremely complicated. Alex states that it “has no databases, no HTTP calls, just logic and exhaustively-thorough TypeScript”. &lt;/p&gt;

&lt;p&gt;But if you look at the library, I’m sure you’ll quickly notice that it’s a pretty far cry from a “hello, world” application. This is a production-ready, pre-existing project, making it a fantastic opportunity for putting Bun’s “drop-in replacement” claims to the test.&lt;/p&gt;

&lt;p&gt;More importantly, this wasn’t just an experiment for Alex. &lt;strong&gt;He saw legitimate benefits in Bun&lt;/strong&gt; and wanted to leverage those benefits in his project:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I have played with a number of different workflows and build scripts – &lt;code&gt;rollup&lt;/code&gt; , &lt;code&gt;parcel&lt;/code&gt;, straight-up &lt;code&gt;tsc&lt;/code&gt; – and had lost my head a few times working my way around &lt;code&gt;cjs&lt;/code&gt; vs. &lt;code&gt;esm&lt;/code&gt;. What I wanted was a little existential stability in my workflow – something to put my back against. When I heard of Bun’s built-in (lightning fast) bundler &lt;em&gt;and&lt;/em&gt; saw it operated with first-level TypeScript support, I decided to try my hand at converting from a &lt;code&gt;yarn/node&lt;/code&gt; backed project to being one run on &lt;code&gt;bun&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If you’ve lived in the JavaScript world for a while, this is probably a familiar sentiment. My hope is Bun helps to smooth out developer experience by avoiding these sorts of headaches.&lt;/p&gt;

&lt;p&gt;Replacing Node.js with Bun was fairly straightforward, though Alex mentioned it did involve “a small amount of re-architecturing”. To be fair, I expected that changing the JavaScript runtime for an existing project would be an outright nightmare; &lt;a href="https://github.com/RANDSUM/randsum-ts/pull/543"&gt;his experience&lt;/a&gt; seemed positively seamless by comparison.&lt;/p&gt;

&lt;p&gt;He did, however, highlight testing as a pain point during adoption:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The biggest headache was the test suite, where a few useful / mission critical things are not yet implemented when  compared to the modern &lt;code&gt;jest&lt;/code&gt; suite (while I can live without &lt;code&gt;expect.arrayContaining&lt;/code&gt;, it is a slightly more annoying existence) With  some creative dependency injection and well-architected tests, this was easy to get around.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That said, Alex seemed genuinely impressed with the results once he was done. He stated his test suite and build script ran “instantly” after the switch, which serves as yet more validation of Bun’s impressive performance claims.&lt;/p&gt;

&lt;p&gt;Alex additionally pointed out an interesting benefit to Bun’s TypeScript support that hadn’t occurred to me:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;There is already a plugin ecosystem developing (making it easy for my &lt;code&gt;build&lt;/code&gt; script to also spit out a &lt;code&gt;d.ts&lt;/code&gt; file). Being able to REPL one of my TypeScript files natively has really opened up my debugging possibilities.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So is Bun a drop-in replacement for Node.js? I’ll let you come to your own conclusion on that. But from my perspective, Alex’s experience is a solid endorsement of that claim.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>bunjs</category>
      <category>typescript</category>
      <category>zig</category>
    </item>
    <item>
      <title>Anything Can be Used for File Storage if You Use it Wrongly Enough</title>
      <dc:creator>Nick Maloney</dc:creator>
      <pubDate>Mon, 31 Jul 2023 19:32:43 +0000</pubDate>
      <link>https://dev.to/thegnarco/anything-can-be-used-for-file-storage-if-you-use-it-wrongly-enough-1hb</link>
      <guid>https://dev.to/thegnarco/anything-can-be-used-for-file-storage-if-you-use-it-wrongly-enough-1hb</guid>
      <description>&lt;p&gt;I recently came across the most excellent blog post “&lt;a href="https://xeiaso.net/blog/anything-message-queue"&gt;Anything can be a message queue if you use it wrongly enough&lt;/a&gt;” (It is a fun read and I highly recommend checking it out). After reading it, it dawned on me that I recently created an abomination that essentially did the inverse, I used a “message queue” (if you would please indulge me in considering Redis one) as a semi-ephemeral file storage service. &lt;em&gt;Please do not use this for production, you have been warned!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;In building out a proof-of-concept for some fun with Machine Learning (ML) audio processing, I needed a quick, easy way to store processed files. One of the challenges was that files required multiple processing steps across different services so local file storage was not the best option. The ideal solution would be to use something like S3, but given this was purely a proof of concept (POC) and being a lazy developer, I was looking for something simple and easy. The processing was happening via the most excellent &lt;a href="https://dramatiq.io/"&gt;Dramatiq&lt;/a&gt; library, which uses Redis. This got me thinking that Redis is actually pretty well suited for this use case; it stores binary data well, is relatively fast, is quite stable AND by using expiration param in &lt;code&gt;SET&lt;/code&gt; it would handle house cleaning of old files. &lt;/p&gt;

&lt;p&gt;For the sake of this example, we’ll create a simple Flask application that handles &lt;code&gt;POST&lt;/code&gt; requests for file uploads and saves them to Redis, using a &lt;code&gt;sha256&lt;/code&gt; of the payload as the key:&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="nn"&gt;flask&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Flask&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;send_file&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;base64&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;datetime&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;hashlib&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;logging&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;magic&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;mimetypes&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;re&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;redis&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Flask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;__name__&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setLevel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DEBUG&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

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

&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;methods&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"POST"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="nb"&gt;file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;files&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'file'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="n"&gt;encoded_file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;base64&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;b64encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
  &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;hashlib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sha256&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;encoded_file&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;hexdigest&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="n"&gt;expiration_time&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;timedelta&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hours&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;24&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;encoded_file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;expiration_time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;total_seconds&lt;/span&gt;&lt;span class="p"&gt;()))&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The example above will save the data to Redis and return the key. It sets the file to expire in 24 hours. &lt;/p&gt;

&lt;p&gt;To retrieve the files, we’ll use a few utilities for attempting to determine mime-type/extension of the saved files and use Flask’s &lt;code&gt;send_file&lt;/code&gt; method for the response.&lt;/p&gt;

&lt;p&gt;Using that endpoint, &lt;code&gt;GET&lt;/code&gt; requests made to &lt;code&gt;/sha_of_file&lt;/code&gt; will return the file, with the appropriate mime-type/extension.&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="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/&amp;lt;key&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;methods&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"GET"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;show&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="n"&gt;encoded_file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;encoded_file&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;"File not found"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;404&lt;/span&gt;
  &lt;span class="n"&gt;decoded_file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;base64&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;b64decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;encoded_file&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;mimetype&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;magic&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;from_buffer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;decoded_file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mime&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;file_extension&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mimetypes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;guess_extension&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mimetype&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;temp_filename&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;.&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;file_extension&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;

  &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nb"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;temp_filename&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;'wb'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;temp_file&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;temp_file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;decoded_file&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;send_file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;temp_filename&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mimetype&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;mimetype&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;as_attachment&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;…and there you have it. This ended up working quite well for an audio processing pipeline, quickly and easily being able to send audio files exceeding 100mb. The Dramatiq library has the concept of storing results between tasks baked in, but doesn’t solve for sending results back over the wire to the browser. Again, do not use this in a production app, but I would absolutely use this technique again for future POC projects.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>How Generative AI Impacts Software Development at The Gnar</title>
      <dc:creator>Ethan Fertsch</dc:creator>
      <pubDate>Thu, 27 Jul 2023 18:50:26 +0000</pubDate>
      <link>https://dev.to/thegnarco/how-generative-ai-impacts-software-development-at-the-gnar-4662</link>
      <guid>https://dev.to/thegnarco/how-generative-ai-impacts-software-development-at-the-gnar-4662</guid>
      <description>&lt;p&gt;It would be difficult to make it through a day without hearing, reading, or seeing something related to Artificial Intelligence (AI). While the &lt;em&gt;concept&lt;/em&gt; of AI is nothing &lt;a href="https://en.wikipedia.org/wiki/2001:_A_Space_Odyssey_(film)"&gt;new&lt;/a&gt;, there is a new form of AI that has generated a lot of buzz: &lt;a href="https://en.wikipedia.org/wiki/Generative_artificial_intelligence#:~:text=Generative%20artificial%20intelligence%20or%20generative,media%20in%20response%20to%20prompts."&gt;generative AI&lt;/a&gt;. In the simplest terms, generative AI is a system that can generate content (text, images, code) in response to user input. This type of system has the potential to significantly impact many industries - from healthcare to legal to the software development industry. &lt;/p&gt;

&lt;p&gt;As a consultancy, we are constantly evaluating new tools and technologies that will help us to efficiently deliver high-quality work to our clients. At this point, it’s important to highlight that we are &lt;em&gt;consumers&lt;/em&gt; of AI, not &lt;em&gt;creators&lt;/em&gt; of it. That is to say, we won’t be building the next big natural language processing tool, but we may use one to help us write a test for a certain piece of code. There are a number of AI platforms that are already proliferating in the software design and development space, and our team is happy to experiment with them. &lt;/p&gt;

&lt;h2&gt;
  
  
  Generative AI at The Gnar
&lt;/h2&gt;

&lt;p&gt;We asked our team what tools they were using to make their process more efficient and there were two at the top of the list: GitHub Copilot and ChatGPT. But we’re more than just an engineering shop - we also offer a full suite of design services and there are a number of tools that our designers use to improve the quality of their deliverables, such as MagiCopy. &lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://github.com/features/copilot"&gt;GitHub Copilot&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;GitHub Copilot is marketed as an “AI pair programmer”. It is a paywalled service ($10/mo) that was released in the fall of 2021. The tool plugs into a text editor, such as VSCode, and can make real-time code suggestions based on input from the developer. &lt;/p&gt;

&lt;h4&gt;
  
  
  How We Are Using It
&lt;/h4&gt;

&lt;p&gt;The majority of our team is using it to increase velocity and reduce the monotony of repetitive tasks. GitHub Copilot is anticipatory so if you begin typing some code it will try to automatically complete a word, line of code, or function. This may sound simple, but when we look at time spent on the keyboard over the course of an engagement, all of those reserved keystrokes have a cost savings associated with them. As a more practical example, we are firm believers in the value of comprehensive test coverage in the applications that we build. However, writing tests is time-consuming, and by nature, there is quite a bit of repetitive setup for each file. GitHub Copilot really shines here because it can handle much of the setup and can even write complete tests &lt;em&gt;for&lt;/em&gt; you (though we strongly caution against accepting these at face value, more on this later). &lt;/p&gt;

&lt;p&gt;Here’s what one of our team members had to say when asked how they were using AI to make their life easier:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;“Github Copilot! I much prefer the in-editor interface to anything prompt-based and while it often spits out nonsense for business logic it excels at test suites. [...]”&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  Why We Are Using It
&lt;/h4&gt;

&lt;p&gt;GitHub Copilot has been an easy choice for our team for a number of reasons. &lt;a href="https://github.com/"&gt;GitHub&lt;/a&gt; is a credible and familiar platform. We use it almost exclusively for storing and managing code. Moreover, GitHub was &lt;a href="https://news.microsoft.com/2018/06/04/microsoft-to-acquire-github-for-7-5-billion/"&gt;acquired&lt;/a&gt; by Microsoft back in 2018 so we know that they have strong financial and technical backing, which is promising for the tool’s longevity. It also integrates directly with &lt;a href="https://code.visualstudio.com/"&gt;VSCode&lt;/a&gt; (another Microsoft brainchild) which is a favorite text editor of many of our team members and therefore has a low barrier to entry. This combined with the time and effort savings during development makes it really attractive for developers and clients alike. However, it’s not all kittens and rainbows (more on this in the &lt;em&gt;Challenges with Generative AI&lt;/em&gt; section). &lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://openai.com/blog/chatgpt"&gt;ChatGPT&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Chat Generative Pre-Trained Transformer (affectionately referred to as &lt;a href="https://openai.com/blog/chatgpt"&gt;ChatGPT&lt;/a&gt;), is a natural language processing tool which is really just a fancy way of saying “an AI-based chatbot” which was developed by &lt;a href="https://openai.com/"&gt;OpenAI&lt;/a&gt; and released in late 2022. A user can engage with ChatGPT via prompts and receive human-like responses. It is not software specific/focused - you can ask it what the meaning of life is, to build a keto meal plan for you, or to convert a Ruby method to Python. The world is your oyster. &lt;/p&gt;

&lt;h4&gt;
  
  
  How We Are Using It
&lt;/h4&gt;

&lt;p&gt;ChatGPT is used less often than tools like GitHub Copilot but it did come up a number of times when discussing Generative AI with our team. It can be a great replacement for specific, simple questions about things like code syntax. With these types of questions, it’s often faster to use a tool like this than scouring blog posts or forums like &lt;a href="https://stackoverflow.com/"&gt;Stack Overflow&lt;/a&gt;. One team member finds utility in ChatGPT as a “&lt;a href="https://en.wikipedia.org/wiki/Rubber_duck_debugging"&gt;rubber duck&lt;/a&gt;” when they are trying to solve a problem.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;“It's better curated than Stack Overflow. Sometimes AI is my "rubber duck". The process of forming a coherent question for ChatGPT helps me better understand the problem I'm working on.”&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  Why We Are Using It
&lt;/h4&gt;

&lt;p&gt;The primary reason for using ChatGPT is that it is super easy to get started. Getting started requires nothing more than an account with OpenAI and a question. Developers spend a significant amount of time learning, researching, and reading documentation. There are certain situations in which tools like ChatGPT cut down on reading so more time can be spent writing code.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://www.figma.com/community/plugin/1184110746118034942/MagiCopy-%E2%80%93-AI-Text-Generator"&gt;MagiCopy&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;MagiCopy is a &lt;a href="https://www.figma.com/"&gt;Figma&lt;/a&gt; plug-in. Figma is the choice interface design application of our design team. The plug-in can generate copy based on project type, company, desired tone, and content type. &lt;/p&gt;

&lt;h4&gt;
  
  
  How We Are Using It
&lt;/h4&gt;

&lt;p&gt;Our design team uses MagiCopy to generate meaningful text for wireframes and mockups.&lt;/p&gt;

&lt;h4&gt;
  
  
  Why We Are Using It
&lt;/h4&gt;

&lt;p&gt;We use MagiCopy because it provides a more realistic feel to design deliverables, which translates to a more delightful experience for our clients and their stakeholders. Without a tool like this, there would be a time investment in generating realistic copy in the specific context of a project, or worse, we’d see a lot more &lt;a href="https://en.wikipedia.org/wiki/Lorem_ipsum"&gt;lorem ipsum&lt;/a&gt;. Additionally, as with many of the other tools mentioned in this article, MagiCopy integrates with a platform that we know and love, lowering the barrier to entry.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;“There's a constant struggle when designing of whether we need copy before design or design before copy. In instances when copy isn't written yet or fully approved for a website, designers will use "Lorem Ipsum" as placeholder text in their work, but it doesn't do a good job of representing how real copy will flow on a page. Not only that, but telling the brand story is important to elevating the design, so if the copy isn't final I write my own to help make a design feel more real. Having the ability to generate copy for my designs with a tool like MagiCopy I not only save time, but no longer have to default to "Lorem ipsum" when copy is not available or I'm unable to write it myself.”&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;While these tools have the potential to enhance the design and development process, we also envision a number of broad challenges that Generative AI poses to the industry. With great power comes great responsibility…&lt;/p&gt;

&lt;h3&gt;
  
  
  Challenges with Generative AI
&lt;/h3&gt;

&lt;p&gt;While these recent advances in Generative AI are remarkable and the conceptual utility is there, there are a number of challenges that have prevented widespread adoption at The Gnar. &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Overreliance.&lt;/strong&gt; At this stage, the output from these tools is often flat-out &lt;strong&gt;wrong&lt;/strong&gt; and still requires a careful eye to ensure what is being returned is usable and correct. Could ChatGPT build a web app for you? Probably. Would it be production-ready or usable out of the box? Almost certainly not. The responses from the tools are only as good as the level of detail and prompts that you provide to them. As users of ChatGPT and GitHub Copilot, we’re still required to take everything with a grain of salt and double-check the output - it would be a mistake to become overly reliant on these tools to perform our work for us.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Misrepresentation.&lt;/strong&gt; This applies less to our current team but more to potential hires (and the potential hires of our clients). It is becoming increasingly easier to misrepresent a skillset using tools like this. A design candidate could use an image-based Generative AI platform to create and submit a portfolio of work just as easily as a software developer could use ChatGPT or GitHub Copilot to complete a coding assessment. While we like to assume the best, this is a serious ethical concern that will need to be monitored as these tools become more advanced.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Intellectual Property.&lt;/strong&gt; Generative AI tools are built and continuously trained on the data that they are fed. The more data they process, in theory, the more helpful they become. The work we produce is always considered the intellectual property of our clients. When it comes to the impact of using a plugin like Github Copilot, that intellectual property is being used as input data to improve that tool. There’s a big question mark in our minds about whether companies are aware of this and more importantly, if they’re &lt;em&gt;okay with it&lt;/em&gt;. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Impact on Cost and Quality.&lt;/strong&gt; While we don’t think AI is going to cost us our jobs or take over the world, there are specific concerns about how we work with AI that will impact software development:&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;“In its current form, generative AI could be a repeat of the "low code" experiments of the 1990s: I think a lot of non-technical decision makers will think it can heavily automate software development. They will pay consultancies to ‘use AI’ to cheaply write their apps. The apps won't work very well and they need more money to fix them than it would have taken to write them properly the first time.”&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The takeaway from all of this? Generative AI tools are very powerful and have the potential to greatly impact the software development industry. We are still in the very early days of technology of this type and there are a lot of challenges and ethical concerns that prevent us from diving in head first. However, we plan to continue to evaluate these tools and to use them to boost productivity by assisting with repetitive tasks - &lt;strong&gt;but only when and where it makes sense to do so, without compromising quality&lt;/strong&gt;. &lt;/p&gt;

</description>
      <category>ai</category>
      <category>githubcopilot</category>
      <category>chatgpt</category>
      <category>softwaredevelopment</category>
    </item>
    <item>
      <title>Meet The Gnarnians: Erik</title>
      <dc:creator>Ethan Fertsch</dc:creator>
      <pubDate>Fri, 21 Jul 2023 14:34:56 +0000</pubDate>
      <link>https://dev.to/thegnarco/meet-the-gnarnians-erik-894</link>
      <guid>https://dev.to/thegnarco/meet-the-gnarnians-erik-894</guid>
      <description>&lt;h2&gt;
  
  
  &lt;strong&gt;Gnarnian [nahr-nee-uhn] &lt;em&gt;noun&lt;/em&gt;:&lt;/strong&gt; An employee of The Gnar Company
&lt;/h2&gt;

&lt;p&gt;In our &lt;em&gt;Meet The Gnarnians&lt;/em&gt; series, we unravel the stories, experiences, and passions that drive the folks on our talented team at The Gnar. Through a collection of interview-style questions, you’ll get a sneak peek into daily life on our fully-remote team, some epic origin stories, and answers to some questions as weird and wonderful as our team itself.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Basics&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Where are you located?&lt;/strong&gt; Chicago, IL&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What is your Role?&lt;/strong&gt; Software Engineer&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When did you join the Gnar?&lt;/strong&gt; December 2021&lt;/p&gt;

&lt;h2&gt;
  
  
  What’s the last song you played or are listening to right now?
&lt;/h2&gt;

&lt;p&gt;Ballroom Blitz. The Crucial Taunt version.&lt;/p&gt;

&lt;h2&gt;
  
  
  What was your career journey up until joining the Gnar?
&lt;/h2&gt;

&lt;p&gt;I started out in networking and servers and took a nice long education break before coming back as a developer, then worked for a nonprofit for a long time before joining an early stage startup. In general, I've just stayed curious by doing what I want and things have worked out pretty well.&lt;/p&gt;

&lt;h2&gt;
  
  
  In your role, do you have any specializations or areas of focus you really enjoy doing?
&lt;/h2&gt;

&lt;p&gt;I really enjoy designing software systems: Thinking in terms of architectural patterns, how best to model the real world concepts involved, how you can play to the strengths of your chosen language, and so on. There's something very satisfying about producing things that are easy to understand and do what you expect, and the design phase is generally where you're closest to what stakeholders really want in the end, whether it's a feature or a greenfield app.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is your ideal work/desk set-up?
&lt;/h2&gt;

&lt;p&gt;A standing desk, (not a convertible, which I will not convert) with good light, decently-but-not-maximally clackety keyboard, enough screen real estate to read a browser tab and a terminal (dark text, light background) at the same time. Zsh, tmux, vim, asdf. Good headphones for music, which I will feel weird about wearing for meetings. Coffee, for which my taste is utterly gauche.&lt;/p&gt;

&lt;h2&gt;
  
  
  What has your experience been at Gnar?
&lt;/h2&gt;

&lt;p&gt;I've gotten to be involved in a long standing state project with a really interesting back end architecture, which is maybe not the first thing you picture working on going into a Rails consulting shop. It's very satisfying to see features through not just to completion but all the way through fine tuning them in the real world. The best answer here though is the people--- folks at Gnar are really welcoming and supportive.&lt;/p&gt;

&lt;h2&gt;
  
  
  What advice would you give someone who is just starting out in your industry?
&lt;/h2&gt;

&lt;p&gt;Don't let popular opinion tell you what success looks like for you. There are a ton of great jobs out there in the world, so keep an open mind and figure out what makes you happy and keeps you curious. And it's ok to go slow.&lt;/p&gt;

&lt;h2&gt;
  
  
  Outside of work, what do you enjoy doing?
&lt;/h2&gt;

&lt;p&gt;Music. Listening to it, playing it, recording it, talking about it, etc.&lt;/p&gt;

&lt;h2&gt;
  
  
  If you weren’t in your current role, what would you be doing?
&lt;/h2&gt;

&lt;p&gt;Live sound guy.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Learn more about &lt;a href="https://www.thegnar.com/about"&gt;The Gnar and our team&lt;/a&gt;.&lt;/em&gt; &lt;/p&gt;

</description>
      <category>softwareengineering</category>
      <category>culture</category>
      <category>community</category>
    </item>
    <item>
      <title>Gnarly Learnings from June 2023</title>
      <dc:creator>Ethan Fertsch</dc:creator>
      <pubDate>Fri, 30 Jun 2023 19:18:49 +0000</pubDate>
      <link>https://dev.to/thegnarco/gnarly-learnings-from-june-2023-3lk8</link>
      <guid>https://dev.to/thegnarco/gnarly-learnings-from-june-2023-3lk8</guid>
      <description>&lt;p&gt;At The Gnar we are always reading, watching, and listening in order to keep our skills sharp and our perspectives fresh. Here are some of the resources we learned from this month.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://www.totaltypescript.com/typescript-5-2-new-keyword-using"&gt;Understanding TypeScript’s Newest Keyword: &lt;code&gt;using&lt;/code&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;One of the reasons I appreciate TypeScript’s place in the Node.js ecosystem is how they approach incoming JS proposals, and how eager and willing they are to bring these new advancements to the ecosystem early enough to get excited, but late enough to trust it. &lt;code&gt;using&lt;/code&gt; is a reflection of the upcoming ECMA TC39 proposal for more explicit handling of “temporary” connections (DB executions, file handlers, etc), and since it has made it far enough to be eventually adopted into JS proper, TypeScript is more than happy to prepare me by adopting it now. &lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://spin.atomicobject.com/2023/06/05/javascript-backtick-strings-wrong/"&gt;Backtick Strings vs &lt;code&gt;URLSearchParams&lt;/code&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;The title of the article linked above is a _little _clickbait-y but hey, I read it and I am glad I did. The React documentation provides an example of building a URL string to query an API using template literals or “backtick strings”. However, as the author correctly points out, this is not the best tool for the job. Template literals will _work _but aren’t made for creating serializable strings. For that, we have &lt;code&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams"&gt;URLSearchParams&lt;/a&gt;&lt;/code&gt;, which will properly escape parameter values, among other things.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://github.com/rails/rails/pull/48339"&gt;Removing Active Storage Objects via Form POST&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Gone are the days of needing to reach for clunky conditionals to massage Active Storage-related params when using direct uploads! Previously, to remove an Active Storage object from a record it would need to be explicitly set to &lt;code&gt;nil&lt;/code&gt;. This was all well and good until you would try to do it on form submission and receive an error because the passed value in the &lt;code&gt;params&lt;/code&gt; was &lt;em&gt;actually&lt;/em&gt; an empty string. Now, setting the value to &lt;code&gt;nil&lt;/code&gt; or an empty string yields the same result. 🎉&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://mattbrictson.com/blog/gateway-pattern"&gt;The Gateway Pattern&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Especially on large projects, my services directory admittedly ends up looking a bit like a junk drawer. So, if you’re like me and wrap all external API communication in service objects, you’ll be glad you found this article. A gateway is used to formalize the boundary between your app’s domain and an external API, translating between the two. This is more specific and nuanced than how I typically use service objects, and helps clean up the codebase.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://shopify.engineering/improving-the-developer-experience-with-ruby-lsp"&gt;Shopify’s Ruby LSP in VSCode&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;I was shopping around for a simpler way to auto-format my Ruby code with minimal extensions and config. In my research I came across the Ruby LSP (Language Server Protocol) created by Shopify, who maintain one of the most well-known commercial Rails monoliths. This article describes the LSP and the VSCode integration of it, but it also includes a nice dive into LSPs in general.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://survey.stackoverflow.co/2023/#overview"&gt;Stack Overflow Releases Their 2023 Developer Survey&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Here at The Gnar, we love a good developer survey. Admittedly, this is my first experience with the one released by Stack Overflow (SO). There are a bunch of interesting takeaways but I loved reading about how Postgres is now the most loved, admired, &lt;em&gt;and&lt;/em&gt; popular relational database (this is true here at The Gnar too - we have a whole &lt;a href="https://www.thegnar.com/blog/history-tracking-with-postgres"&gt;series&lt;/a&gt; on some of its lesser-known features). SO also includes a new section on the prevalence of AI search tools where ChatGPT (unsurprisingly) is sitting pretty with 83% of professional respondents leveraging it in the past year.&lt;/p&gt;

&lt;h2&gt;
  
  
  Contributors:
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/alxjrvs"&gt;Alex Jarvis&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/efertsch"&gt;Ethan Fertsch&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/mikestone14"&gt;Mike Stone&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://github.com/taylorkearns"&gt;Taylor Kearns&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Learn more about how The Gnar &lt;a href="https://www.thegnar.com/software-development"&gt;builds software&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>ruby</category>
      <category>architecture</category>
      <category>rails</category>
    </item>
    <item>
      <title>Metaprogramming in Ruby: Advanced Level</title>
      <dc:creator>Ethan Fertsch</dc:creator>
      <pubDate>Fri, 30 Jun 2023 19:15:47 +0000</pubDate>
      <link>https://dev.to/thegnarco/metaprogramming-in-ruby-advanced-level-5e83</link>
      <guid>https://dev.to/thegnarco/metaprogramming-in-ruby-advanced-level-5e83</guid>
      <description>&lt;p&gt;&lt;em&gt;This post is the third in a series focused on the application of Ruby metaprogramming. If you’re just starting to learn about metaprogramming, “&lt;a href="https://www.thegnar.com/blog/metaprogramming-in-ruby-beginner-level"&gt;Metaprogramming in Ruby: Beginner Level&lt;/a&gt;” is a great place to get started. For those seeking a more practical example of metaprogramming, check out “&lt;a href="https://www.thegnar.com/blog/metaprogramming-in-ruby-intermediate-level"&gt;Metaprogramming in Ruby: Intermediate Level&lt;/a&gt;”. In this article, we’ll discuss how a few popular Ruby gems make use of metaprogramming to solve everyday programming problems.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;When you go out for a walk, do you bring a paddle with you? Of course not! A paddle would be pointless. It’s only when you’re up the creek that a paddle is invaluable. &lt;/p&gt;

&lt;p&gt;So too it goes with metaprogramming.&lt;/p&gt;

&lt;p&gt;Welcome to the final installment of our &lt;em&gt;Metaprogramming in Ruby&lt;/em&gt; series. Up to this point, we’ve been going through relatively simple, handcrafted Ruby metaprogramming examples. In the &lt;a href="https://www.thegnar.com/blog/metaprogramming-in-ruby-beginner-level"&gt;beginner level post&lt;/a&gt;, we discussed how to use Ruby &lt;code&gt;define_method&lt;/code&gt; and &lt;code&gt;send&lt;/code&gt; to dynamically create methods. And in the &lt;a href="https://www.thegnar.com/blog/metaprogramming-in-ruby-intermediate-level"&gt;intermediate level post&lt;/a&gt;, we put those concepts into practice with a slightly more practical example.&lt;/p&gt;

&lt;p&gt;This time, we’re going to look at how metaprogramming is used in the wild by diving into the code underlying popular Ruby gems. The libraries we’ll be looking at are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/heartcombo/devise"&gt;devise&lt;/a&gt;: An authentication library designed for Rails&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/thoughtbot/factory_bot"&gt;factory_bot&lt;/a&gt;: A fixtures replacement&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/rspec"&gt;rspec&lt;/a&gt;: A testing library&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Ruby metaprogramming in the &lt;code&gt;devise&lt;/code&gt; gem
&lt;/h2&gt;

&lt;p&gt;Of the gems we’re analyzing today, this is probably the most straightforward example of metaprogramming in Ruby. &lt;/p&gt;

&lt;p&gt;When initially setting up devise in your project, you generally run &lt;code&gt;rails generate devise MODEL&lt;/code&gt;, where &lt;code&gt;MODEL&lt;/code&gt; is the name of your devise model. So if you wanted your users to have a model name of &lt;code&gt;User&lt;/code&gt; , you’d run &lt;code&gt;rails generate devise User&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;To make this example a little clearer, let’s assume you run &lt;code&gt;rails generate devise Gnarnian&lt;/code&gt;, which will make your devise model &lt;code&gt;Gnarnian&lt;/code&gt; (that’s what we call ourselves at Gnar).&lt;/p&gt;

&lt;p&gt;After migrating, you’ll get access to a slew of helpers created by the generator, including some nifty url and path helpers like &lt;code&gt;new_gnarnian_session_path&lt;/code&gt;, &lt;code&gt;gnarnian_session_path&lt;/code&gt;, and &lt;code&gt;destroy_gnarnian_session_path&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;But that’s not all! As you add third-party sign in methods to your &lt;code&gt;app/models/gnarnian.rb&lt;/code&gt; file, you additionally get access to &lt;em&gt;those&lt;/em&gt; path helpers! For instance, you can add &lt;code&gt;:omniauthable, omniauth_providers: [:google_oauth2]&lt;/code&gt; to your devise modules to set up Google sign in; that should provide you with &lt;code&gt;user_google_oauth2_omniauth_authorize_path&lt;/code&gt; for redirecting users to Google sign in.&lt;/p&gt;

&lt;p&gt;At its core, this is made possible by &lt;a href="https://github.com/heartcombo/devise/blob/ec0674523e7909579a5a008f16fb9fe0c3a71712/lib/devise/controllers/url_helpers.rb"&gt;lib/devise/controllers/url_helpers.rb&lt;/a&gt;, which uses &lt;code&gt;define_method&lt;/code&gt; to create the path helpers, like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# lib/devise/controllers/url_helpers.rb&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;generate_helpers!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;routes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;routes&lt;/span&gt; &lt;span class="o"&gt;||=&lt;/span&gt; &lt;span class="k"&gt;begin&lt;/span&gt;
    &lt;span class="n"&gt;mappings&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Devise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mappings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;values&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="ss"&gt;:used_helpers&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;flatten&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;uniq&lt;/span&gt;
    &lt;span class="no"&gt;Devise&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;URL_HELPERS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;mappings&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="n"&gt;routes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;module_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;actions&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:url&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;path_or_url&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="n"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
        &lt;span class="n"&gt;action&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;_"&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt;
        &lt;span class="nb"&gt;method&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="ss"&gt;:"&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="si"&gt;}#{&lt;/span&gt;&lt;span class="n"&gt;module_name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="ss"&gt;_&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;path_or_url&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="ss"&gt;"&lt;/span&gt;

        &lt;span class="n"&gt;define_method&lt;/span&gt; &lt;span class="nb"&gt;method&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;resource_or_scope&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
          &lt;span class="n"&gt;scope&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Devise&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Mapping&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find_scope!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;resource_or_scope&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
          &lt;span class="n"&gt;router_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Devise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mappings&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;router_name&lt;/span&gt;
          &lt;span class="n"&gt;context&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;router_name&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nb"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;router_name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;_devise_route_context&lt;/span&gt;
          &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="si"&gt;}#{&lt;/span&gt;&lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;_&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;module_name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;_&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;path_or_url&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;end&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Ruby metaprogramming in the &lt;code&gt;factory_bot&lt;/code&gt; gem
&lt;/h2&gt;

&lt;p&gt;Factory Bot is considered by many to be a must-have tool for testing Rails projects. The &lt;code&gt;factory_bot&lt;/code&gt; gem actually uses metaprogramming concepts in a couple of places, but here we’ll focus on the use of traits, callbacks, and the &lt;code&gt;evaluator&lt;/code&gt; argument.&lt;/p&gt;

&lt;p&gt;For the sake of example, let’s say you have &lt;code&gt;articles&lt;/code&gt; and &lt;code&gt;authors&lt;/code&gt; tables. Each &lt;code&gt;Article&lt;/code&gt; belongs to a specific &lt;code&gt;Author&lt;/code&gt; and each &lt;code&gt;Author&lt;/code&gt; can have many &lt;code&gt;Articles&lt;/code&gt;. You could create a basic factory like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# spec/factories/authors.rb&lt;/span&gt;

&lt;span class="no"&gt;FactoryBot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;define&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;factory&lt;/span&gt; &lt;span class="ss"&gt;:article&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s2"&gt;"MyString"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s2"&gt;"MyText"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;published&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="kp"&gt;false&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;author&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But Factory Bot also provides callbacks that allow you to execute code for a given strategy. So if you’d like to run code after calling &lt;code&gt;create&lt;/code&gt;, then you could write something like &lt;code&gt;after(:create) do &amp;lt;something&amp;gt;&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Now let’s assume an &lt;code&gt;Author&lt;/code&gt; has written &lt;code&gt;Books&lt;/code&gt; that you need to access in your tests; to generate &lt;code&gt;Books&lt;/code&gt; written by your &lt;code&gt;Author&lt;/code&gt;, you could make something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# spec/factories/authors.rb&lt;/span&gt;

&lt;span class="no"&gt;FactoryBot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;define&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;factory&lt;/span&gt; &lt;span class="ss"&gt;:author&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;transient&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;book_names&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'The Color of Magic'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="nb"&gt;name&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s1"&gt;'Terry Pratchett'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="n"&gt;trait&lt;/span&gt; &lt;span class="ss"&gt;:has_books&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;after&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:create&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;author&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;evaluator&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="no"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;evaluator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;book_names&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;book_name&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
        &lt;span class="n"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:book&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;name: &lt;/span&gt;&lt;span class="n"&gt;book_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;author: &lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice that you can use &lt;code&gt;evaluator&lt;/code&gt; to access transient properties. So in your test, you could write &lt;code&gt;create(:author, :has_books, book_names: ['Equal Rites', 'Mort'])&lt;/code&gt;, which would create two books with the provided names and the same &lt;code&gt;Author&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This flexible and powerful behavior is facilitated by the Ruby &lt;code&gt;define_method&lt;/code&gt; and &lt;code&gt;method_missing&lt;/code&gt; blocks found in &lt;a href="https://github.com/thoughtbot/factory_bot/blob/4a37cb64090d6354a719e8c5ef73653f5d242017/lib/factory_bot/evaluator.rb"&gt;factory_bot/lib/factory_bot/evaluator.rb&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# lib/factory_bot/evaluator.rb (selected snippets)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;method_missing&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;method_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="vi"&gt;@instance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;respond_to?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;method_name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="vi"&gt;@instance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;method_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;else&lt;/span&gt;
    &lt;span class="no"&gt;SyntaxRunner&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;method_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;define_attribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;instance_methods&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kp"&gt;false&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;include?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;private_instance_methods&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kp"&gt;false&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;include?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;undef_method&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="n"&gt;define_method&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="vi"&gt;@cached_attributes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;key?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="vi"&gt;@cached_attributes&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;
      &lt;span class="vi"&gt;@cached_attributes&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;instance_exec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For more detailed examples, check out the &lt;a href="https://github.com/thoughtbot/factory_bot/blob/main/GETTING_STARTED.md#with-callbacks"&gt;getting started docs&lt;/a&gt;!&lt;/p&gt;

&lt;h2&gt;
  
  
  Ruby metaprogramming in the &lt;code&gt;rspec-core&lt;/code&gt; gem
&lt;/h2&gt;

&lt;p&gt;At long last, we’ve arrived at our final Ruby metaprogramming example: the 800-lb gorilla that is &lt;code&gt;rspec&lt;/code&gt; and its many associated gems. Of the three examples discussed here, &lt;code&gt;rspec&lt;/code&gt; uses metaprogramming most extensively. This makes some intuitive sense given that we’re talking about a full-fledged testing framework.&lt;/p&gt;

&lt;p&gt;That being said, the code and documentation for &lt;code&gt;rspec&lt;/code&gt; is generally clear and descriptive, making it a fantastic repository for learning about metaprogramming techniques. In &lt;a href="https://github.com/rspec/rspec-core/blob/8caecca0b9b299ccbaa5c7ea5dd885ab42cd57d3/lib/rspec/core/dsl.rb"&gt;rspec-core/lib/rspec/core/dsl.rb&lt;/a&gt;, for example, the implementation is spelled out in code comments.&lt;/p&gt;

&lt;p&gt;Let’s create an example for illustration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;RSpec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;describe&lt;/span&gt; &lt;span class="s2"&gt;"posts/index"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;type: :view&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="s2"&gt;"renders a list of posts"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="c1"&gt;# ...something&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c1"&gt;# Creates RSpec::ExampleGroups::PostsIndex&lt;/span&gt;
&lt;span class="c1"&gt;# $ RSpec::ExampleGroups::PostsIndex.describe&lt;/span&gt;
&lt;span class="c1"&gt;# $  =&amp;gt; RSpec::ExampleGroups::ArticlesIndex::Anonymous&lt;/span&gt;
&lt;span class="c1"&gt;# $ RSpec::ExampleGroups::PostsIndex.examples&lt;/span&gt;
&lt;span class="c1"&gt;# $  =&amp;gt; [#&amp;lt;RSpec::Core::Example "renders a list of posts"&amp;gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For each spec, RSpec will construct the &lt;code&gt;RSpec::ExampleGroups::Example&lt;/code&gt; subclass. Note as well that &lt;code&gt;RSpec::ExampleGroups::Examples::Anonymous.describe&lt;/code&gt; will increment the id appended to the end of &lt;code&gt;RSpec::ExampleGroups::Examples::Anonymous&lt;/code&gt; (e.g., &lt;code&gt;Anonymous_2&lt;/code&gt;, &lt;code&gt;Anonymous_3&lt;/code&gt;, &lt;code&gt;Anonymous_4&lt;/code&gt;, etc.).&lt;/p&gt;

&lt;p&gt;When you create your spec file with &lt;code&gt;RSpec.describe&lt;/code&gt;, behind the scenes your arguments are being passed to &lt;code&gt;dsl.rb&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# rspec-core/lib/rspec/core/dsl.rb&lt;/span&gt;
&lt;span class="c1"&gt;# In this case, `args` are `["posts/index", {:type=&amp;gt;:view}]`&lt;/span&gt;
&lt;span class="c1"&gt;# and `example_group_block` is the block of code representing your spec &lt;/span&gt;

&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;RSpec&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;__send__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:define_method&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;example_group_block&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="n"&gt;group&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;RSpec&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Core&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;ExampleGroup&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;__send__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;example_group_block&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="no"&gt;RSpec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;world&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;record&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;group&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;group&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Those arguments are then passed to &lt;code&gt;RSpec::Core::ExampleGroup&lt;/code&gt;, which determines if the block is top-level and, if so, defines a new &lt;code&gt;ExampleGroup&lt;/code&gt; subclass.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# rspec-core/lib/rspec/core/example_group.rb (selected snippets)&lt;/span&gt;
&lt;span class="c1"&gt;# In this case, `name` is `:describe`&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;define_example_group_method&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;metadata&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{})&lt;/span&gt;
  &lt;span class="n"&gt;idempotently_define_singleton_method&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;example_group_block&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="n"&gt;thread_data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;RSpec&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Support&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;thread_local_data&lt;/span&gt;
    &lt;span class="n"&gt;top_level&lt;/span&gt;   &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="no"&gt;ExampleGroup&lt;/span&gt;

    &lt;span class="n"&gt;registration_collection&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;top_level&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;thread_data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:in_example_group&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
          &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="s2"&gt;"Creating an isolated context from within a context is "&lt;/span&gt; &lt;span class="p"&gt;\&lt;/span&gt;
                &lt;span class="s2"&gt;"not allowed. Change `RSpec.&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;` to `&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;` or "&lt;/span&gt; &lt;span class="p"&gt;\&lt;/span&gt;
                &lt;span class="s2"&gt;"move this to a top-level scope."&lt;/span&gt;
        &lt;span class="k"&gt;end&lt;/span&gt;

        &lt;span class="n"&gt;thread_data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:in_example_group&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;
        &lt;span class="no"&gt;RSpec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;world&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;example_groups&lt;/span&gt;
      &lt;span class="k"&gt;else&lt;/span&gt;
        &lt;span class="n"&gt;children&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="k"&gt;begin&lt;/span&gt;
      &lt;span class="n"&gt;description&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;shift&lt;/span&gt;
      &lt;span class="n"&gt;combined_metadata&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dup&lt;/span&gt;
      &lt;span class="n"&gt;combined_metadata&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;merge!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pop&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;last&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;is_a?&lt;/span&gt; &lt;span class="no"&gt;Hash&lt;/span&gt;
      &lt;span class="n"&gt;args&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;combined_metadata&lt;/span&gt;

      &lt;span class="n"&gt;subclass&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;registration_collection&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;example_group_block&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;ensure&lt;/span&gt;
      &lt;span class="n"&gt;thread_data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:in_example_group&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;top_level&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="no"&gt;RSpec&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Core&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;DSL&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;expose_example_group_alias&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;subclass&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;registration_collection&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;example_group_block&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;subclass&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Class&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;subclass&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set_it_up&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;registration_collection&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;example_group_block&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;subclass&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;module_exec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;example_group_block&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;example_group_block&lt;/span&gt;

  &lt;span class="c1"&gt;# The LetDefinitions module must be included _after_ other modules&lt;/span&gt;
  &lt;span class="c1"&gt;# to ensure that it takes precedence when there are name collisions.&lt;/span&gt;
  &lt;span class="c1"&gt;# Thus, we delay including it until after the example group block&lt;/span&gt;
  &lt;span class="c1"&gt;# has been eval'd.&lt;/span&gt;
  &lt;span class="no"&gt;MemoizedHelpers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;define_helpers_on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;subclass&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="n"&gt;subclass&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The returned subclass, in this case, would be &lt;code&gt;RSpec::ExampleGroups::PostsIndex&lt;/code&gt;. Calling &lt;code&gt;subclass.module_exec&lt;/code&gt; will step through the &lt;code&gt;example_group_block&lt;/code&gt; and define &lt;code&gt;Example&lt;/code&gt;s (i.e., tests) for the &lt;code&gt;RSpec::ExampleGroups::PostsIndex&lt;/code&gt; subclass:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# rspec-core/lib/rspec/core/example_group.rb&lt;/span&gt;
&lt;span class="c1"&gt;# In this case, `name` is `:it`&lt;/span&gt;
&lt;span class="c1"&gt;# and `all_args` is `renders a list of posts`&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;define_example_method&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;extra_options&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{})&lt;/span&gt;
  &lt;span class="n"&gt;idempotently_define_singleton_method&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|*&lt;/span&gt;&lt;span class="n"&gt;all_args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="n"&gt;desc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;all_args&lt;/span&gt;

    &lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Metadata&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;build_hash_from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:skip&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;RSpec&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Core&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Pending&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;NOT_YET_IMPLEMENTED&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="n"&gt;block&lt;/span&gt;
    &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;extra_options&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="no"&gt;RSpec&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Core&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Example&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&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="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that the &lt;code&gt;ExampleGroups&lt;/code&gt; subclass (i.e., the spec) and its &lt;code&gt;Examples&lt;/code&gt; (i.e., the tests within the spec) have been defined, the runner is called to execute the tests!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# rspec-core/lib/rspec/core/runner.rb&lt;/span&gt;
&lt;span class="c1"&gt;# If we're running only this spec, `example_groups` would be&lt;/span&gt;
&lt;span class="c1"&gt;# a single-member array containing `RSpec::ExampleGroups::PostsIndex`&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;run_specs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;example_groups&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;examples_count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vi"&gt;@world&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;example_count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;example_groups&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;examples_passed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vi"&gt;@configuration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reporter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;report&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;examples_count&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;reporter&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="vi"&gt;@configuration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;with_suite_hooks&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;examples_count&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="vi"&gt;@configuration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fail_if_no_examples&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="vi"&gt;@configuration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;failure_exit_code&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;

      &lt;span class="n"&gt;example_groups&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;reporter&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}.&lt;/span&gt;&lt;span class="nf"&gt;all?&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="n"&gt;exit_code&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;examples_passed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Is metaprogramming in Ruby pointless?
&lt;/h2&gt;

&lt;p&gt;If you’ve read all three installments in this series, then firstly – thank you! We hope we adequately addressed your Ruby metaprogramming questions, or at least inspired curiosity.&lt;/p&gt;

&lt;p&gt;Secondly, you’ve probably noticed we reiterated &lt;em&gt;ad nauseum&lt;/em&gt; that metaprogramming is a “break glass in case of emergency” kind of tool.&lt;/p&gt;

&lt;p&gt;So let’s wrap this up by addressing the elephant in the room one last time: is Ruby metaprogramming useful to the average developer? Our hope is that these three articles have proven to you that the answer should be an emphatic, “YES”....&lt;/p&gt;

&lt;p&gt;…followed by a cautiously muttered, “under the right circumstances and for specific use-cases.”&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Learn more about how The Gnar &lt;a href="https://www.thegnar.com/software-development/ruby-on-rails-development"&gt;builds Ruby on Rails applications&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ruby</category>
      <category>rails</category>
      <category>programming</category>
    </item>
    <item>
      <title>Meet The Gnarnians: Taylor</title>
      <dc:creator>Ethan Fertsch</dc:creator>
      <pubDate>Thu, 29 Jun 2023 17:56:14 +0000</pubDate>
      <link>https://dev.to/thegnarco/meet-the-gnarnians-taylor-56k0</link>
      <guid>https://dev.to/thegnarco/meet-the-gnarnians-taylor-56k0</guid>
      <description>&lt;h2&gt;
  
  
  Gnarnian [nahr-nee-uhn] &lt;em&gt;noun&lt;/em&gt;: An employee of The Gnar Company
&lt;/h2&gt;

&lt;p&gt;In our &lt;em&gt;Meet The Gnarnians&lt;/em&gt; series, we unravel the stories, experiences, and passions that drive the folks on our talented team at The Gnar. Through a collection of interview-style questions, you’ll get a sneak peek into daily life on our fully-remote team, some epic origin stories, and answers to some questions as weird and wonderful as our team itself.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Basics&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Where are you located?&lt;/strong&gt; Charlestown, MA&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What is your Role?&lt;/strong&gt; Software developer / grumpy old man&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When did you join the Gnar?&lt;/strong&gt; 2016&lt;/p&gt;

&lt;h2&gt;
  
  
  If you were in the middle of a zombie apocalypse, who are three people you'd want on your team?
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;My wife Lauren: she's the most practical person I know. The kids we can leave behind.&lt;/li&gt;
&lt;li&gt;Nick Maloney. I'm not saying he's already prepared for this exact scenario, but he's probably pretty close.&lt;/li&gt;
&lt;li&gt;Zach Galifianakis. I just feel like someone would need to lighten the mood.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  What’s the last song you played or are listening to right now?
&lt;/h2&gt;

&lt;p&gt;"Rust" by Lagwagon is generally running through my head a few times a week. &lt;/p&gt;

&lt;h2&gt;
  
  
  In your role, do you have any specializations or areas of focus you really enjoy doing?
&lt;/h2&gt;

&lt;p&gt;From a technical perspective I'm a Rubyist and a Javascripter. I have had a lot of fun and made a lot of web applications armed with these very powerful tools. Being a full-stack developer means I never get tired of writing back-end or front-end code, as I get a pretty good mix of both all year round.&lt;/p&gt;

&lt;p&gt;From a career perspective, I have always gravitated toward the people side of the work. Building software is fun, but seeing what it means for a founder and their users is what makes the work exciting for me. Consulting has given me an opportunity to build software and build relationships at the same time.&lt;/p&gt;

&lt;h2&gt;
  
  
  If you weren’t in your current role, what would you be doing?
&lt;/h2&gt;

&lt;p&gt;If I didn't work at the Gnar, I would probably be starting a coffee roasting business. I have been a hobbyist coffee roaster for some time now, and have put most of my Gnar anniversary gifts towards grinders, roasters, espresso machines, even a refractometer! Google that one.&lt;/p&gt;

&lt;h2&gt;
  
  
  What has your experience been at Gnar?
&lt;/h2&gt;

&lt;p&gt;I was the first full-time hire at The Gnar, more than six years ago. When I first met Mike (I already knew Nick), the company was located in the conference room of another company. There was a big white board with all of the client work organized on it, two large desks, about six chairs, and cables everywhere. I said to myself ""this is definitely where I want to work."" &lt;/p&gt;

&lt;p&gt;Since that day I have upgraded offices four times, and I have watched the team grow from four to twenty-five. And throughout all of this change and growth, my sense of belonging and pride in the company has only grown. It's been a pleasure thus far.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's your favorite part of working at Gnar?
&lt;/h2&gt;

&lt;p&gt;The people. First and foremost, if Mike and Nick were not the people they are, this company would not be what it is. The trickle-down effect is that we have hired really great team members. Strong developers, designers, and relationship managers, of course. But as importantly, we've hired people with interesting perspectives, different ideas, and bold opinions. And mutual respect is table stakes. It makes for this evolving culture of growth and energy, pulled in a slightly different direction by each new hire.&lt;/p&gt;

&lt;p&gt;Also clapping. There's a lot of clapping.&lt;/p&gt;

&lt;h2&gt;
  
  
  What advice would you give someone who is just starting out in your industry?
&lt;/h2&gt;

&lt;p&gt;Ask questions early and often. Whether it's a question to a colleague about writing code or a question to a client about a requirement, make sure you understand exactly what is happening and what is needed. Nobody will ever fault you for wanting to get it right.&lt;/p&gt;

&lt;h2&gt;
  
  
  Outside of work, what do you enjoy doing?
&lt;/h2&gt;

&lt;p&gt;I really enjoy gardening when I'm outside and reading fiction when I'm inside. I grew up in the Rocky Mountains, so winter sports are a big part of my life. I have used the term "gnar" many times prior to working here.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Learn more about &lt;a href="https://www.thegnar.com/about"&gt;The Gnar and our team&lt;/a&gt;.&lt;/em&gt; &lt;/p&gt;

</description>
      <category>webdev</category>
      <category>culture</category>
      <category>community</category>
    </item>
    <item>
      <title>Meet The Gnarnians: Erica</title>
      <dc:creator>Ethan Fertsch</dc:creator>
      <pubDate>Thu, 01 Jun 2023 17:50:11 +0000</pubDate>
      <link>https://dev.to/thegnarco/meet-the-gnarnians-erica-e52</link>
      <guid>https://dev.to/thegnarco/meet-the-gnarnians-erica-e52</guid>
      <description>&lt;h2&gt;
  
  
  &lt;strong&gt;Gnarnian [nahr-nee-uhn] &lt;em&gt;noun&lt;/em&gt;:&lt;/strong&gt; An employee of The Gnar Company
&lt;/h2&gt;

&lt;p&gt;In our &lt;em&gt;Meet The Gnarnians&lt;/em&gt; series, we unravel the stories, experiences, and passions that drive the folks on our talented team at The Gnar. Through a collection of interview-style questions, you’ll get a sneak peek into daily life on our fully-remote team, some epic origin stories, and answers to some questions as weird and wonderful as our team itself.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Basics&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Where are you located?&lt;/strong&gt; Philadelphia, PA&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What is your Role?&lt;/strong&gt; Design Director&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When did you join the Gnar?&lt;/strong&gt; November 2022&lt;/p&gt;

&lt;h2&gt;
  
  
  What's the best advice you've ever received?
&lt;/h2&gt;

&lt;p&gt;When I was in college I used to order enough Chinese food to last me a couple of days (very normal, we all did it). The best part of not only having cheap meals that would last the week but the large amount of fortune cookies they would include in the assumed large family sized order. Most of the time the messages you get are pretty repetitive, "Don't hold onto things. that require a tight grip,"  "A faithful friend is a strong defense, " or like "Good luck will find you soon". However, this one night while I was stressing over my end of year portfolio, I opened one that said, "You can do anything but not everything", then another that said, "Don't Panic". Both very different than you'd typically get. I kept them, and taped them to a post card I pinned to my wall, and carried them with me until I lost them in a move. So yeah, I could say that was the best advice I've received thanks to my delivery food.&lt;/p&gt;

&lt;h2&gt;
  
  
  What’s the last song you played or are listening to right now?
&lt;/h2&gt;

&lt;p&gt;Currently listening to "I'm a Princess" by Bill Wurtz. &lt;/p&gt;

&lt;h2&gt;
  
  
  What was your career journey up until joining the Gnar?
&lt;/h2&gt;

&lt;p&gt;My entire career has been in the design industry but not the same mediums. When I graduated I worked for small NYC design studios, designing identities specifically for hotels, restaurants, and food &amp;amp; beverage companies. That work included a lot of print design, web design and creation of die-lines for physical product packaging. The work I did for restaurants focused around menus, branding for the interior, signage, and their websites. After working for a few small studios I moved to a global agency in NYC, and developed design systems for healthcare and consumer brands like Vonage, Amtrak, and Nestle Waters. At some point everyone was switching from agencies to tech start-ups and I thought I'd might try that out too. While working at various tech start-ups, I got to wear many hats (marketing hats, product management hats, and even an office design hat), was able to build my experience in product design, lead several small design teams, and most recently was part of a fintech start-up that went public during my tenure there.&lt;/p&gt;

&lt;h2&gt;
  
  
  If you weren’t in your current role, what would you be doing?
&lt;/h2&gt;

&lt;p&gt;There was a point in my life where I was torn between culinary school and art school. Obviously I picked art school, but if I didn't I would 100% be doing Pastry/baking. &lt;/p&gt;

&lt;h2&gt;
  
  
  What is your ideal work/desk set-up?
&lt;/h2&gt;

&lt;p&gt;I recently moved into my own house and for the first time had the freedom to put my desk in its own room other than my bedroom or living room. I built a wall-length desk with a counter top, added some table legs and desk drawers. Since it's technically a kitchen countertop I don't have to worry about the condensation from all my iced coffees ruining it. I also keep a lot of design books within reach, pens, my sketchbook, and a Wacom tablet. On the other end of the desk is a work area for my partner and the xbox. With summer coming, I'll probably move to take some calls on my back patio, but I prefer to work from my office. It's important to me to maintain that feeling of stepping away from my "work area" for a mental break which is why I tend to only be productive from one area versus multiple set-ups throughout my house. &lt;/p&gt;

&lt;h2&gt;
  
  
  How do you structure your work day or week?
&lt;/h2&gt;

&lt;p&gt;One thing about me that has been forever true, I am NOT a morning person. I am not productive at all. I start my day either taking a brisk walk with my dog to a local coffee shop or just the walk and then make coffee at home. Either way I need coffee, coffee is a priority. Knowing my focus isn't the best in the morning I tend to designate that time then to things like, checking emails, planning my tasks for the day, and attending stand-ups for various projects. One of the best things about working remotely is I actually am more productive earlier than when I used to commute to work. Before lunch, I typically spend time doing any additional research, and looking up design inspiration. By the afternoon I am more heads down on client work which ranges from writing user research plans, designing, connecting with contract designers, and working internal projects for The Gnar like, designing social posts, writing blog posts, or documenting internal processes. At 3pm my dog reminds me it's time for her walk, and after that 'm usually heads down for the rest of the afternoon to early evening. Being a night-owl I will often be back online after 8pm for anything that is still swirling around in my head. &lt;/p&gt;

&lt;h2&gt;
  
  
  Outside of work, what do you enjoy doing?
&lt;/h2&gt;

&lt;p&gt;My partner and I own a small gelato shop in Fishtown, Philadelphia. If I'm not there helping him in the shop, I'm either out with my dog, playing video games or visiting friends in NYC for the weekend.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Learn more about &lt;a href="https://www.thegnar.com/about"&gt;The Gnar and our team&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>design</category>
      <category>culture</category>
      <category>community</category>
    </item>
  </channel>
</rss>
