<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Julian Burr</title>
    <description>The latest articles on Forem by Julian Burr (@julianburr).</description>
    <link>https://forem.com/julianburr</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F145241%2Fc388701e-b499-4aaa-8cee-51cdc57f0020.jpeg</url>
      <title>Forem: Julian Burr</title>
      <link>https://forem.com/julianburr</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/julianburr"/>
    <language>en</language>
    <item>
      <title>Deja vu — How Server Components Are Changing The Game Using Old Ideas</title>
      <dc:creator>Julian Burr</dc:creator>
      <pubDate>Thu, 02 Nov 2023 14:12:00 +0000</pubDate>
      <link>https://forem.com/julianburr/deja-vu-how-server-components-are-changing-the-game-using-old-ideas-4ib3</link>
      <guid>https://forem.com/julianburr/deja-vu-how-server-components-are-changing-the-game-using-old-ideas-4ib3</guid>
      <description>&lt;p&gt;&lt;strong&gt;A brief look at the history of web rendering, what problems different strategies were trying to solve, and how Server Components conceptually fit into all of this.&lt;/strong&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;This is the write-up of a talk I gave at the &lt;a href="https://www.meetup.com/reactbris/" rel="noopener noreferrer"&gt;ReactBris meetup&lt;/a&gt; (in a simplified form) and at &lt;a href="https://webdirections.org/summit/" rel="noopener noreferrer"&gt;Web Directions Summit 2023&lt;/a&gt;. You can find the original slides &lt;a href="https://www.julianburr.de/my-work/talks/dejavu-how-server-components-change-the-game/" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Innovation
&lt;/h2&gt;

&lt;p&gt;What is innovation, and what powers it?&lt;/p&gt;

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

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



&lt;/p&gt;

&lt;p&gt;I think about this meme a lot. A news article on how wind-powered cargo shops are going to revolutionise the shipping industry. &lt;/p&gt;

&lt;p&gt;And in good old internet fashion, social media took this article and tore it to pieces. With comments like "Wind powered ships, what a time to be alive", "We really do live in the future", and just generally making fun of the idea that something like wind-powered ships, which in their collective mind was just the re-invention of sailboats, which we've had for thousands of years, could be deemed innovative or groundbreaking.&lt;/p&gt;

&lt;p&gt;I think about this a lot cause it's how I sometimes feel being a Software Engineer, especially in the frontend community and ecosystems. How often do we see people come in with new ideas, and we put them aside, or even worse, laugh them off, just because we think they're just old ideas dressed up?&lt;/p&gt;




&lt;p&gt;In the following, I'll try to explain how Server Components conceptually work, why we need them and hopefully show that innovation is often driven by applying old ideas and principles to new technologies to improve them.&lt;/p&gt;

&lt;p&gt;To do all of that, I think we need to take a brief look at the history of web rendering, the different strategies we've applied over the past 15 or so years, what problems they solved and how we got to where we are today.&lt;/p&gt;

&lt;h2&gt;
  
  
  Good ol' HTML and CSS 
&lt;/h2&gt;

&lt;p&gt;And what better place to start than the late 90s - early 2000s? When we were writing plain HTML and CSS, for the most part. Those were simpler times when it came to development itself, but also when it came to how we rendered websites.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Disclaimer: I am going to over-simplify a few things here and throughout this article to be able to get my main points across and not get too hung up on irrelevant details.&lt;/em&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%2Fcdn-images-1.medium.com%2Fmax%2F1600%2F1%2A_gO179rl1yW8yjnTcYFh1w.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%2Fcdn-images-1.medium.com%2Fmax%2F1600%2F1%2A_gO179rl1yW8yjnTcYFh1w.png" alt="Image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Back in the day, the browser would request a page from the server&lt;/li&gt;
&lt;li&gt;The server would look up the relevant files from the file system and return them&lt;/li&gt;
&lt;li&gt;And the browser would then use the files to render the page&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Again, simple. And this was great from a user experience perspective. Pages would render fast because there's not much going on on the server; it just needs to look up files, and there's not much going on on the client either; it just renders the page. Very little back and forth, no extra logic needing to be executed, and no other shenanigans happening.&lt;/p&gt;

&lt;p&gt;But there are some very obvious downsides to this approach from a developer experience perspective.&lt;/p&gt;

&lt;p&gt;Building large-scale websites and applications was a pain because it meant you had to write a lot of code. Even worse, a lot of redundant code due to the lack of abstractions helping with reusable functionality, layouts and components.&lt;/p&gt;

&lt;p&gt;Building dynamic applications was pretty much impossible. There was no way to use data from external sources like databases, and you couldn't really serve content that was specific to the user who requested the page.&lt;/p&gt;

&lt;h2&gt;
  
  
  Moving to the server
&lt;/h2&gt;

&lt;p&gt;So, we started using server-side languages like PHP. This is the era of Wordpress, Drupal, Magento and the sorts, so we're still talking early 2000s.&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%2Fcdn-images-1.medium.com%2Fmax%2F1600%2F1%2ABZb0oMMpWr2p-J4ivpejog.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%2Fcdn-images-1.medium.com%2Fmax%2F1600%2F1%2ABZb0oMMpWr2p-J4ivpejog.png" alt="Image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With server-side languages, when the browser requested a page, the server would actually do things. There's actual logic happening, including potentially pulling content from databases, etc.&lt;/p&gt;

&lt;p&gt;Then, the server would generate the HTML on demand and would return files just as it did before. So, for the browser, nothing really changed; it would still just use the returned files and render the page.&lt;/p&gt;

&lt;p&gt;This fixes all of our DX problems; now, we can easily write abstractions for reusable logic, write templates for layouts and components, and generally break down large applications in nice little chunks. We can also easily write dynamic applications with all the power we have in whatever language we choose on the server.&lt;/p&gt;

&lt;p&gt;But we took a step back from a user experience perspective. Now that the server actually does things, the response times get a lot slower. And this gets worse the more logic the server needs to run and the more complex the application is. Meaning: it doesn't scale well.&lt;/p&gt;

&lt;p&gt;And this becomes very apparent when the user navigates.&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%2Fcdn-images-1.medium.com%2Fmax%2F1600%2F1%2A6mgeaOn1fa1Z_R6cKjPM6g.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%2Fcdn-images-1.medium.com%2Fmax%2F1600%2F1%2A6mgeaOn1fa1Z_R6cKjPM6g.png" alt="Image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The page goes blank, as is the browser default behaviour, while we're waiting for the server response of the new page. The longer that takes, the worse the experience for the user at the other end.&lt;/p&gt;

&lt;p&gt;What can we do about that?&lt;/p&gt;

&lt;h2&gt;
  
  
  Introducing Javascript and AJAX
&lt;/h2&gt;

&lt;p&gt;Let's introduce Javascript, arguably the main protagonist (or antagonist, depending on what side you're standing on) of the web for the last decade or two. As well as its smaller side-kick "AJAX".&lt;/p&gt;

&lt;p&gt;AJAX stands for "Asynchronous Javascript and XML" and was invented by Google in 2005 to make it possible for websites to request data from the server, even after they've already been rendered. All through the magic of Javascript 🧙&lt;/p&gt;

&lt;p&gt;But what does that mean for our rendering process?&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%2Fcdn-images-1.medium.com%2Fmax%2F1600%2F1%2AqRBejaS7EuLJ7qKXUcnu1A.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%2Fcdn-images-1.medium.com%2Fmax%2F1600%2F1%2AqRBejaS7EuLJ7qKXUcnu1A.png" alt="Image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The initial render doesn't change (for the time being), but now when the user navigates, we intercept. And then we're in full control.&lt;/p&gt;

&lt;p&gt;We're in full control over what we show to the user while we're waiting for the server response, e.g. displaying nice loading states and progress bars, and we're also in full control over what we request from the server. We can say: hey, we already have the layout; all we really need from the server is the new content of the main section of the page.&lt;/p&gt;

&lt;p&gt;This means we can reduce the amount of work the server needs to do, we can reduce the amount of data the server needs to send back over the network, and then we're also in full control over what we want to do with that response. Instead of having to replace the whole DOM, we can be more specific and only replace the elements we know have changed.&lt;/p&gt;

&lt;p&gt;Awesome, right? This massively improves the user experience when navigating through the app. It reduces actual load times by reducing the amount of logic required on the server, but it also improves the perceived performance by allowing us to give the users visual feedback while stuff is loading.&lt;/p&gt;

&lt;p&gt;But, once again, we actually took a few steps back from a developer experience standpoint. And I hope you start seeing a pattern here: the evolution of web rendering is essentially just a continuous game of tug and pull, where we are trying to find the balance between UX and DX trade-offs.&lt;/p&gt;

&lt;p&gt;In this case, the developer experience isn't great because the code is still split into two parts, often across two separate code bases. One is the server-side code, e.g. written in PHP, that's responsible for generating the HTML on demand. This isn't great to start with, but now it gets a lot messier really quickly since we start dealing with subsets and small chunks of HTML or other response formats on the server while also starting to add DOM manipulations dealing with HTML in our Javascript on the client.&lt;/p&gt;

&lt;p&gt;Personally, I also think that AJAX was just a little bit ahead of its time. We've had jQuery, introduced by John Resig in 2006, which helped with the general adoption of Javascript across the web and made using AJAX a lot easier (especially when it came to cross-browser quirks), but actual MVC frameworks that would help us build complex, large scale web applications were still rare. This meant, more often than not (and I'm as guilty as anyone else of that, maybe even more), we would write our own custom solutions to many of the common problems at the time.&lt;/p&gt;

&lt;p&gt;And this is why, unfortunately, even now, when we think back to this era, we still think about spaghetti code - cause that's what we wrote.&lt;/p&gt;

&lt;h2&gt;
  
  
  Client-side rendering and SPAs
&lt;/h2&gt;

&lt;p&gt;To address that, what if we moved more stuff to the client? What if we moved everything to the client?&lt;/p&gt;

&lt;p&gt;This will likely start to look more and more familiar now, as we're entering the era of client-side rendering (CSR) and single page applications (SPAs), with frameworks like KnockoutJS and Angular in 2010, and then later React and Vue in 2013 and 2014 respectively.&lt;/p&gt;

&lt;p&gt;And the idea is simple: when the browser requested a page, the server would basically go back to square one and just look up static files from the file system. However, the HTML is now just an empty shell (e.g. an empty div) and some Javascript that now has the sole responsibility for generating the HTML.&lt;/p&gt;

&lt;p&gt;So when the browser renders the HTML, the page is still empty (again, the HTML is just an empty shell at this stage). It then parses the Javascript, which means it might stumble across a few more resources it needs, like other Javascript files or images. It then parses those and might stumble across some data requests it needs to send to the server, which then needs to do a bunch of work to return that data, which gets parsed …. and so on.&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%2Fcdn-images-1.medium.com%2Fmax%2F1600%2F1%2AK27dxJX4wNTjUnih6mgbyw.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%2Fcdn-images-1.medium.com%2Fmax%2F1600%2F1%2AK27dxJX4wNTjUnih6mgbyw.png" alt="Image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I hope you can tell: this initial render is just awful from a user experience standpoint. It's leading to what we call a "network waterfall", which means there's potentially a lot of forth and back happening between client and server while the client parses and executes the Javascript, which all happens sequentially cause the browser doesn't know about it until the Javascript is being executed. This cascade of network requests gets worse the bigger and more complex the application is. Again, this doesn't scale well.&lt;/p&gt;

&lt;p&gt;However, once the initial render is done, we get all the benefits of the previous AJAX approach. We're in full control to show loading states, we only request from the server what we need, and we only replace the content that has changed.&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%2Fcdn-images-1.medium.com%2Fmax%2F1600%2F1%2ASZHVJZDTXV7ojCcJHChmwQ.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%2Fcdn-images-1.medium.com%2Fmax%2F1600%2F1%2ASZHVJZDTXV7ojCcJHChmwQ.png" alt="Image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We also obviously improved the developer experience massively this time. Frameworks like React revolutionised how we build web apps, made building complex interfaces more accessible to a lot of developers, and - I believe - just in general further drove the adoption of the web as the medium of choice for a lot of product teams.&lt;/p&gt;

&lt;p&gt;But what can we do about that initial render?&lt;/p&gt;

&lt;h2&gt;
  
  
  Static site generation and server-side rendering
&lt;/h2&gt;

&lt;p&gt;This is where we see a lot more people taking a few steps back, looking at some of the old strategies we've used in the past and attempting to apply the good ideas from those to the new technologies we introduced in the meantime.&lt;/p&gt;

&lt;h3&gt;
  
  
  Static site generation (SSG)
&lt;/h3&gt;

&lt;p&gt;The initial wave was "static site generation". What if we kept using those awesome new frameworks like React and Vue to write websites and apps but then generated static HTML from that at build time? This could either happen locally or, more likely, through some form of CI/CD setup whenever the content changes.&lt;/p&gt;

&lt;p&gt;What started with experimental packages and webpack plugins eventually led to the introduction of proper frameworks like Gatsby and NextJS.&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%2Fcdn-images-1.medium.com%2Fmax%2F1600%2F1%2ApDi0B1_LSvrnjECdLEmwzg.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%2Fcdn-images-1.medium.com%2Fmax%2F1600%2F1%2ApDi0B1_LSvrnjECdLEmwzg.png" alt="Image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is great from a user experience perspective. We're basically back to the plain HTML days:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The browser requests a page&lt;/li&gt;
&lt;li&gt;The server is back to only having to look up the static files we pre-generated and return them&lt;/li&gt;
&lt;li&gt;And the browser uses them to render the page&lt;/li&gt;
&lt;li&gt;But now, we introduce a new step! This is necessary because we're writing the core code in Javascript, so it likely contains a lot of client-side logic like state, effects, context, event listeners, etc, which can't be included in the static HTML we generated. So, to make sure we get all of this interactive stuff, we need to execute the JS and inject it into the page. This is what we call "hydration".&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We cherry-picked all of the good parts of static HTML while keeping all of the benefits of SPAs when the user navigates. The framework will intercept, only fetch what it needs for the new page, and then replace all changed parts of the website for us. Nice!&lt;/p&gt;

&lt;p&gt;But this approach obviously has the same shortcomings we had back in the HTML days: it doesn't really work well for dynamic content.&lt;/p&gt;

&lt;h3&gt;
  
  
  Server-side rendering
&lt;/h3&gt;

&lt;p&gt;But the solution to that is pretty straightforward; we've done it before when we transitioned from plain HTML to PHP, generating HTML on demand.&lt;/p&gt;

&lt;p&gt;However, instead of PHP, we now use Node. It's the same we're doing for static-site generation, running our JS in Node to generate the HTML, but instead of doing it at build time, we do it on demand on a server.&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%2Fcdn-images-1.medium.com%2Fmax%2F1600%2F1%2ATAKBHwWgsHyYQjm5wKPYug.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%2Fcdn-images-1.medium.com%2Fmax%2F1600%2F1%2ATAKBHwWgsHyYQjm5wKPYug.png" alt="Image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We're basically back in the AJAX days: the server generates HTML on demand, and when we navigate, we use the power of the client-side rendering to create a smooth experience for the user. We do still have that "hydration" step, meaning on the initial render, the content is there but not interactive yet until that step has been completed.&lt;/p&gt;

&lt;p&gt;But, generally speaking, we finally came full circle: we've brought the code close together, having everything in JS now, and we've introduced frameworks to help us with common abstractions and "best practices".&lt;/p&gt;

&lt;p&gt;And we've introduced a lot of them! While it might feel like NextJS has the upper hand in this game, I think there's a lot of healthy competition in this space, with frameworks like Gatsby, Remix, FreshJS, etc.&lt;/p&gt;




&lt;h2&gt;
  
  
  Server Components
&lt;/h2&gt;

&lt;p&gt;This is where we're finally getting to Server Components. React first introduced the concept in 2020 when &lt;a href="https://www.youtube.com/watch?v=TQQPAU21ZUw" rel="noopener noreferrer"&gt;Dan Abramov and Lauren Tan showcased it&lt;/a&gt; during a conference talk.&lt;/p&gt;

&lt;p&gt;Since then, the feature has pretty much been experimental. But we have seen it getting introduced into the wider ecosystem more and more, most recently through Next 13, where the new app router is built on top of Server Components.&lt;/p&gt;

&lt;p&gt;It's important to note that Server Components are not a new rendering strategy. It is not replacing SSR or SSG; instead, think of it as an enhancement to both of those.&lt;/p&gt;

&lt;p&gt;But what problem are they trying to solve?&lt;/p&gt;

&lt;h3&gt;
  
  
  The "hydration" problem
&lt;/h3&gt;

&lt;p&gt;There are a few things Server Components are aiming to do, but there are two primary goals relevant to us in this context: tackle the "hydration" bottleneck we've just seen in the SSG and SSR flow and, in the process, reduce the size of our client-side JS bundles.&lt;/p&gt;

&lt;p&gt;But where does this "hydration" problem come from? We didn't have it back in the PHP and AJAX days, right?&lt;/p&gt;

&lt;p&gt;Fundamentally, I think the core of the issue was introduced when we moved to client-side rendering and SPAs. All the frameworks created at that time (React, Vue, etc.) were created to be run on the client, allowing developers to easily handle things like event listeners, local state, side effects when the page or individual components re-render, etc. And that's great from a UX perspective; we want websites and apps to be more interactive; we want to create more immersive experiences.&lt;/p&gt;

&lt;p&gt;But it didn't translate when we moved back to the server. HTML itself doesn't have any concept of all of these interactive things. So when we render HTML from our JS on the server, they get lost. To ensure the website still does what we want it to do, we then added that "hydration" step.&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%2Fcdn-images-1.medium.com%2Fmax%2F1600%2F1%2AlG3jCeU_ZVDyW8p-kEuU_w.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%2Fcdn-images-1.medium.com%2Fmax%2F1600%2F1%2AlG3jCeU_ZVDyW8p-kEuU_w.png" alt="Image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Very simplified, that hydration step basically re-runs all the JS we already executed to pre-render the page (either statically or on the server) to create a virtual copy of the same page. It then uses that virtual copy to inject all of the interactive parts into the rendered DOM.&lt;/p&gt;

&lt;p&gt;But that's slow, especially the larger and more complex the application gets. Which means, you guessed it, it doesn't scale very well.&lt;/p&gt;

&lt;p&gt;And if you think about it, this is the complete opposite of what we did with PHP and AJAX, right? When we were generating HTML through PHP, it would be static. There is no way to describe dynamic behaviour, like local state, effects, etc, in PHP. Instead, we would create a separate Javascript file, which will target specific elements on the page to make those elements interactive.&lt;/p&gt;

&lt;p&gt;So we basically went from a "static first" mindset, only injecting interactivity where specifically needed, to "everything is or can be interactive".&lt;/p&gt;

&lt;p&gt;The problem with all of this is that the bundler has no idea whether certain code or components are interactive or not. So, for sites that use classical SSR or SSG, we need to hydrate the whole page. Meaning, as I mentioned before, re-running all of the Javascript we already ran on the server, generating the HTML, creating a virtual copy of the DOM of the page, and then injecting the interactive parts into the existing DOM.&lt;/p&gt;

&lt;h3&gt;
  
  
  Hydration strategies of the future
&lt;/h3&gt;

&lt;p&gt;Server components are trying to take a step back and re-apply more of the old mindset. What if we allowed developers to tell the bundler whether a component is static or interactive? Or, in other words, whether a component can be fully rendered on the server or if it needs to be hydrated on the client. And what if we made everything static by default (like back in the PHP days) but made it easy to opt out and make components interactive?&lt;/p&gt;

&lt;p&gt;That's all the "use client" directive React introduced really is. A hint for the bundler that this component needs to be hydrated and, therefore, that its code needs to be included in the client-side bundle.&lt;/p&gt;

&lt;p&gt;On the flip side, it also tells the bundler which components are meant to be static. By definition, those components only ever need to be rendered once on the server, so they don't need to be hydrated, and their code can be completely excluded from the client bundle.&lt;/p&gt;

&lt;p&gt;This is huge! It means, for a lot of static content, we can completely ignore all the dependencies we might need (like date formatting, markdown to HTML conversion or syntax highlighting, e.g. if you're showing code snippets on your website) that used to blow up our Javascript bundles. So we're reducing our bundle sizes, which improves the overall load time to start with, but we also reduce the amount of code that needs to be executed for the hydration step, which further improves the time until the page becomes interactive.&lt;/p&gt;

&lt;p&gt;This is what's called "partial hydration", and React is by no means the only library to look into this or even the front runner. Frameworks like Astro, which introduced the idea of "component islands" in 2019, are trying to achieve very similar things: allowing developers to write code that's static by default but then letting them opt out of the static behaviour and add interactive elements where needed.&lt;/p&gt;

&lt;p&gt;Other frameworks go even further with what they call "zero hydration". Qwik, created by Misko Hevery, who also created Angular, is probably the most popular one at the moment. The idea is that there is no hydration step at all on the initial render. Qwik achieves this primarily through two things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Resumable state - the server can leave state in serialised form within the HTML, so it can be picked up and "resumed" at any later point without having to start from scratch again&lt;/li&gt;
&lt;li&gt;Going hard on the code splitting - this one is probably more relevant in the context of this article; in Qwik, everything is split into its own tiny little chunks, every function, every closure. Then, instead of hydrating dynamic parts and logic into the DOM at the initial load, Qwik waits until the user interacts with the elements that need those dynamic parts. For example, say we have a button with a click handler on it; the JS code for that click handler is a tiny chunk, and that chunk only gets loaded when the user actually interacts with that button.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;There's obviously a balance to find with these approaches, but I think it's interesting to compare them and their philosophies. Essentially, when we do full or even partial hydration, we're still wasting a lot of resources and time hydrating elements that the user will never actually interact with.&lt;/p&gt;

&lt;h3&gt;
  
  
  Other benefits of Server Components
&lt;/h3&gt;

&lt;p&gt;I don't really want to go into too much detail here since the article is already long enough as it is, but I think it's worth mentioning some of the other benefits of server components so you can dig deeper yourselves if you find any of these interesting or intriguing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Since SCs are only ever rendered on the server, they can make dealing with server-side logic much simpler, e.g. getting data from the database&lt;/li&gt;
&lt;li&gt;Since SCs can be asynchronous functions, the React team made sure they work really well with &lt;a href="https://react.dev/reference/react/Suspense" rel="noopener noreferrer"&gt;Suspense&lt;/a&gt;. The server-side rendered SCs are actually sent to the client in a JSON-like format that describes the components in a way that React can then turn into HTML. That format can also mark components as "suspended" and trigger any boundaries to show, for example, some form of loading state. The format can also easily be streamed so that when the server finally resolves the asynchronous function, React can simply swap out the loading state with the (now available) component 🤯&lt;/li&gt;
&lt;/ul&gt;




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

&lt;p&gt;The point of this article was primarily to give some context on why Server Components exist, what problem they are trying to solve and how all of this fits into the (ongoing) evolution of rendering on the web.&lt;/p&gt;

&lt;p&gt;But if you are looking for some personal takeaways, here are a few:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Do I think, conceptually, Server Components are the future?&lt;/strong&gt; Yes, 100%. They very specifically solve a problem we've introduced when we moved to client-side rendering and SPAs and then tried to move back to the server. Fundamentally, I think Server Components will help us as developers to get back into that "static first" mindset I've mentioned, and enforce best practices. A lot of the anti-patterns we've started adopting, e.g. using useState and useEffect everywhere, even when it's not needed, can be addressed through that mindset shift. Server Components also encourage using the platform more, which by now has become a bit of a meme, but I do think it's important. If your server component can't hold local state, use the platform (aka the URL and query parameters) to store the serialisable state. This automatically leads to a better user experience, cause state can now more easily be shared and persisted by your users - win-win.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Do I think the way Server Components are implemented in React and especially in Next is perfect?&lt;/strong&gt; Absolutely not, and that's fine. There's a reason the feature went through that extensive "beta" phase. Personally, I think the developer experience can still be improved. There are still a lot of unintuitive parts that lead to confusion and misunderstandings in the community. But that's okay; no one nails a new thing on the first try. And other libraries and frameworks like Astro and Qwik looking at the same problem from different angles will hopefully drive improvements, especially around the DX, to improve the whole ecosystem.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Do I think everyone needs to start using Server Components now?&lt;/strong&gt; I'm only including this here, because I always see these articles and talks online that showcase a cool new trend or feature, and then tell people they have to use it or otherwise they are stupid. I think that's pretty offensive to start with, but then also generally poor and useless advice. I don't know your background, your context or the specific requirements your projects might have. If you're mostly working on small apps and websites, where the hydration bottleneck is not really noticeable, and bundle size is not really an issue, the benefits of Server Components will likely be very minimal. However, what I do hope is that the concept eventually becomes so baked into our tools, patterns and best practices that in the future, you don't really have to think about whether or not you need to use it.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>react</category>
      <category>servercomponents</category>
    </item>
    <item>
      <title>How to Use Semantic Versioning for Your Apps</title>
      <dc:creator>Julian Burr</dc:creator>
      <pubDate>Tue, 05 May 2020 03:52:42 +0000</pubDate>
      <link>https://forem.com/julianburr/how-to-use-semantic-versioning-for-your-apps-2dak</link>
      <guid>https://forem.com/julianburr/how-to-use-semantic-versioning-for-your-apps-2dak</guid>
      <description>&lt;p&gt;&lt;em&gt;Disclaimer: this is not supposed to be a definitive guide on how you have to do versioning in your apps, it is more aiming to share the experience and processes we went through at &lt;a href="https://rexlabs.io" rel="noopener noreferrer"&gt;rexlabs&lt;/a&gt; and where we might be heading...&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The core idea and requirements
&lt;/h2&gt;

&lt;p&gt;At rexlabs we're primarily building web applications as SaaS products, so the problems and solutions I'll be going through here are very much focused on that type of application.&lt;/p&gt;

&lt;p&gt;To give some more context though: in general, we're no strangers to versioning. Like most other development teams we actively use version control for all of our development work. We also created a whole JS ecosystem of utility libraries and React components which we all publish to private npm packages, where they are all versioned using standard semantic versioning (aka &lt;a href="https://semver.org/" rel="noopener noreferrer"&gt;semver&lt;/a&gt;). I won't be covering those basics here, so if you don't know what that is, I highly recommend reading up on it. &lt;/p&gt;

&lt;p&gt;Coming from all this, we really wanted to add versioning to our main applications as well. But semver doesn't really make much sense in the context of web apps. It's focused on increasing the transparency around the type of changes to a product in between versions, e.g. a breaking change will lead to a new major version, a new feature to a new minor version, and a bugfix to a new patch version. This makes a lot of sense for products that keep all versions of themselves around, where the consumer can pick and choose which version they want to use. If the consumer sees a new major version, they know it might break the way they've been using the product so far, so they know to be extra careful when upgrading and have the chance to avoid upgrading alltogether.&lt;/p&gt;

&lt;p&gt;But there is no such thing as "breaking changes" in our web apps ... at least not intentionally 😅. We don't give users any choice what version they're being served, so using the version to identify the type of change since the last release doesn't make much sense. Our apps are not consumed in the same way as e.g. our npm libraries. So why bother with versioning at all in web apps? For us, there were a few reasons for this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;one of the main objectives was around the ability to track bugs - we use &lt;a href="https://www.bugsnag.com/" rel="noopener noreferrer"&gt;Bugsnag&lt;/a&gt;, seeing when an issue was first introduced helps a lot with tracking the error down quickly&lt;/li&gt;
&lt;li&gt;also, just because you always deploy a specific version doesn't mean all your users are always using that version - we found users of our app would sometimes not refresh the browser tab with our app for several days or weeks 😕&lt;/li&gt;
&lt;li&gt;it also makes our internal QA process easier - being able to associate a fix with a version and note that down in the support ticket means both QA and customer support can easily see whether or not a specific bug is supposed to be fixed in the version they are working with&lt;/li&gt;
&lt;li&gt;and last but not least: release notes - having linear versioning makes it easier for everyone to curate and consume changelogs for the releases we ship&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why semver
&lt;/h2&gt;

&lt;p&gt;Ok, we identified why we want to keep track of app versions. By why use semver, especially if it loses all meaning in the web app context? Why not simply use incremental integers and call it a day?&lt;/p&gt;

&lt;p&gt;First of all, we found it helpful to see what environment we're looking at just by seeing the version number. We usually have multiple environments for each of our applications deployed, i.e. &lt;code&gt;production&lt;/code&gt; (environment used by the end users), &lt;code&gt;beta&lt;/code&gt; (staging environment connected to production database, mostly used for testing and QA) and &lt;code&gt;alpha&lt;/code&gt; (staging environment with a separate throwaway database). Using versions to represent these environments helps identifying what you're looking at and, as mentioned before, identifying where a bug originated. The versioning using the semver version structure could look something like this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;production&lt;/code&gt; = major version&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;beta&lt;/code&gt; = minor version&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;alpha&lt;/code&gt; = patch version&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Meaning, every time we push to &lt;code&gt;alpha&lt;/code&gt;, we do a patch bump, every time we push to &lt;code&gt;beta&lt;/code&gt; we do a minor bump, and so on. This also makes iit very easy to keep track of where the different environments are at, e.g. if &lt;code&gt;production&lt;/code&gt; is on v20.0.0 and &lt;code&gt;beta&lt;/code&gt; is on v20.10.0, you know immediately that beta is quite a few changes ahead which haven't made it into production yet.&lt;/p&gt;

&lt;h2&gt;
  
  
  Release notes
&lt;/h2&gt;

&lt;p&gt;As I said, we use semver for our internal packages of our FE ecosystem, where we take advantage of a lot of open source tools that deal with versioning and release note automation through &lt;a href="https://www.conventionalcommits.org/en/v1.0.0/" rel="noopener noreferrer"&gt;conventional commits&lt;/a&gt;, such as &lt;a href="https://github.com/conventional-changelog/commitlint" rel="noopener noreferrer"&gt;&lt;code&gt;commitlint&lt;/code&gt;&lt;/a&gt; or &lt;a href="https://github.com/lerna/lerna" rel="noopener noreferrer"&gt;&lt;code&gt;lerna&lt;/code&gt;&lt;/a&gt;. But again, these tools were made specifically with library authors in mind, and try to follow true semver versioning. So while we took inspiration from these tools, using them for our web apps without any customisation wasn't really a choice.&lt;/p&gt;

&lt;p&gt;We still use conventional commits for apps though. It makes it easier to scan the git logs of a project, and even without using it to determine the next version according to semver, it can still be very useful to group commits for internal changelogs. We just don't tie it to the versioning of the apps.&lt;/p&gt;

&lt;p&gt;However, especially when thinking about release notes for our support team or even potentially customer facing, one of the challenges we anticipated was how technical our commit messages usually are. This is usually not a problem for e.g. changelogs of an npm package, since your consumers are also developers with some technical understanding, but for app changelogs with non-tech end users these seemed unhelpful.&lt;/p&gt;

&lt;p&gt;This is where we started our journey, looking into how we wanted to tackle versioning in our apps and how we could automate the process as much as possible.&lt;/p&gt;

&lt;h2&gt;
  
  
  Attempt #1: manually
&lt;/h2&gt;

&lt;p&gt;The equivalent to the stone age in our journey to app versioning. Besides some node scripts for bumping versions in the &lt;code&gt;package.json&lt;/code&gt;, this approach means a lot of manual steps for every release. These included:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;manually running that script before pushing to a deployment branch - we would use the version in the &lt;code&gt;package.json&lt;/code&gt; to pass this information e.g. to Bugsnag and other services that needed to know the current version of the app&lt;/li&gt;
&lt;li&gt;manually creating release notes by scanning through the git history since the last release (which hopefully had been tagged in git, otherwise the task would become even more tedious)&lt;/li&gt;
&lt;li&gt;then pushing to the deployment branch which would trigger our CI pipeline&lt;/li&gt;
&lt;li&gt;finally, once the CI pipeline was finished, manually sending a Slack message with the release notes, to ensure other teams including customer support and sales were aware of the changes&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fhndu791299l5yq4t835d.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%2Fi%2Fhndu791299l5yq4t835d.png" alt="Flow chart of manual release and versioning process"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Take aways:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;👍 unique versions that worked really well with Bugsnag to keep track of issues in relation to specific releases&lt;/li&gt;
&lt;li&gt;👍 very readable release notes, easy to digest for non-devs, e.g. the support team or even end users&lt;/li&gt;
&lt;li&gt;🤬 very slow process, especially with multiple development environments its basically unfeasible&lt;/li&gt;
&lt;li&gt;👎 unreliable, very prone to human error&lt;/li&gt;
&lt;li&gt;👎 a lot of duplicated effort (describing issues and solutions in commits, PRs and then manually for the release notes again)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This process was tedious to say the least, but I still think it's always good to start here. It might seem unnecessary and painful, but doing a task that you would like to automate manually first usually allows you to identify specifically what it is that you want to automate and what the ideal outcomes should be. Too early abstractions and automations can easily lead to messy solutions that don't actually do what you wanted in the beginning.&lt;/p&gt;

&lt;h2&gt;
  
  
  Attempt #2: pre-push git hook
&lt;/h2&gt;

&lt;p&gt;It became very obvious very quickly that the initial approach was not really scalable or in any shape or form developer friendly. To improve that, we wrote a node script, that kept track of releases and release notes. To do so, you would need to run it on the relevant branches before pushing to them. It would then:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;use git to find the commits since the last release and create release notes based on that&lt;/li&gt;
&lt;li&gt;to make the notes more non-tech user friendly and readable, we also used the Github API to find PRs related to the commits and use the PR titles instead of commit messages&lt;/li&gt;
&lt;li&gt;the script would then bump the version for the specific environment in a dedicated &lt;code&gt;versions.json&lt;/code&gt; in the repo, which would serve as the source of truth e.g. for Bugsnag reporting&lt;/li&gt;
&lt;li&gt;it would then add general release information like the commit range to a &lt;code&gt;changelog.json&lt;/code&gt;, as well as release notes based on the git/Github information to &lt;code&gt;changelog/[ENVIRONMENT].md&lt;/code&gt; files&lt;/li&gt;
&lt;li&gt;and finally it would send a Slack message with the generated changelogs to a dedicated channel, again trying to keep other teams in the loop as we did before&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To automate the whole process we added a &lt;a href="https://git-scm.com/docs/githooks" rel="noopener noreferrer"&gt;pre-push hook&lt;/a&gt; (using &lt;a href="https://github.com/typicode/husky" rel="noopener noreferrer"&gt;husky&lt;/a&gt;) to our repos, which then triggered it whenever anyone pushed to the release branches. The release notes and other release information the script created were stored and committed to the repo itself. The idea was that this way you could just jump around in the history and always see the relevant release notes and history relevant to the commit you're on. Also having the readmes in github seemed like a convenient way to consume them (especially with them being tied to the git history, so you could jump back to any older commit and the changelogs would correctly represent the state at the time).&lt;/p&gt;

&lt;p&gt;This all sounded good in theory, but it meant every release would create an extra commit, which also gets a bit weird in a pre-push hook, since you can't change what's being pushed on the fly (so you basically need to create a new commit, push everything and then cancel the original push), which made the hook a bit weird and confusing unless you knew about this 😕&lt;/p&gt;

&lt;p&gt;Actually storing the release information was also problematic for several reasons:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a major one was space - they will quickly grow out of proportion&lt;/li&gt;
&lt;li&gt;they also caused merge conflicts on pretty much &lt;strong&gt;every&lt;/strong&gt; merge - to get around those, we added a custom merge driver that the node script would install on first run, that would deal with any merge conflicts related to the release files and resolve them sensibly automatically&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Without going into any details, you can already tell that the whole system got pretty complex pretty quickly.&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%2Fi%2Fllmgwl4161dd49b0yb1q.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%2Fi%2Fllmgwl4161dd49b0yb1q.png" alt="Flow chart showing the process with the pre-push hook implementation"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;What we learned:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;👍 process is automated, so no more manual digging through the git logs every time you wanted to push to a release branch&lt;/li&gt;
&lt;li&gt;👍 accurate release notes, that we could post e.g. in Slack to keep everyone aware of changes being pushed to the different environments&lt;/li&gt;
&lt;li&gt;👎 very technical release notes, PR titles are OK but still not as good as manually curated changelogs&lt;/li&gt;
&lt;li&gt;👎 still quite tedious process, even with the release automation in place, especially with multiple deployment environments&lt;/li&gt;
&lt;li&gt;👎 still unreliable to an extent - to be able to do the Github API calls and send the Slack message, the local environment of the developer pushing to the deployment branch needed to be set up with the respective auth tokens, which is really inconvenient and made the process quite brittle&lt;/li&gt;
&lt;li&gt;👎 the release notes were actually created and sent to Slack on push, not on deployment, which added confusion e.g. for customer support, not knowing when exactly an issue that was reported fixed in the latest version would actually be fixed in the product&lt;/li&gt;
&lt;li&gt;👎 having the release notes in the repo seemed useful at the start, but the space they take vs the amount of times people actually looked at them there quickly got out of hand, to the point where we actually deleted older logs manually every now and then&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Attempt #3: CI script
&lt;/h2&gt;

&lt;p&gt;The main reason we didn't start here was because we didn't know where to properly store the release information. Creating commits in our CI pipeline to bump versions and add release info seemed like a very bad idea.&lt;/p&gt;

&lt;p&gt;Having experienced all the problems the pre-push hook solution caused though, we went back to our CI pipeline again looking other ways we could keep track of releases. The obvious choice was Bugsnag, one of the core reason why we wanted proper versioning in the first place. Turns out Bugsnag offers a &lt;a href="https://docs.bugsnag.com/api/" rel="noopener noreferrer"&gt;REST API&lt;/a&gt; that allows you to &lt;a href="https://docs.bugsnag.com/api/build/" rel="noopener noreferrer"&gt;read and create releases&lt;/a&gt;, including custom meta data on them, so seemingly perfect for what we want to do.&lt;/p&gt;

&lt;p&gt;But now that we want to do all "magic" in the CI pipeline, we won't necessarily have access to git anymore to get all the commit information we need. Luckily, the &lt;a href="https://developer.github.com/v3/" rel="noopener noreferrer"&gt;Github API&lt;/a&gt; gives you pretty much all you need: &lt;a href="https://developer.github.com/v3/repos/commits/#compare-two-commits" rel="noopener noreferrer"&gt;search for commits for a specific range&lt;/a&gt; (i.e. last release to current commit), &lt;a href="https://developer.github.com/v3/git/tags/" rel="noopener noreferrer"&gt;create tags&lt;/a&gt; for your releases, &lt;a href="https://developer.github.com/v3/search/#search-issues-and-pull-requests" rel="noopener noreferrer"&gt;search for PRs for specific commits&lt;/a&gt; which we already used for the last solution, etc. So essentially we just ended up re-writing our node script to make it able to run in CI and moved the execution of the script into our build step.&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%2Fi%2Fl9dqduhvzdr335vy4bw7.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%2Fi%2Fl9dqduhvzdr335vy4bw7.png" alt="Flow chart showing the data flow in the new process"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Where we're at right now:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;👍 process truly automated, no developers have to worry about their local setup being correct to be able to push to a deployment branch&lt;/li&gt;
&lt;li&gt;👍 reliable, due to the above, as long as the setup for the CI pipeline is correct, everything will run smoothly&lt;/li&gt;
&lt;li&gt;👍 no ugly release commits that create unnecessary noise in your git history&lt;/li&gt;
&lt;li&gt;👍 no release notes in the repo anymore that added extra merge conflict pain&lt;/li&gt;
&lt;li&gt;👍 the release notes will now be generated when the app is deployed, not pushed to the deployment branch&lt;/li&gt;
&lt;li&gt;👎 we still haven't done much about the readability of the release notes, still the same as before&lt;/li&gt;
&lt;li&gt;👎 we're now relying on a third party service (= Bugsnag) for the release information&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The future?
&lt;/h2&gt;

&lt;p&gt;Overall it feels like we're moving in the right direction with out changes to the way we version our apps and the processes around it. I think it makes sense at this stage to start thinking about building our own release service, even if it's super simple in the beginning. It could store all the data related to our release, including even more sophisticated metrics like bundle sizes, test coverage, etc.&lt;/p&gt;

&lt;p&gt;I'm personally also keen to open source the tools we build around the processes described above, maybe even make them more generic and therefore useful for others (e.g. by structuring it in a way that allows custom handlers and middleware for each of the performed steps).&lt;/p&gt;

&lt;p&gt;But beyond all, this is obviously still very much moving and work in progress. I'm really keen to see how the latest iteration of our process holds up in production and what next steps we can take to further improve it from here 😊&lt;/p&gt;

</description>
    </item>
    <item>
      <title>A Sea of Red Herring</title>
      <dc:creator>Julian Burr</dc:creator>
      <pubDate>Mon, 04 May 2020 03:06:01 +0000</pubDate>
      <link>https://forem.com/julianburr/a-sea-of-red-herring-7ei</link>
      <guid>https://forem.com/julianburr/a-sea-of-red-herring-7ei</guid>
      <description>&lt;p&gt;The importance of the yarn lock file, a good understanding of  PureComponent and why its worth updating legacy code.&lt;/p&gt;




&lt;h2&gt;
  
  
  Some context - what happened?
&lt;/h2&gt;

&lt;p&gt;This is a story from my work at &lt;a href="https://www.rexlabs.io/"&gt;rexlabs&lt;/a&gt;, showing how easy it is to be mislead when debugging issues and to fall into rabbit holes. Let's start with the backstory to give some context.&lt;/p&gt;

&lt;p&gt;We have our own JS toolkit at rexlabs, very similar to &lt;a href="https://github.com/facebook/create-react-app/tree/master/packages/react-scripts"&gt;&lt;code&gt;react-scripts&lt;/code&gt;&lt;/a&gt; but tailored specifically to our needs and coding conventions we decided on in the frontend team. It allows us to easily develop, build and test packages, components and apps without having to worry about the setup under the hood. We just rewrote large parts of that toolkit, to bring it back up to speed with the latest developments in the ecosystem (e.g. upgrade all dependencies like babel, webpack, jest and storybook). This rewrite touched a lot of the core functionality, so we decided to make it a new major version.&lt;/p&gt;

&lt;p&gt;To test the toolkit and to make sure it didn't only work, but was also as backwards compatible as possible, we used this new version in one of our packages of our component library. For that we chose the simplest package possible, a &lt;code&gt;Box&lt;/code&gt; component. And while at it, why not clean the code up a bit?&lt;/p&gt;

&lt;p&gt;Everything seemed to work great. So, as the next step, we tried upgrading the toolkit in one of our apps. And again, no problems. Everything seemed to work perfectly as intended. Amazing! 🎉&lt;/p&gt;

&lt;p&gt;At the same time, we were experimenting with Typescript. Our toolkit seemed like the obvious place to start, allowing us to introduce the setup through this central library. We created a release candidate containing the changes necessary to enable TS in our app, and again upgraded the toolkit.&lt;/p&gt;

&lt;p&gt;And again, everything seemed to work fine. Except for one bug. Our select components seemed to be broken, not saving the text input value (for searchable selects) correctly. What?&lt;/p&gt;

&lt;h2&gt;
  
  
  Red herring #1: It must be the TS changes, right?
&lt;/h2&gt;

&lt;p&gt;It's obvious. The TS changes introduced the issue, so they must be causing it, right? Well, usually things are not that simple. Yet, since it seemed so obvious, we spent quite a bit of time looking at the problem from this perspective.&lt;/p&gt;

&lt;p&gt;After too many hours of debugging and pulling our hair out, we finally took a step back. There was just nothing in the TS changes that would explain this weird bug. So, what if it wasn't related.&lt;/p&gt;

&lt;p&gt;To verify whether or not the TS changes were the cause, we simply downgraded our toolkit again. And … the problem still persisted!? 🤔&lt;/p&gt;

&lt;p&gt;Ok, so was it related to the changes we made before when rewriting parts of the toolkit, and we just didn't catch the problem earlier? Again, we downgraded the toolkit even further, and … still no luck! The problem was still there.&lt;/p&gt;

&lt;p&gt;Even more confusing: when creating a fresh branch off master, and simply upgrading the toolkit again, we weren't able to replicate the issue.&lt;/p&gt;

&lt;p&gt;Wtf!? Whenever you find yourself in such a situation with a lot of seemingly contradictory evidence, it's almost certain that you found it: the infamous red herring 😅&lt;/p&gt;

&lt;h2&gt;
  
  
  Red herring #2: Maybe it's a completely unrelated change we did to select?
&lt;/h2&gt;

&lt;p&gt;Ok, so it wasn't related to the toolkit. So it must be the select component itself, right?&lt;/p&gt;

&lt;p&gt;But we didn't change the component or its version in the &lt;code&gt;package.json&lt;/code&gt;. Nevertheless, since we were out of ideas, we shifted our focus to debugging the package of the select component, to at the very least understand what the core of the issue actually was.&lt;/p&gt;

&lt;p&gt;In isolation (e.g. in storybook within our component library), the component worked as intended. The value for the text field within the select component is managed via context at the very top level of the component. This is to allow the select to be composed by whatever parts and components you want, without loosing any of the core functionality. Since it is one of our older components, it's still using the &lt;a href="https://reactjs.org/docs/legacy-context.html"&gt;legacy context API&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;While still unable to explain why the issue suddenly popped up in that one feature branch of our app, we were at least able to see that the context was the core of the problem. The onChange event triggered the context to update but the text input didn't receive the new context. But how could that be? Select didn't depend on any other components that could have changed.&lt;/p&gt;

&lt;p&gt;Again, it just seemed like another red herring 🤔&lt;/p&gt;

&lt;h2&gt;
  
  
  Starting to look through the red herring: What dependencies did actually change?
&lt;/h2&gt;

&lt;p&gt;Something must have changed. Something that didn't change when we tried to create a fresh test branch. And what's the easiest way to check what changed? The &lt;code&gt;yarn.lock&lt;/code&gt; file!&lt;/p&gt;

&lt;p&gt;This is something we should have looked at way earlier. Turns out, in the first test branch we deleted the yarn.lock in an attempt to re-install dependencies in a fresh state. Something, we do quite often to be honest. Usually to fix weird npm issues, or simply because of merge conflicts in the yarn.lock. Instead of manually resolving those, it's much easier to just delete it, reinstall dependencies and use that newly generated version.&lt;/p&gt;

&lt;p&gt;However this strategy comes with a trade-off. Every dependency, that is defined as a range in the &lt;code&gt;package.json&lt;/code&gt;, will look for the latest available version matching that range, meaning dependencies will potentially upgrade.&lt;/p&gt;

&lt;p&gt;And that's what happened here. When we removed the lock file and reinstalled dependencies, A &lt;em&gt;lot&lt;/em&gt; of them changed to a newer version. But the select wasn't one of them. So was that really the root of everything?&lt;/p&gt;

&lt;p&gt;Simple test to verify this: we changed to the second test branch that didn't have the problem, deleted the lock file and reinstalled the dependencies. And rest assure, now the issue was also present in that second branch. This is a good sign. Everything that proofs a theory and adds sense to the situation is a step closer to get out of the sea of red herring.&lt;/p&gt;

&lt;p&gt;However, we still didn't have any idea of which of these dependencies was causing the problem. A quick look through the packages didn't reveal any obvious candidate either. So we just bit the bullet and locked down one dependency at a time to its old version. This, ladies and gentlemen, is what desperation looks like. Welcome to the fun festival that is frontend engineering. 😅&lt;/p&gt;

&lt;h2&gt;
  
  
  The light at the end of the tunnel: Looking at the Box component
&lt;/h2&gt;

&lt;p&gt;Ok, this could have taken ages. But "luckily" we fairly quickly stepped on the one dependency that was the cause of the problem, the source of all evil. The Box component. In hindsight we probably should have thought about it, since it was one of the other changes that we did, but after all the changes we did were so trivial, how could they cause the problem.&lt;/p&gt;

&lt;p&gt;Taking a closer look at the changes made it clear pretty quickly. For that, another piece of context though: at rexlabs we decided a while ago to use &lt;a href="https://reactjs.org/docs/react-api.html#reactpurecomponent"&gt;&lt;code&gt;React.PureComponent&lt;/code&gt;&lt;/a&gt; over &lt;a href="https://reactjs.org/docs/react-api.html#reactcomponent"&gt;&lt;code&gt;React.Component&lt;/code&gt;&lt;/a&gt; by default, mainly to reduce confusion especially for new devs when to use which. That obviously wasn't one of our best decisions, but that's not the point of this article. However, this very likely caused me, when cleaning up the Box component, to change it from using Component to use PureComponent instead.&lt;/p&gt;

&lt;p&gt;Now, if you remember from earlier, there are two facts about the select component that make this change problematic. &lt;/p&gt;

&lt;p&gt;First of all, it uses legacy context, which &lt;a href="https://medium.com/@gobindathakur/problems-with-previous-react-context-api-317b247d78d4"&gt;famously has problems with PureComponent&lt;/a&gt;, since its method to check whether or not the component needs to re-render doesn't take context changes into consideration. But that would only be a problem if there is a pure component in between the context provider and the consumer.&lt;/p&gt;

&lt;p&gt;That's where the second fact comes in: the select as a whole can be composed by any components you want. And in our app, we used the Box component to allow for more complex design requirements as part of the select values.&lt;/p&gt;

&lt;p&gt;There we have it, changing Box to a pure component meant that the input rendered within the Box didn't have access to context changes anymore, which meant changes to the input value in the context didn't get passed down anymore. It's so obvious, once you know it 🙈&lt;/p&gt;

&lt;h2&gt;
  
  
  Lessons learned
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The yarn lock file has an important purpose, so be aware of it
&lt;/h3&gt;

&lt;p&gt;The lock file is there for a reason. It locks down dependencies to certain versions. As mentioned before, deleting this file means all dependencies will change to the latest version that still matches the ranges defined in your &lt;code&gt;package.json&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Lesson learned: be aware of this, and always double check the lock file after deleting it and reinstalling dependencies, if you really have to do so.&lt;/p&gt;

&lt;h3&gt;
  
  
  Understand the difference between Component and PureComponent
&lt;/h3&gt;

&lt;p&gt;I was aware of the issues with pure components and legacy context, and yet I didn't think of it when looking at the issue. The main take away from that is: always be aware of what changes you are making to your code, components and libraries and what impacts these changes might have in a larger context. &lt;/p&gt;

&lt;p&gt;Also, if you weren't aware of the issues with legacy context, well, now you are. So take this into account when considering upgrading your code to the "new" context API.&lt;/p&gt;

&lt;h3&gt;
  
  
  Try to keep your libraries up to date
&lt;/h3&gt;

&lt;p&gt;This ties into the next lesson. It is definitely worth keeping your code up to date. If we had updated Select to use the new context API, none of this would have happened. I guess we still learned a lot of valuable lessons from this, but one of them is to try to keep iterating over your libraries and components when sensible and possible.&lt;/p&gt;

&lt;h3&gt;
  
  
  If you struggle with a problem, take a step back
&lt;/h3&gt;

&lt;p&gt;This is not groundbreaking, but a lesson I keep learning over and over again, not only in regards to JS debugging, but as a general lesson in life. If you struggle with something and can't seem to find a way out of it, it's always worth taking a deep breath and a step back, looking at the problem from a different perspective. Red herring are everywhere. And it's just too easy to get lost in them.&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
