﻿<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" xmlns:googleplay="http://www.google.com/schemas/play-podcasts/1.0"><channel><title><![CDATA[Optimism (for the web)]]></title><description><![CDATA[I write about things in tech and web development that make me optimistic for the future.]]></description><link>https://leerob.substack.com</link><image><url>https://substackcdn.com/image/fetch/$s_!Hc2w!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Fad2c3449-520e-4ae9-8d9a-0c1acc055a13_500x500.png</url><title>Optimism (for the web)</title><link>https://leerob.substack.com</link></image><generator>Substack</generator><lastBuildDate>Fri, 19 Jun 2026 21:41:00 GMT</lastBuildDate><atom:link href="https://leerob.substack.com/feed" rel="self" type="application/rss+xml"/><copyright><![CDATA[Lee Robinson]]></copyright><language><![CDATA[en]]></language><webMaster><![CDATA[leerob@substack.com]]></webMaster><itunes:owner><itunes:email><![CDATA[leerob@substack.com]]></itunes:email><itunes:name><![CDATA[Lee Robinson]]></itunes:name></itunes:owner><itunes:author><![CDATA[Lee Robinson]]></itunes:author><googleplay:owner><![CDATA[leerob@substack.com]]></googleplay:owner><googleplay:email><![CDATA[leerob@substack.com]]></googleplay:email><googleplay:author><![CDATA[Lee Robinson]]></googleplay:author><itunes:block><![CDATA[Yes]]></itunes:block><item><title><![CDATA[Reflections on the React community]]></title><description><![CDATA[I&#8217;ve been using React professionally for almost 10 years across three different companies.]]></description><link>https://leerob.substack.com/p/reflections-on-the-react-community</link><guid isPermaLink="false">https://leerob.substack.com/p/reflections-on-the-react-community</guid><dc:creator><![CDATA[Lee Robinson]]></dc:creator><pubDate>Mon, 11 Aug 2025 00:29:01 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/7ae40b3f-6c66-4ce4-b75d-97bd21e603b4_2578x1936.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>I&#8217;ve been using React professionally for almost 10 years across three different companies. <strong>I love React</strong>. It&#8217;s responsible for much of my own personal growth as a product engineer and has helped me build wonderful things.</p><p>I started using Next.js in 2018. From 2018 to 2020, I independently created content and taught the React community. And then for the past 5 years, I helped manage and grow the Next.js community at Vercel.</p><p>This is a post I&#8217;ve wanted to write, but felt I could only write it after stepping away from my official Next.js role.</p><h2><strong>The overwhelming positive side</strong></h2><p>React is &#8220;<a href="https://boringtechnology.club/">boring tech</a>&#8221; at this point.</p><p>Every year, React continues to grow and gain adoption. The &#8220;React way&#8221; clicks with many developers, and that has led to it becoming one of the largest ecosystems of developers, packages, and educational content for building on the web.</p><p>This comes down to two main things for me: composition and stability. The composition model of React was the right architecture choice in the beginning, and continues to have benefits. For example, AI models are able to generate encapsulated components you can drop into your app without affecting global state. Local reasoning is awesome.</p><p>React is also stable. The core APIs rarely change and there&#8217;s great backwards compatibility, and most of the innovation comes from other tools or frameworks trying new ideas on top of the core React foundation. You could argue that ecosystem thrash reflects on React itself, but I&#8217;m not so sure. It&#8217;s popular enough that some people are bound to use it incorrectly.</p><p>With that being said, you might have felt that the React of today is not the same as the React of 2015-2020. Why is that?</p><h2><strong>Communities are hard</strong></h2><p>Let me start with a personal note. Five years of managing the Next.js community was hard. There are so many great things to reflect on, but also a lot of unneeded stress and frustration. I&#8217;m proud of the work I did and the community that we all built together, but at the same time, it does feel good to step away and write this.</p><p>It takes dedicated people to persevere in growing a large, open-source project over 10 years like React. This is why some of the most successful OSS projects have BDFLs (Benevolent Dictator For Life). DHH has some great <a href="https://www.youtube.com/watch?v=cE0-HZ-FmF4&amp;pp=ygUHZGhoIGxleA%3D%3D">rants</a> on this. He&#8217;s building Rails as a gift back to the community, not as a commercial OSS project. He doesn&#8217;t mind telling people no when they make their demands. &#8220;PRs are welcome&#8221; as they say.</p><p>The reason I&#8217;m starting by talking about community management, is that I can empathize with the React team (this includes current and former members, those at Meta and outside). They built something incredible and poured their heart into it. Showing up and having a voice in the community takes a lot of time and energy. I get why some have tuned out and stopped engaging.</p><p>The problem to me is that React isn&#8217;t a self-governing community. It requires work.</p><h2><strong>Listening to feedback</strong></h2><p>As the size of the React community has grown, so has the amount of feedback. Some of that feedback is good, some bad, but in both cases community members are taking time to share how they feel about a piece of technology. That&#8217;s a gift, even if there is a signal/noise filter that needs to be applied to some percentage.</p><p>Now, if we take the most pessimistic view, you could argue: Lee, none of that community management stuff matters. Clearly React continues to <a href="https://npmtrends.com/react">grow</a> and get more popular, even without the React team being active in community discussions. They built a great tool and that&#8217;s all that matters. Why waste time on all of those discussions? They know the right thing to build and all of that feedback is just noise.</p><p>There&#8217;s some truth to that. Will React continue to grow even if the team never responds to another tweet, GitHub discussion, or Reddit post? Yep, definitely. There&#8217;s also the human side of that argument. It is draining to process and filter negative feedback into something constructive that can be used to push an OSS project forward.</p><p>But when you separate yourself from the community as a tool author, it means other voices will fill the void. Those voices have their own incentives and don&#8217;t have the best knowledge about how React works. This can lead to FUD (fear, uncertainty, doubt) and topics running on for much longer than they need to.</p><p>A few recent examples that come to mind are the CRA deprecation drama and the &#8220;Vite isn&#8217;t as prominent in the docs&#8221; drama. Did both the community and React core team have some good points? Yeah. Did it need to escalate that far? No. Were there good intentioned people on both sides? Yes.</p><p>If for nothing else, one of the reasons to have DevRel for OSS projects, is so that it is someone&#8217;s full time job to do this work. If it&#8217;s not their job, then other stuff rightly comes up and gets in the way, like building the actual library. Core developers don&#8217;t want to sink a bunch of emotional and mental energy into wording the right response, knowing they will invariably get negative feedback.</p><h2><strong>Commercial vs non-commercial OSS</strong></h2><p>React is not a commercial project. Meta/Facebook made React and give it away for free.</p><p>You can try to argue the other side that React is beneficial for them: React brings talented developers to Meta, it helps improve their core product experience, they get bug fixes and improvements from outside the Meta team, and more.</p><p>And yes, some of that is true. I&#8217;d argue there&#8217;s not that many React bugs being fixed outside the core team. My larger point is that those benefits are <em><strong>tiny</strong></em> in comparison to the cost of employing people who work on React. Think about how many R&amp;D dollars were spent on the React Compiler, just for them to give it to everyone 100% free.</p><p>Meta isn&#8217;t making money off React. They are not financially incentivized to grow the project. They built something amazing and gave it back to the community. We should be thankful for that.</p><p>Where things start to get tricky is when there are <em><strong>commercial</strong></em> OSS projects built on top of React. Their incentives are different. They <em><strong>do</strong></em> want to grow their project, because it is usually relevant to the growth of the company. For example:</p><ul><li><p>Gatsby, which was a React framework, had an infrastructure business called Gatsby Cloud. They were incentivized to grow the usage of Gatsby in the hope that some percentage would choose to use Gatsby Cloud. Their business ultimately did not work and they were acquired by Netlify.</p></li><li><p>Remix, which was a React framework, had a business model where you would pay for access to the framework. This was probably one of the clearest incentive structures possible. It did work, and they were acquired by Shopify.</p></li><li><p>Astro, which is a general web framework, <a href="https://astro.build/blog/the-astro-technology-company/">took funding</a> and eventually launched a <a href="https://www.youtube.com/watch?v=FBWxEywo85Q">managed database</a> from the backing company. The DB was spun down. I haven&#8217;t caught up on the latest there.</p></li><li><p>Redwood, which is a React framework, had an initial business model to help support startups. It was funded by the founder of GitHub. They have since pivoted to be a framework specifically for Cloudflare. There is prior art with <a href="https://flareact.com/">Flareact</a>, and then <a href="https://hono.dev/">Hono</a>, which is built by a Cloudflare employee.</p></li></ul><p>And then there&#8217;s Next.js. The business model of Vercel is to primarily provide managed infrastructure, or a &#8220;platform as a service&#8221;, which aligns with the incentive to grow Next.js.</p><p>Both of these things are true: Vercel doesn&#8217;t need to monetize Next.js to have a successful infrastructure business, as they support many different frontend and API frameworks. But also, as Next.js becomes more popular, some percentage of that usage does end up on Vercel.</p><p>As if getting eyeballs and usage of your OSS project wasn&#8217;t enough, you must also build a sustainable business on top. And if you take funding from venture capital, the goal isn&#8217;t a business that just does well, but a business that does exceptional in the category on both adoption and revenue metrics.</p><p>Companies have tried the Vercel &amp; Next.js business model, but almost always fail. Vercel has been an <em><strong>exception</strong></em> at $200M in revenue and growing, not the rule to follow going forward. Expo is another successful example.</p><p>So why did I give all of that context? Because commercial and non-commercial OSS have different relationships with the community. React doesn&#8217;t have a financial obligation to respond to GitHub issues. It&#8217;s more like &#8220;read-only OSS&#8221;. Next.js <em><strong>does</strong></em>. If Next.js doesn&#8217;t do right by the community, the project will not succeed.</p><p>This is why you continue to see Next.js growing and evolving based on community feedback. I spent a lot of energy helping people <a href="https://www.youtube.com/watch?v=sIVL4JMqRfc">self-host</a> Next.js successfully, and Next.js will soon have deployment adapters to make it easy to deploy to any managed cloud provider.</p><h2><strong>React Server Components (RSC)</strong></h2><p>The React team had the ambition to extend React&#8217;s composition model across the network boundary, bringing the React you love on the frontend to the backend as well.</p><p>This was a massive project. One that would require financial backing to build out. As mentioned above, Meta was not incentivized to build this. So what can the React team do to make this vision a reality?</p><p>This is how Vercel got more involved in helping contribute to React core going back to 2021. Some members of the React team joined Vercel, and React evolved from a single-company to multi-company project. There are other independent, non-Meta folks on the React team as well, but Vercel was now the biggest outside corporate backer.</p><p>So, Vercel invests millions of R&amp;D dollars into building new React features. This includes React Server Components, Actions, any other features eventually included in <a href="https://react.dev/blog/2024/12/05/react-19">React 19</a> years later.</p><p>Many of these new React APIs weren&#8217;t yet usable by the broader community until they had been validated in the real world. This meant being integrated into frameworks and getting deployed in customer-facing applications. Like I mentioned, this wasn&#8217;t happening at Meta.</p><p>This is why the pairing with Next.js made a lot of sense. The React APIs were being co-created and validated in the Next.js App Router, such that they could then be released more generally on React stable channels for the broader community. My favorite example of this are <a href="https://react.dev/reference/react/useActionState">React Actions</a>, which work without a server or framework!</p><p>Now, we can litigate exactly how these improvements were communicated and released to the world. There&#8217;s plenty of missteps here, including those made by yours truly. The App Router was likely marked stable too soon, with the intention of declaring the API surface area as stable (not that it was generally bug-free). Obviously that was a mistake, as people felt it was pushing a sub-par RSC experience too soon.</p><p>Further, the incentive structure here meant that it was unlikely and/or difficult for other frameworks to invest the same amount of time and energy into integrating these APIs as the Next.js team did. I do think the React team could have done a better job of bringing the community along for this ride in the open, and at the same time, I understand their hesitation.</p><p>Ideas are fragile. I&#8217;m sure the React team felt like everyone would have strong opinions about RSC and the API design, and that led them to be more reserved in what they shared. Plus, they probably felt burned from sharing Suspense and Concurrent Features work early in the past. Sure, there were some React Labs posts every now and then, but RSC was developed quite differently than say <a href="https://github.com/sveltejs/svelte/discussions/15845">Svelte adding async support</a>. I see both sides.</p><p>I wish I had been more vocal in the community about how RSC was being built. I did talk about this at conferences, which had calm, intentional, and thoughtful discussions with members of the React community.</p><p>Posting online, I&#8217;d get feedback like &#8220;you&#8217;re a shill stop pushing RSC to sell more Vercel&#8221;. This made me more reserved sharing openly, even though I did believe we were building something amazing for the <em><strong>entire</strong></em> React community.</p><h2><strong>RSC in 2025</strong></h2><p>Now in 2025, we have RSC stable and related features like Actions and Transitions shipped. But putting technology out into the world doesn&#8217;t mean the community is immediately going to run with it and build an ecosystem on top.</p><p>Again, this is likely a place I could have done better with Next.js. Most of the &#8220;RSC innovation&#8221; happening in the ecosystem was from those building on Next.js. It was still very difficult to build non-Next.js RSC things, largely because RSC is a <em><strong><a href="https://overreacted.io/why-does-rsc-integrate-with-a-bundler/">bundler</a></strong></em><a href="https://overreacted.io/why-does-rsc-integrate-with-a-bundler/"> feature</a>.</p><p>Some savvy developers were able to build their own bundler hacks, or build off the webpack plugin, but there weren&#8217;t docs and any guidance for how to navigate that journey&#185;. Moment of vulnerability here, this was also not my strong suit. I&#8217;m not a bundler developer and in fact, part of the reason I liked Next.js was because I never wanted to think about bundlers again. Shoutout to Dan for his <a href="https://overreacted.io/">great writing on RSC</a>.</p><p>The result of this was that it took many years before there was a community-led effort to get RSC in other places besides Next.js, in a <em><strong>production-like</strong></em> way. There were plenty of experiments and side projects, like <a href="https://waku.gg/">Waku</a> and <a href="https://x.com/leerob/status/1772651496544317804">others</a>, but <a href="https://parceljs.org/recipes/rsc/">Parcel</a> was the first bundler to give us an abstraction that you could ship to prod with.</p><p>Since then, many of the Vite plugin authors building their own RSC implementations collaborated on building an official Vite plugin for RSC. And thanks to <a href="https://github.com/hi-ogawa">hi-ogawa</a> and others, we now have <code>@vitejs/plugin-rsc</code>. We also have <a href="https://remix.run/blog/react-router-and-react-server-components">RSC for React Router</a>. Nature is healing?</p><h2><strong>Community burnout</strong></h2><p>One of the most frustrating parts of my previous job working on Next.js was trying to explain the above context to the React community. It was a far easier story for people to say &#8220;Vercel is taking over React so they can sell more compute in the cloud&#8221;. Nuance didn&#8217;t scale there. Maybe a skill issue on my part.</p><p>And I can understand the hesitation, especially when you re-read the list above of other React frameworks trying to do something similar. As I mentioned, the Next.js and Vercel business model is <em><strong>not</strong></em> one that normally works.</p><p>So eventually, I stopped trying to fight this fight, and then ultimately stopped engaging. You might say: but Lee, there are millions of Next.js developers who are happy building things and never contributing to the discourse. And sure, that is true! I did appreciate those people.</p><p>My bigger issue was that other prominent people in the React community never took the time to fully grok what I was saying above. It often felt like I was talking to a brick wall trying to convince others what we were doing at Vercel and Next.js was good for the community and worth their time.</p><p>In the fullness of time, I expect this to change. I have confidence RSC will be incredibly impactful and be largely regarded as the third generation of React (if you consider hooks the second). I expect a lot of people who dogged on RSC will eventually come around. But hey, now I get to watch how the story plays out from the sideline.</p><p>My call to action for anyone in the React community today, and hoping to grow with it over time, is the following: remember there are humans working on the software, and they want to be treated with kindness and respect just like you. Remember that OSS is a gift and you are not entitled to make demands without putting any energy forward yourself. And above all else, be the optimistic change you want to see in the world.</p>]]></content:encoded></item><item><title><![CDATA[A new chapter]]></title><description><![CDATA[It&#8217;s been almost a year since my last update.]]></description><link>https://leerob.substack.com/p/a-new-chapter</link><guid isPermaLink="false">https://leerob.substack.com/p/a-new-chapter</guid><dc:creator><![CDATA[Lee Robinson]]></dc:creator><pubDate>Mon, 21 Jul 2025 00:54:17 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/dc382e57-762c-46c3-9052-beb0d8a5330c_1536x1024.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>It&#8217;s been almost a year since my last update. A lot has changed:</p><ol><li><p>After 5 years at Vercel, I finished up <a href="https://leerob.com/vercel">earlier this month</a>.</p></li><li><p>I joined Cursor to <a href="https://x.com/leerob/status/1946323104692945188">teach developers about AI</a> and the future of coding.</p></li><li><p>I wrote about making <a href="https://leerob.com/personal-software">personal software</a>.</p></li><li><p>I wrote about <a href="https://leerob.com/">things I believe</a> on my updated website.</p></li><li><p>I blinked and my daughter is 10 months old &#129327;</p></li></ol><p>Like many of you, I&#8217;m finding that AI is changing how I build software. It started out as a fancy autocomplete, but has since become a core part of my coding workflow.</p><p>And that&#8217;s&#8230; strange, if I&#8217;m being honest. The way I wrote software 10 years ago feels archaic. You mean I can just ask an agent to code the feature and it will work!? Wild times.</p><p>But this new world has a lot of problems, too. Especially for beginners. I start debugging a problem with them, and they tell me how they use AI to write code, only to realize <strong>they don&#8217;t actually know how AI models work.</strong></p><p>And I don&#8217;t mean the <em>machine learning</em> part (although that doesn&#8217;t hurt). They don&#8217;t understand determinism vs. non-determinism. When they don&#8217;t get good answers one day, they think the tools are broken. Wrong. They are missing the correct mental model.</p><p>It&#8217;s made me realize there is a massive gap in AI education. More people are becoming developers because of AI, but they&#8217;re not learning the right skills to actually succeed. They vibe code their app in a few days, but then next week they&#8217;re in a death spiral of errors with no path to escape.</p><p>I don&#8217;t have the answers, but I do want to start contributing some solutions. So expect to see more content explaining the fundamentals of AI models, how to actually use them to get work done, and hopefully I can start to help push things forward.</p>]]></content:encoded></item><item><title><![CDATA[The JavaScript ecosystem in 2024]]></title><description><![CDATA[When there's competition in DevTools, developers win]]></description><link>https://leerob.substack.com/p/the-javascript-ecosystem-in-2024</link><guid isPermaLink="false">https://leerob.substack.com/p/the-javascript-ecosystem-in-2024</guid><dc:creator><![CDATA[Lee Robinson]]></dc:creator><pubDate>Wed, 30 Oct 2024 01:20:10 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/63efba97-37be-4342-8125-49335e150ed4_2104x1138.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Three years ago, I tweeted the following:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!HUkZ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F60075639-963d-44c8-b053-e7d4042b4c80_1386x964.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!HUkZ!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F60075639-963d-44c8-b053-e7d4042b4c80_1386x964.png 424w, https://substackcdn.com/image/fetch/$s_!HUkZ!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F60075639-963d-44c8-b053-e7d4042b4c80_1386x964.png 848w, https://substackcdn.com/image/fetch/$s_!HUkZ!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F60075639-963d-44c8-b053-e7d4042b4c80_1386x964.png 1272w, https://substackcdn.com/image/fetch/$s_!HUkZ!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F60075639-963d-44c8-b053-e7d4042b4c80_1386x964.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!HUkZ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F60075639-963d-44c8-b053-e7d4042b4c80_1386x964.png" width="1386" height="964" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/60075639-963d-44c8-b053-e7d4042b4c80_1386x964.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:964,&quot;width&quot;:1386,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:159026,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!HUkZ!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F60075639-963d-44c8-b053-e7d4042b4c80_1386x964.png 424w, https://substackcdn.com/image/fetch/$s_!HUkZ!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F60075639-963d-44c8-b053-e7d4042b4c80_1386x964.png 848w, https://substackcdn.com/image/fetch/$s_!HUkZ!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F60075639-963d-44c8-b053-e7d4042b4c80_1386x964.png 1272w, https://substackcdn.com/image/fetch/$s_!HUkZ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F60075639-963d-44c8-b053-e7d4042b4c80_1386x964.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>I&#8217;d say it was mostly correct, so I wrote down some thoughts on what was accurate and what has changed.</p><h2>1. Svelte is pushing React</h2><p>Compilers are great, actually. Why not let the computer optimize your code instead of humans? Svelte has always innovated with a compiler-first approach.</p><p><a href="https://svelte.dev/blog/svelte-5-is-alive">Svelte 5</a> continues to evolve with Runes, an explicit way of defining reactive state. On the React side, the React Compiler is finally available for use (now in <a href="https://react.dev/blog/2024/10/21/react-compiler-beta-release">beta</a>) after many years of development.</p><p>Compiler-friendly directives like <code>'use client'</code> and <code>'use server'</code> have become core to the React Server Components <a href="https://react.dev/reference/rsc/use-client">programming model</a>. And React frameworks like Next.js are also adding <a href="https://nextjs.org/blog/our-journey-with-caching">their own directives</a>, like <code>'use cache'</code>.</p><h2>2. Remix is pushing Next.js</h2><p>When my original tweet was posted, the Next.js App Router <em>wasn't even released</em>. I remember two of our top requests for the Pages Router were better support for layouts and global data fetching (e.g. <code>_app.js</code>).</p><p>Remix iterated on the <code>getServerSideProps</code> model of Next.js, allowing more granular data fetching, and adding built-in conventions for layouts. Ultimately, there's been a convergent evolution towards the Server Components and Server Actions model, with Remix helping shape some of the <a href="https://www.youtube.com/watch?v=AdkNcFUsRQQ">React 19 features</a> as well.</p><p>The outcome has been, in my opinion, a better ecosystem for all React developers.</p><h2>3. Prisma is pushing ORMs</h2><p>Prisma has raised the bar for how the JS/TS ecosystem works with databases. Having great TypeScript support is now table stakes. For example, Drizzle has taken this a step further by being able to define your schema and relations in TypeScript (versus the Prisma schema file).</p><p>Prisma should also get credit for helping push the entire "data layer" forward to be more lightweight and runtime-agnostic. More libraries and vendors now use Web standard APIs, which means better interoperability between platforms.</p><p>With that context, it makes sense that you now see a <a href="https://www.prisma.io/blog/announcing-prisma-postgres-early-access">Prisma-managed Postgres database</a> that connects over HTTP.</p><h2>4. Deno is pushing Node.js</h2><p>This was an understatement. Node.js has improved <em>so much</em> in the past three years.</p><p>I'd amend this original statement to add Bun as well. Multiple runtimes has meant more visibility and work towards performance, developer experience, and solving long-standing papercuts.</p><p>For example, can you believe we have native support for CJS/ESM interop in <a href="https://nodejs.org/en/blog/announcements/v22-release-announce">Node.js 22</a>? Amazing. Notably, <a href="https://deno.com/blog/v2.0">Deno 2</a> is now stable and has backward compatibility with Node.js.</p><h2>5. Supabase is pushing Firebase</h2><p>I haven't kept up as much with Firebase, because Supabase has largely replaced it (for me). That&#8217;s not to say Firebase is bad; I still see it frequently used, especially when building web and mobile apps together.</p><p>More so that Supabase has won the heart and minds of developers &#8212; and they've been able to ride the wave of Postgres love. I'd amend this to say Supabase has pushed the entire BaaS (backend-as-a-service) category forward.</p><h2>6. esbuild / SWC are pushing JS tooling</h2><p>After my original tweet, I <a href="https://leerob.com/n/rust">wrote an article</a> about how Rust was becoming the future of JavaScript tooling infra. That prediction turned out to be true.</p><p>Now in 2024, we have Biome, Rspack, Rolldown, Oxc, Lightning CSS, Turbopack, and more. This includes Vite/Rolldown/Oxc <a href="https://voidzero.dev">raising funding</a> to continue investing in Rust tooling.</p><p>Notably, Turbopack Dev is now stable in Next.js 15. The team wrote up a very detailed post <a href="https://nextjs.org/blog/turbopack-for-development-stable">talking about the journey</a>.</p><h2>7. Bun is pushing SWC</h2><p>Bun has pushed package managers, compilers, test runners, and other runtimes forward. It's shown there's many places to speed up the JavaScript ecosystem, if we try hard enough.</p><p>It's already been a year since Bun 1.0, and adoption seems to continue to rise. The iteration velocity of the team is incredibly high.</p><div><hr></div><p>Now in 2024, what tools are pushing other tools? What will we look back on and compare in three years?</p>]]></content:encoded></item><item><title><![CDATA[Summer 2024]]></title><description><![CDATA[A quick recap of my past few months.]]></description><link>https://leerob.substack.com/p/summer-2024</link><guid isPermaLink="false">https://leerob.substack.com/p/summer-2024</guid><dc:creator><![CDATA[Lee Robinson]]></dc:creator><pubDate>Sat, 19 Oct 2024 20:41:36 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F577c5c6a-ac6e-42a6-9dc3-24b8a599d484_2426x1778.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>It&#8217;s been a few months since we last spoke, so time for an update:</p><ul><li><p>I became a dad (6 weeks in, with a happy daughter and wife &#8212; so thankful)</p></li><li><p>I shipped a SaaS template, music player, books inventory, and other new apps</p></li><li><p>I got a new domain (<a href="http://leerob.com">leerob.com</a>) and refreshed my site</p></li><li><p>I made a bunch of demos, tutorials, and quick tip videos</p></li><li><p>I learned Vim and changed editors to Neovim</p></li></ul><h2>My SaaS template</h2><p>Four years ago, I made a React and Next.js course showing how to build a SaaS application.</p><p>I wanted to show how an individual developer could go from zero to a real product that accepts money on the internet. There&#8217;s of course many other ways to achieve this same outcome &#8212; whether it be PHP (Laravel), Ruby (Rails), and others. But the React component model meshed with my brain, and has been my preference for writing UIs since 2015.</p><p>Next.js enabled me to write my entire application in one programming language (TypeScript) and write code for both the client and server. Now four years later, it&#8217;s even easier to build this same application with React Server Components, Server Actions, and other new features.</p><p>So, I built a new template. It uses the latest Next.js and React patterns. These patterns simplify common tasks like building forms, talking to your database, and more.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!zlqJ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F38a66de7-0e47-42d1-bcc5-bef35777e77b_2426x1778.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!zlqJ!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F38a66de7-0e47-42d1-bcc5-bef35777e77b_2426x1778.png 424w, https://substackcdn.com/image/fetch/$s_!zlqJ!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F38a66de7-0e47-42d1-bcc5-bef35777e77b_2426x1778.png 848w, https://substackcdn.com/image/fetch/$s_!zlqJ!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F38a66de7-0e47-42d1-bcc5-bef35777e77b_2426x1778.png 1272w, https://substackcdn.com/image/fetch/$s_!zlqJ!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F38a66de7-0e47-42d1-bcc5-bef35777e77b_2426x1778.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!zlqJ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F38a66de7-0e47-42d1-bcc5-bef35777e77b_2426x1778.png" width="1456" height="1067" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/38a66de7-0e47-42d1-bcc5-bef35777e77b_2426x1778.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1067,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:566126,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!zlqJ!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F38a66de7-0e47-42d1-bcc5-bef35777e77b_2426x1778.png 424w, https://substackcdn.com/image/fetch/$s_!zlqJ!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F38a66de7-0e47-42d1-bcc5-bef35777e77b_2426x1778.png 848w, https://substackcdn.com/image/fetch/$s_!zlqJ!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F38a66de7-0e47-42d1-bcc5-bef35777e77b_2426x1778.png 1272w, https://substackcdn.com/image/fetch/$s_!zlqJ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F38a66de7-0e47-42d1-bcc5-bef35777e77b_2426x1778.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The app uses Postgres (with Drizzle ORM), Stripe for payments, and Tailwind CSS (with shadcn/ui). There&#8217;s a bunch of goodies to dig into. Cookie-based authentication, RBAC with different roles, global middleware to protect logged-in routes, local middleware to protect Server Actions and validate schemas, and more.</p><p><a href="https://next-saas-start.vercel.app/">Try out the demo</a> or <a href="https://github.com/leerob/next-saas-starter">view the repo</a> (now at 5,000 GitHub stars &#129327;). I hope you find it helpful or interesting. I wrote more about <a href="https://x.com/leeerob/status/1835777934361084316">how it&#8217;s built here</a>.</p><h2>My new portfolio</h2><p>I&#8217;ve wanted <a href="http://leerob.com">leerob.com</a> for a long time, but it was taken with a redirect. Hilariously, from a guy named Rob Lee. As fate would have it, the domain expired and I was able to purchase it &#8212; which seems to be good timing as .io domains <em>might</em> have <a href="https://every.to/p/the-disappearance-of-an-internet-domain">some issues</a> in the future.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!y9IX!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa5891201-ca4f-4038-bb44-43b3d1cc5fe9_2338x1690.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!y9IX!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa5891201-ca4f-4038-bb44-43b3d1cc5fe9_2338x1690.png 424w, https://substackcdn.com/image/fetch/$s_!y9IX!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa5891201-ca4f-4038-bb44-43b3d1cc5fe9_2338x1690.png 848w, https://substackcdn.com/image/fetch/$s_!y9IX!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa5891201-ca4f-4038-bb44-43b3d1cc5fe9_2338x1690.png 1272w, https://substackcdn.com/image/fetch/$s_!y9IX!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa5891201-ca4f-4038-bb44-43b3d1cc5fe9_2338x1690.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!y9IX!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa5891201-ca4f-4038-bb44-43b3d1cc5fe9_2338x1690.png" width="1456" height="1052" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/a5891201-ca4f-4038-bb44-43b3d1cc5fe9_2338x1690.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1052,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:395587,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!y9IX!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa5891201-ca4f-4038-bb44-43b3d1cc5fe9_2338x1690.png 424w, https://substackcdn.com/image/fetch/$s_!y9IX!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa5891201-ca4f-4038-bb44-43b3d1cc5fe9_2338x1690.png 848w, https://substackcdn.com/image/fetch/$s_!y9IX!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa5891201-ca4f-4038-bb44-43b3d1cc5fe9_2338x1690.png 1272w, https://substackcdn.com/image/fetch/$s_!y9IX!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa5891201-ca4f-4038-bb44-43b3d1cc5fe9_2338x1690.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>As tradition goes, I spend time rebuilding my site instead of writing an update. I wanted to focus on simplicity and remove a lot of cruft that had accrued over the years. Simple design, clean typography, and refreshed content.</p><p>I&#8217;m using the View Transitions API with the Next.js App Router for smooth animations between pages. It&#8217;s minimal and I love it.</p><p><a href="https://leerob.com/">Check it out</a> or <a href="https://github.com/leerob/site">view the code</a>. I also wrote two new posts:</p><ul><li><p><a href="https://leerob.com/n/moderation">On Community Moderation</a></p></li><li><p><a href="https://leerob.com/n/stack">My Stack</a></p></li></ul><h2>My minimal music player</h2><p>I have a love/hate relationship with Spotify. It has great playlists. Also, hours of listening data to intelligently recommend new songs to me. But wow, the interface leaves a lot to be desired.</p><p>So I started trying to build a better, information dense version of a music player inside of <a href="https://x.com/leeerob/status/1834992860904489178">v0</a>. I didn&#8217;t plan make it real at the start, but was impressed by how far I got thanks to AI. So, v0 helped me finish the job and get a working MVP.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!8omQ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F577c5c6a-ac6e-42a6-9dc3-24b8a599d484_2426x1778.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!8omQ!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F577c5c6a-ac6e-42a6-9dc3-24b8a599d484_2426x1778.png 424w, https://substackcdn.com/image/fetch/$s_!8omQ!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F577c5c6a-ac6e-42a6-9dc3-24b8a599d484_2426x1778.png 848w, https://substackcdn.com/image/fetch/$s_!8omQ!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F577c5c6a-ac6e-42a6-9dc3-24b8a599d484_2426x1778.png 1272w, https://substackcdn.com/image/fetch/$s_!8omQ!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F577c5c6a-ac6e-42a6-9dc3-24b8a599d484_2426x1778.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!8omQ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F577c5c6a-ac6e-42a6-9dc3-24b8a599d484_2426x1778.png" width="1456" height="1067" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/577c5c6a-ac6e-42a6-9dc3-24b8a599d484_2426x1778.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1067,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1217678,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!8omQ!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F577c5c6a-ac6e-42a6-9dc3-24b8a599d484_2426x1778.png 424w, https://substackcdn.com/image/fetch/$s_!8omQ!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F577c5c6a-ac6e-42a6-9dc3-24b8a599d484_2426x1778.png 848w, https://substackcdn.com/image/fetch/$s_!8omQ!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F577c5c6a-ac6e-42a6-9dc3-24b8a599d484_2426x1778.png 1272w, https://substackcdn.com/image/fetch/$s_!8omQ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F577c5c6a-ac6e-42a6-9dc3-24b8a599d484_2426x1778.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The repo might be interesting to look at. It again uses Postgres (and Drizzle ORM) to save songs and playlists. Album art and playlist images are stored in Vercel Blob storage. It&#8217;s a Progressive Web Application (PWA), so I can install it like an app on macOS or iOS.</p><p><a href="https://next-tracks.vercel.app/?q=">Try out the demo</a> or <a href="https://github.com/leerob/next-music-player">view the repo</a>. I also wrote about some <a href="https://x.com/leeerob/status/1841293100825596149">other interesting details</a> building it, if you&#8217;re curious to learn more.</p><h2>My books inventory app</h2><p>I wanted to build a demo of searching, filtering, and pagination with Next.js &#8212; and needed a very large dataset for the demo. So, I took a Goodreads database of 600,000+ books and dumped it into Postgres.</p><p>This was a fun challenge. Even writing the seed script to get a CSV of that many books into the database was interesting. After some tweaking of database indexes, I&#8217;m pretty happy with the outcome.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!bIRA!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F335f5cd9-38ee-4904-bcce-0ff46a61368f_2338x1690.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!bIRA!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F335f5cd9-38ee-4904-bcce-0ff46a61368f_2338x1690.png 424w, https://substackcdn.com/image/fetch/$s_!bIRA!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F335f5cd9-38ee-4904-bcce-0ff46a61368f_2338x1690.png 848w, https://substackcdn.com/image/fetch/$s_!bIRA!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F335f5cd9-38ee-4904-bcce-0ff46a61368f_2338x1690.png 1272w, https://substackcdn.com/image/fetch/$s_!bIRA!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F335f5cd9-38ee-4904-bcce-0ff46a61368f_2338x1690.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!bIRA!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F335f5cd9-38ee-4904-bcce-0ff46a61368f_2338x1690.png" width="1456" height="1052" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/335f5cd9-38ee-4904-bcce-0ff46a61368f_2338x1690.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1052,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:2868335,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!bIRA!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F335f5cd9-38ee-4904-bcce-0ff46a61368f_2338x1690.png 424w, https://substackcdn.com/image/fetch/$s_!bIRA!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F335f5cd9-38ee-4904-bcce-0ff46a61368f_2338x1690.png 848w, https://substackcdn.com/image/fetch/$s_!bIRA!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F335f5cd9-38ee-4904-bcce-0ff46a61368f_2338x1690.png 1272w, https://substackcdn.com/image/fetch/$s_!bIRA!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F335f5cd9-38ee-4904-bcce-0ff46a61368f_2338x1690.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The app uses URL state exclusively. For example, when you search, the query param updates and causes a React Server Component to re-run, look at the <code>searchParams</code>, and query the database. Same idea for pagination and adjusting filters in the left sidebar.</p><p>If you type into the search, or adjust the filters, your input is preserved when navigating to a book page. This gives it an app-like feel. Similarly, with scroll position &#8212; if you navigate to the 20th page, scroll down, and then click into a book, going back will take you back to your exact spot.</p><p><a href="https://next-books-search.vercel.app/">Try out the demo</a> or <a href="https://github.com/vercel-labs/book-inventory">view the repo</a>. One interesting bit to explore is the generation of the blur-up placeholder images for the books using &#8220;<a href="https://github.com/vercel-labs/book-inventory/blob/6815ee9471d2b0c43c690a6d9ffe67b7b56f033c/lib/db/seed-thumbhash.ts">thumbhashes</a>&#8221;.</p><h2>Learning Vim</h2><p>I am surprised I have never learned Vim until now.</p><p>I&#8217;ve fumbled around in <code>vi</code> before, but never actually gave it a proper go. Since I had some free time, I decided to quit VS Code cold turkey and jump straight into Neovim.</p><p>It&#8217;s been <em>awesome</em>. Vim motions are magic.</p><p>Moving to Neovim shows how slow VS Code is. Further, a lot of the &#8220;magic&#8221; of VS Code is just integrations with Language Server Protocols (LSPs). With a proper Neovim configuration (which didn&#8217;t take long thanks to the Kickstart repo), I was able to match the floor of what VS Code provides, but with a higher ceiling for complete customization.</p><p>To help myself learn Vim, I started writing down notes and a cheat sheet. After tweeting about Vim and my progress, I got a lot of replies asking about resources to learn and my configuration. So I made a <a href="https://vimforreactdevs.com/">quick, free course</a> if you&#8217;re interested in trying it out.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!szZT!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6442e46c-4663-4035-b01c-03c2d5b7fbd0_2430x1780.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!szZT!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6442e46c-4663-4035-b01c-03c2d5b7fbd0_2430x1780.png 424w, https://substackcdn.com/image/fetch/$s_!szZT!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6442e46c-4663-4035-b01c-03c2d5b7fbd0_2430x1780.png 848w, https://substackcdn.com/image/fetch/$s_!szZT!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6442e46c-4663-4035-b01c-03c2d5b7fbd0_2430x1780.png 1272w, https://substackcdn.com/image/fetch/$s_!szZT!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6442e46c-4663-4035-b01c-03c2d5b7fbd0_2430x1780.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!szZT!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6442e46c-4663-4035-b01c-03c2d5b7fbd0_2430x1780.png" width="1456" height="1067" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/6442e46c-4663-4035-b01c-03c2d5b7fbd0_2430x1780.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1067,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:684709,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!szZT!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6442e46c-4663-4035-b01c-03c2d5b7fbd0_2430x1780.png 424w, https://substackcdn.com/image/fetch/$s_!szZT!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6442e46c-4663-4035-b01c-03c2d5b7fbd0_2430x1780.png 848w, https://substackcdn.com/image/fetch/$s_!szZT!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6442e46c-4663-4035-b01c-03c2d5b7fbd0_2430x1780.png 1272w, https://substackcdn.com/image/fetch/$s_!szZT!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6442e46c-4663-4035-b01c-03c2d5b7fbd0_2430x1780.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2>And more!</h2><p>Lots of other smaller ships, like improving the Next.js docs, or quick tip videos showing how a feature works.</p><ul><li><p><a href="https://nextjs.org/docs/app/building-your-application/configuring/progressive-web-apps">Next.js Docs: Progressive Web Apps</a></p></li><li><p><a href="https://nextjs.org/docs/app/building-your-application/data-fetching/fetching">Next.js Docs: Data Fetching</a></p></li><li><p><a href="https://nextjs.org/docs/canary/app/building-your-application/examples">Next.js Docs: Examples</a></p></li><li><p><a href="https://nextjs.org/docs/canary/app/building-your-application/data-fetching/incremental-static-regeneration">Next.js Docs: Incremental Static Regeneration</a></p></li><li><p><a href="https://x.com/leeerob/status/1827437428992622626">Next.js Tip: How </a><code>next/image</code><a href="https://x.com/leeerob/status/1827437428992622626"> works</a></p></li><li><p><a href="https://x.com/leeerob/status/1811891606724968841">Next.js Tip: Active state for nav links</a></p></li><li><p><a href="https://x.com/leeerob/status/1810114442098569625">Next.js Tip: Route Groups</a></p></li><li><p><a href="https://x.com/leeerob/status/1812927466551275625">React Tip: </a><code>server-only</code><a href="https://x.com/leeerob/status/1812927466551275625"> package</a></p></li><li><p><a href="https://x.com/leeerob/status/1810707436551991412">React Tip: Colocated components</a></p></li><li><p><a href="https://www.youtube.com/watch?v=AdkNcFUsRQQ">React Demo: React 19 Features</a></p></li><li><p><a href="https://www.youtube.com/watch?v=zA-eCGFBXjM">Vercel Demo: v0</a></p></li><li><p><a href="https://x.com/leeerob/status/1795224356810834036">Vercel Demo: Firewall</a></p></li><li><p><a href="https://www.youtube.com/watch?v=99U78rS1LeQ">Vercel Demo: Skew Protection</a></p></li><li><p><a href="https://www.youtube.com/watch?v=DAY8ATPBK2k">Vercel Demo: Toolbar</a></p></li><li><p><a href="https://www.youtube.com/watch?v=-N4yZ8KkY4o">Vercel Demo: AI SDK Chatbot</a></p></li><li><p><a href="https://www.youtube.com/watch?v=sIVL4JMqRfc">Next.js Demo: Self-Hosting</a></p></li><li><p><a href="https://www.youtube.com/watch?v=Fl1x57e5i4k">Next.js Demo: Optimistic UI</a></p></li></ul><p>Until next time &#9996;&#65039;</p>]]></content:encoded></item><item><title><![CDATA[On Developer Marketing]]></title><description><![CDATA[What influences developers to adopt a product?]]></description><link>https://leerob.substack.com/p/on-developer-marketing</link><guid isPermaLink="false">https://leerob.substack.com/p/on-developer-marketing</guid><dc:creator><![CDATA[Lee Robinson]]></dc:creator><pubDate>Sun, 24 Mar 2024 16:15:10 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/0b80d940-36c4-4797-854a-6f496c2721d1_1920x1080.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>What influences developers to adopt a product?</p><p>Trust. The tools they adopt and advocate for impact their reputation. You might have heard &#8220;no one ever got fired for buying IBM&#8221; in reference to making good choices.</p><p>But picking older, well-adopted tools can be at odds with how fast the industry moves. Is there a newer alternative that solves my needs better? Ask your developer.</p><p>Keeping up the latest trends is part of their job. They need to know what's a fad and what's here to stay. This is why experienced developers become skeptical of almost everything. It's hard to earn their trust.</p><p>Developer marketing is about building trust. Here's what's worked for me.</p><h2>How can I improve my developer marketing?</h2><ol><li><p>Don&#8217;t publish content you wouldn&#8217;t share yourself.</p></li><li><p>Always consider how you can build developer trust. It&#8217;s not a one-time transaction. It&#8217;s reflected in every product and marketing decision you make.</p></li><li><p>When something sucks with your product, own it. Don&#8217;t try to hide the failure. Lean into it. Bring the community along for the continuous iteration of your product. &#8220;You told us this was bad, and we fixed it&#8221;. Follow up with people after you ship.</p></li><li><p>Get involved in the community. Host, attend, or sponsor a hackathon or meetup. Spend time talking to developers 1:1. Their feedback drives product improvements <em>and</em> content ideas. Where are people struggling? Write about that.</p></li><li><p>Create a <strong><a href="https://leerob.io/blog/developer-experience-examples">developer experience so good</a></strong> that it does marketing for you. Write docs worth sharing, filled with helpful diagrams and detailed descriptions. Create code examples and templates that help developers get started quickly.</p></li></ol><h2>Throw out the old playbook</h2><p>Developers <em>love</em> great marketing, but most companies get it wrong.</p><p>They&#8217;re trying to apply the enterprise marketing playbook to the wrong audience.</p><ul><li><p><strong>Enterprise Marketing</strong></p><ul><li><p><strong>Goal</strong>: Grow pipeline and revenue</p></li><li><p><strong>Audience</strong>: Directors, VPs, or C-suite</p></li><li><p><strong>Buyer Interests</strong>: Increased efficiency, cost savings, innovation</p></li><li><p><strong>How</strong>: Sales-served, sign a yearly contract for a discount</p></li></ul></li><li><p><strong>Developer Marketing</strong></p><ul><li><p><strong>Goal</strong>: Grow retained signups and community awareness</p></li><li><p><strong>Audience</strong>: Individual developers</p></li><li><p><strong>Buyer Interests</strong>: Transferable skills, affordable, great developer experience</p></li><li><p><strong>How</strong>: Self-serve, start free then swipe a credit card</p></li></ul></li></ul><h2>What does great developer marketing look like?</h2><p>I&#8217;m a developer who <strong><a href="https://leerob.io/work">works</a></strong> at the intersection of engineering, product, and marketing. Based on my experience, the best developer marketing:</p><ol><li><p><strong>Teaches how to build great products</strong></p></li><li><p><strong>Builds and retains trust</strong></p></li><li><p><strong>Uses precise language</strong></p></li></ol><h3>How did they build that!?</h3><p>When I see a great developer tool, my first thought is: <strong>how did they build that?</strong></p><div class="pullquote"><p>&#8220;This is so fast it is actually weird, my brain seems unable to accept such fast UI on an ecommerce website&#8221; - <a href="https://twitter.com/dooart_dev/status/1683856255012462593">Thiago Duarte</a></p></div><p>There&#8217;s a natural curiosity. I want to reverse engineer the product and <strong><a href="https://vercel.com/blog/introducing-next-js-commerce-2-0">learn new tools</a></strong> or skills I can apply to my work.</p><p>The best developer marketing shows how to build great product experiences. Bonus points if you capture the &#8220;current thing&#8221; &#8211; a new AI tool, or a popular style of app (TikTok clone).</p><p>Great developer marketing is invoking this response as a service.</p><div class="pullquote"><p>&#8220;My first thought was damn the link navigations are snappy as hell&#8221; - <a href="https://twitter.com/bcjordan/status/1744201046627356885">Brian Jordan</a></p></div><h3>Zero days since the latest JavaScript framework</h3><p>Developers don&#8217;t try your product the first time they hear about it. They certainly don&#8217;t want to talk to someone to get access first.</p><p>They need to hear positive feedback several times before they trust it enough to try. That's why <strong>building, growing, and retaining developer trust is everything.</strong></p><p>Trust comes from your marketing being helpful regardless of the product. And retention comes from <strong><a href="https://leerob.io/blog/make-something-developers-want">making something developers actually want</a></strong>.</p><p>Individual developers are tired of enterprise marketing being used on them. They&#8217;re the wrong audience&#8212;they don&#8217;t want the cold calls or unhelpful emails. And if you lose their trust, they&#8217;re quick to move on to other products.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!gDSR!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6a7afa83-886a-4c62-9f3a-2cc7c31b2afa_680x430.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!gDSR!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6a7afa83-886a-4c62-9f3a-2cc7c31b2afa_680x430.png 424w, https://substackcdn.com/image/fetch/$s_!gDSR!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6a7afa83-886a-4c62-9f3a-2cc7c31b2afa_680x430.png 848w, https://substackcdn.com/image/fetch/$s_!gDSR!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6a7afa83-886a-4c62-9f3a-2cc7c31b2afa_680x430.png 1272w, https://substackcdn.com/image/fetch/$s_!gDSR!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6a7afa83-886a-4c62-9f3a-2cc7c31b2afa_680x430.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!gDSR!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6a7afa83-886a-4c62-9f3a-2cc7c31b2afa_680x430.png" width="680" height="430" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/6a7afa83-886a-4c62-9f3a-2cc7c31b2afa_680x430.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:430,&quot;width&quot;:680,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;I don't want to play with you anymore&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="I don't want to play with you anymore" title="I don't want to play with you anymore" srcset="https://substackcdn.com/image/fetch/$s_!gDSR!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6a7afa83-886a-4c62-9f3a-2cc7c31b2afa_680x430.png 424w, https://substackcdn.com/image/fetch/$s_!gDSR!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6a7afa83-886a-4c62-9f3a-2cc7c31b2afa_680x430.png 848w, https://substackcdn.com/image/fetch/$s_!gDSR!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6a7afa83-886a-4c62-9f3a-2cc7c31b2afa_680x430.png 1272w, https://substackcdn.com/image/fetch/$s_!gDSR!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6a7afa83-886a-4c62-9f3a-2cc7c31b2afa_680x430.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Trust is built (or lost) through the content you create and put out into the community. It&#8217;s a reflection of what you stand for.</p><p>Here are some of my red flags when reading developer content:</p><ul><li><p><strong>Buzzwords and acronyms</strong>: Sometimes these are necessary, but there&#8217;s a lot of copywriting that looks like &#8220;cutting-edge, LLM-powered RAG platform&#8221; when it should really be &#8220;bring knowledge to your AI model&#8221;. Acronyms also alienate newcomers.</p></li><li><p><strong>Misusing technical terms</strong>: "Our JavaScript framework helps you make responsive websites&#8221; &#8211; does it though, or is that CSS? This can degrade trust because it shows the author might not have a deep understanding of the subject. Another example is broken or incorrectly formatted code examples. Did they even check that it works?</p></li><li><p><strong>Overpromising simplicity</strong>: The developer ecosystem is too obsessed with simplicity. I&#8217;m guilty. While &#8220;get started in one click&#8221; might be good sometimes, it can also be harmful. Maybe I want more options? "Simply install our SDK" might not be as simple as it sounds.</p></li><li><p><strong>Vague explanations</strong>: Developers want a practical example of what your product does. &#8220;Utilizing the latest technology to supercharge your development process&#8221;. Is this an editor, framework, database, or something else?</p></li></ul><p>Developers are more likely to trust open-source tools. These tools likely <strong><a href="https://janairis.medium.com/five-pillars-of-building-developer-communities-at-hashicorp-9c8f50198eb8">have a community</a></strong> of other developers learning together. They can take their knowledge of the tool from job to job as they grow in their career.</p><h3>Show, don&#8217;t tell</h3><p>Great developer marketing is both <em>concise</em> and <em>precise</em>.</p><p>It values your time&#8212;every word matters. Show them how to build interesting things, don&#8217;t fill a post with 1000 words of garbage.</p><p>Consider an email announcing a new version of your product. The pricing has changed, and there's a data migration.</p><ul><li><p><strong>How can I make it concise?</strong></p><ul><li><p><strong>Put the bottom line up front</strong>: If I stop reading after the first paragraph, did I get what I needed to know? Is there action required? Put that in the email subject line.</p></li><li><p><strong>Make it easy to scan</strong>: The visual flow of the content is as important as the words you choose. They should be able to quickly scan for landmarks (like code examples or commands they need to run).</p></li></ul></li><li><p><strong>How can I make it precise?</strong></p><ul><li><p><strong>Address their fears directly</strong>: This email mentions a migration. Am I going to lose my data? Do I need to make changes right away? Am I going to be charged more?</p></li><li><p><strong>Personalize the content</strong>: Show them their team name, the date the migration starts and finishes in UTC, exactly how much their price was before and after, and how they can take action (maybe a CLI command that shows their usage).</p></li></ul></li></ul><h2>What do you want them to remember?</h2><p>Your product is competing between thousands of others for developer mindshare. You believe yours is better. How do you share that message?</p><p>Let the product speak for itself. Listen to customer feedback and iterate. If your marketing isn&#8217;t working, their feedback should tell you why.</p><div class="pullquote"><p>&#8220;You&#8217;re doing sales because you failed at marketing. You&#8217;re doing marketing because you failed at product.&#8221; - <a href="https://twitter.com/naval/status/1505668279678824448">Naval</a></p></div><p>This <a href="https://twitter.com/naval/status/1505668279678824448">tweet</a> is provocative, but it makes a point: you can't fix a bad product with more marketing and sales. When you believe in the product, you don&#8217;t need to speak down against others. I prefer to do the opposite.</p><p>I believe in optimism for software, the web, and the community I&#8217;m building. I want people to remember I was helpful, positive, and enthusiastic about the product.</p><h2>Wait, what about DevRel?</h2><p><strong><a href="https://leerob.io/blog/devrel-at-vercel">Developer Relations</a></strong> is more than only developer marketing. It's the intersection between product, engineering, marketing, and sales.</p><p>For early companies, you might only need DevRel. They are generalists. But as you grow, you need to specialize. Get the right leaders for both developers and enterprise marketing.</p><p>Developer marketing leaders come from non-marketing roles (engineering, developer relations). They love showing developers how to build amazing things.</p><p>Enterprise marketing leaders come from traditional marketing roles (content, product marketing, growth). They can sell to developers, even if not one, and know how to build a pipeline generating engine.</p><p>It&#8217;s possible for one person to do both sides of marketing well, but rare.</p>]]></content:encoded></item><item><title><![CDATA[How I'm Writing CSS in 2024]]></title><description><![CDATA[In case you haven't noticed, writing CSS got really good.]]></description><link>https://leerob.substack.com/p/how-im-writing-css-in-2024</link><guid isPermaLink="false">https://leerob.substack.com/p/how-im-writing-css-in-2024</guid><dc:creator><![CDATA[Lee Robinson]]></dc:creator><pubDate>Sun, 07 Jan 2024 21:57:33 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/0d7329c0-e8ef-454d-b493-385edc369b92_1920x1080.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>CSS in 2024 is amazing.</p><ul><li><p>Cross-browser support for <a href="https://caniuse.com/css-variables">nesting</a>, <a href="https://caniuse.com/css-has">:has()</a>, <a href="https://caniuse.com/css-container-queries">container queries</a>, and more<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-1" href="#footnote-1" target="_self">1</a></p></li><li><p><a href="https://stylexjs.com/">Powerful</a> and <a href="https://lightningcss.dev/">fast</a> new CSS tools</p></li><li><p>Many frameworks and compilers to help optimize CSS loading performance</p></li></ul><p>This post will be a collection of my notes and thoughts about the CSS ecosystem and the tools I'm currently using.</p><h2>Design Constraints</h2><h3>User Experience</h3><p>What does a great experience look like loading stylesheets when visiting a website?</p><ol><li><p>Stylesheets should load as fast as possible (small file sizes)</p></li><li><p>Stylesheets should not re-download unless changed (proper caching headers)</p></li><li><p>The page content should have minimal or no layout shift</p></li><li><p>Fonts should load as fast as possible and minimize layout shift</p></li></ol><h3>Developer Experience</h3><p>Our tools must help us create better user experiences. The developer experience, while important, can't come before the user experience.</p><p>How can the DX of the styling tools we use help us create a better UX?</p><ol><li><p>Prune unused styles, minify, and compress CSS for smaller file sizes</p></li><li><p>Generate hashed file names to enable safe, immutable caching<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-2" href="#footnote-2" target="_self">2</a></p></li><li><p>Bundle CSS files together to make fewer network requests</p></li><li><p>Prevent naming collisions to avoid visual regressions</p></li></ol><p>What about to help us write more maintainable, enjoyable CSS?</p><ol><li><p>Easy to delete styles when deleting corresponding UI code</p></li><li><p>Easy to adhere to a design system or set of themes</p></li><li><p>Editor feedback with TypeScript support, autocompletion, and linting</p></li><li><p>Receive tooling feedback in-editor to prevent errors (type checking, linting)</p></li></ol><h2>CSS in 2024</h2><p>It's never been easier to write great styles without any additional tooling.</p><p><a href="https://codesandbox.io/p/sandbox/async-http-jyftfw?file=%2Fstyles.css">This example I created</a> uses many of the latest CSS features supported cross browser without any build step. You might not need Sass or Less anymore!</p><p>Does that mean the tooling is no longer necessary? For some people, yes.</p><h3>Build Steps</h3><p>To meet the design constraints above, you'll likely need a build step.</p><p>It's unlikely all your users are on the latest browser versions. But more importantly, there will always be newer syntax that isn't yet supported cross-browser you will want to use.</p><p>You <em>can</em> manually write <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/@supports">@supports</a> rules to check for browser support, but that's only solving some of the problems. Rather than leaving the CSS optimization to humans, <a href="https://csswizardry.com/2023/10/the-three-c-concatenate-compress-cache/">why not let the machines handle it</a>?</p><h3>Compilation</h3><p>Compilers make the following workflow easy:</p><ol><li><p>Automatically remove any unused styles, bundle files together to make fewer network requests, add vendor prefixes, and minify the output by removing white spaces and comments</p></li><li><p>Automatically generate unique file names, allowing frameworks to set caching headers like <code>immutable</code> signaling to browsers the content will never change</p></li><li><p>Specify target browsers (<a href="https://browsersl.ist/">browserslist</a>) and have <a href="https://lightningcss.dev/transpilation.html">syntax lowering</a> to compile modern CSS features to work with those browsers</p></li></ol><h3>Streaming CSS</h3><p>You visit Google to book a flight. It can't precompute your intent, so you're given a search bar for the initial UI. You search "Flight SFO to NYC" and the server streams in a flights widget to select dates.</p><p>There is no way Google could have included every possible widget ahead of time. Currency conversions, timers, live sports scores, you name it. The UI and styles for these widgets need to be dynamically streamed in.</p><p>React (and Next.js) now support this pattern with <a href="https://nextjs.org/docs/app/building-your-application/routing/loading-ui-and-streaming">streaming SSR</a> and <a href="https://github.com/reactwg/react-18/discussions/108">CSS</a>. In the React model, you define your UI as components which have dependencies on styles. How can we safely stream in styles for the a widget without affecting anything on the page?</p><p>Styles need to be scoped, or atomic, so that if they load earlier than the DOM content they're intended to style, they don't alter the style of elements already on the page.</p><p>For example, CSS Modules have styling rules scoped to the component that imports it. Tailwind uses atomic utility classes, which are compiled into a single stylesheet that's loaded before any classes are used. StyleX generates atomic classes as well. Global styles don't work well with streaming unless loaded at the beginning of the stream.</p><h2>My Recommendations</h2><h3>CSS Modules</h3><p>CSS Modules are a small but impactful enhancement on top of vanilla CSS.</p><p>They achieve our desired UX constraints and most (but not all) of our DX constraints. They're available in <a href="https://github.com/css-modules/css-modules/blob/master/docs/get-started.md">almost every modern bundler and framework</a>. You can copy / paste existing CSS selectors and they'll work in a CSS Module without any changes.</p><p>They can't generate atomic styles. They don't support using many themes (just CSS variables). And because the styling code lives outside your TypeScript files, you don't get type safety and autocompletion. But those constraints might be fine for you.</p><blockquote><p><a href="https://lightningcss.dev/">Lightning CSS</a>, which supports CSS Modules, is used by Vite, and <a href="https://medium.com/@bomber.marek/whats-tailwind-oxide-engine-the-next-evolution-of-tailwind-css-32e7ef8e19a1">soon by Tailwind</a> and <a href="https://twitter.com/leeerob/status/1740124461683409042">Next.js</a>. Tools like <code>postcss</code> and <code>autoprefixer</code> are being replaced by faster, <a href="https://leerob.io/blog/rust">all-in-one Rust toolchains</a>.</p></blockquote><h3>Tailwind CSS</h3><p>Tailwind uses a compiler to generate only the classes used. So while the utility CSS framework contains <em>many</em> possible class names, only the classes used (e.g. <code>"font-bold text-2xl"</code>) will be included in the single, compiled CSS file.</p><p>Assuming you only write Tailwind code, your bundle will never be larger than the total set of used Tailwind classes. It's extremely unlikely you would use them <em>all</em>. This means you have a fixed upper bound on the size of the generated CSS file, which is then minified, compressed, and cached for the best performance.</p><p>You don't have to <em>only</em> write Tailwind styles. Tailwind classes are just utilities for normal CSS that adhere to a design system. You can mix and match Tailwind with CSS Modules, for example.</p><p>Tailwind doesn't come without tradeoffs. There's a bucket of tools that pair with it:</p><ul><li><p><a href="https://tailwindcss.com/docs/editor-setup#intelli-sense-for-vs-code">VSCode integration</a> for autocompletion, linting, syntax highlighting, and more</p></li><li><p><a href="https://tailwindcss.com/docs/editor-setup#automatic-class-sorting-with-prettier">Prettier integration</a> for automatic sorting of class names</p></li></ul><p>The most controversial part about Tailwind is the syntax. It's both <a href="https://twitter.com/whiskeywebfm/status/1742222761018212460">loved</a> and hated. I didn't appreciate Tailwind until I built something with it, so I'd recommend trying that if your initial reaction is adverse.</p><p>Here&#8217;s another version of the <a href="https://codesandbox.io/p/sandbox/async-http-jyftfw?file=%2Fstyles.css">first example</a>, but <a href="https://codesandbox.io/p/sandbox/modern-sea-z7gkmc?file=%2Fstyles.css">written in Tailwind</a>.</p><h3>StyleX</h3><p>There are two issues with most CSS-in-JS libraries:</p><ol><li><p><strong>Performance:</strong> Components must convert the styles written in JS into CSS to be inserted into the document when rendering. This can have a <a href="https://dev.to/srmagura/why-were-breaking-up-wiht-css-in-js-4g9b">significant</a> cost and is why <a href="https://panda-css.com/docs/overview/why-panda">libraries</a> are moving to &#8220;zero runtime&#8221; libraries like StyleX.</p></li><li><p><strong>Compatibility:</strong> Many existing CSS-in-JS libraries have added support for React's streaming server-rendering, but are still incompatible with other performance optimizations like moving parts of your application to <a href="https://nextjs.org/docs/app/building-your-application/rendering/server-components">React Server Components</a>.</p></li></ol><p>To solve these issues, &#8220;zero runtime&#8221; CSS-in-JS libraries like <a href="https://vanilla-extract.style/">Vanilla Extract</a> and others have been created.</p><p>StyleX is the latest CSS-in-JS library, which <a href="https://stylexjs.com/docs/learn/thinking-in-stylex/">solves these problems and more</a>. I'd recommend reading through <a href="https://stylexjs.com/docs/learn/thinking-in-stylex/">&#8220;Thinking in StyleX&#8221;</a> if you want to dig in.</p><p>Here&#8217;s another version of the <a href="https://codesandbox.io/p/sandbox/async-http-jyftfw?file=%2Fstyles.css">first example</a>, but <a href="https://codesandbox.io/p/devbox/cocky-tu-x7twwd?file=%2FApp.tsx">written in StyleX</a>. This was my first time using it. While it's still new to open-source (and the ecosystem reflects that), it's <em>not</em> a new library. It powers all the Meta sites: Facebook, Instagram, WhatsApp, and Threads.</p><p>You still have to name things though &#129760; Enter <code>buttonWrapperContainer</code>.</p><h2>Conclusion</h2><p>Is CSS... fun for me now? I guess so. I'm excited to see what the next few years bring.</p><p>Would you have picked something different? Did I miss anything? Respond and let me know what you think.</p><p></p><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-1" href="#footnote-anchor-1" class="footnote-number" contenteditable="false" target="_self">1</a><div class="footnote-content"><p>More: <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/easing-function">linear() easing</a>, <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_grid_layout/Subgrid">subgrid</a>, <a href="https://web.dev/blog/viewport-units">dynamic viewport units</a>, <a href="https://developer.chrome.com/blog/meet-the-new-css-color-spaces">color spaces</a>, and <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/@layer">@layer</a>.</p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-2" href="#footnote-anchor-2" class="footnote-number" contenteditable="false" target="_self">2</a><div class="footnote-content"><p>Since the file names are guaranteed to be unique, you can set the <code>immutable</code> caching header to tell browsers the content will never change. This allows browsers to cache the file forever, which is great for performance.</p></div></div>]]></content:encoded></item><item><title><![CDATA[Product and Platform Engineers]]></title><description><![CDATA[The divide between frontend and backend engineers is increasingly less useful:]]></description><link>https://leerob.substack.com/p/product-and-platform-engineers</link><guid isPermaLink="false">https://leerob.substack.com/p/product-and-platform-engineers</guid><dc:creator><![CDATA[Lee Robinson]]></dc:creator><pubDate>Mon, 07 Aug 2023 14:20:16 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!Djuf!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F536f32ce-e2b5-4404-afda-3b1577d1a1e3_1100x743.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>The divide between frontend and backend engineers is increasingly less useful:</p><ul><li><p><strong>Frontend developers</strong> are no longer just writing HTML, CSS, and JavaScript. It's now common to see a frontend-focused developer build an entire web application from scratch. They integrate and connect to databases, handle authentication, and more.</p></li><li><p><strong>Backend developers</strong> are often forced to choose between two paths: writing more frontend code to support their backend, or moving closer towards managing, maintaining, and building software infrastructure used by the former developers.</p></li><li><p><strong>AI-enhanced developers</strong> have shifted the focus from simply writing code to creating great products. Startups are increasingly feeling this shift, running a more lean engineering team that uses LLMs and coding copilots to move faster.</p></li></ul><p>Consider two different verticals: <strong>Product and Platform Engineers</strong>.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://twitter.com/_swanson/status/1631691930345037824" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Djuf!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F536f32ce-e2b5-4404-afda-3b1577d1a1e3_1100x743.png 424w, https://substackcdn.com/image/fetch/$s_!Djuf!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F536f32ce-e2b5-4404-afda-3b1577d1a1e3_1100x743.png 848w, https://substackcdn.com/image/fetch/$s_!Djuf!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F536f32ce-e2b5-4404-afda-3b1577d1a1e3_1100x743.png 1272w, https://substackcdn.com/image/fetch/$s_!Djuf!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F536f32ce-e2b5-4404-afda-3b1577d1a1e3_1100x743.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Djuf!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F536f32ce-e2b5-4404-afda-3b1577d1a1e3_1100x743.png" width="540" height="364.74545454545455" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/536f32ce-e2b5-4404-afda-3b1577d1a1e3_1100x743.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:743,&quot;width&quot;:1100,&quot;resizeWidth&quot;:540,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:&quot;https://twitter.com/_swanson/status/1631691930345037824&quot;,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!Djuf!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F536f32ce-e2b5-4404-afda-3b1577d1a1e3_1100x743.png 424w, https://substackcdn.com/image/fetch/$s_!Djuf!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F536f32ce-e2b5-4404-afda-3b1577d1a1e3_1100x743.png 848w, https://substackcdn.com/image/fetch/$s_!Djuf!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F536f32ce-e2b5-4404-afda-3b1577d1a1e3_1100x743.png 1272w, https://substackcdn.com/image/fetch/$s_!Djuf!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F536f32ce-e2b5-4404-afda-3b1577d1a1e3_1100x743.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2>What are Product Engineers?</h2><p>Product Engineers work backwards from the desired product experience to the set of technologies that enable it. They consider the frontend, backend, design, and everything in between to create a great user experience.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://twitter.com/mipsytipsy/status/1659610352588562433" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!HeZ-!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa39bcb4e-dbc1-4993-a676-f4da98930e00_1100x695.png 424w, https://substackcdn.com/image/fetch/$s_!HeZ-!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa39bcb4e-dbc1-4993-a676-f4da98930e00_1100x695.png 848w, https://substackcdn.com/image/fetch/$s_!HeZ-!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa39bcb4e-dbc1-4993-a676-f4da98930e00_1100x695.png 1272w, https://substackcdn.com/image/fetch/$s_!HeZ-!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa39bcb4e-dbc1-4993-a676-f4da98930e00_1100x695.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!HeZ-!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa39bcb4e-dbc1-4993-a676-f4da98930e00_1100x695.png" width="530" height="334.8636363636364" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/a39bcb4e-dbc1-4993-a676-f4da98930e00_1100x695.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:695,&quot;width&quot;:1100,&quot;resizeWidth&quot;:530,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:&quot;https://twitter.com/mipsytipsy/status/1659610352588562433&quot;,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!HeZ-!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa39bcb4e-dbc1-4993-a676-f4da98930e00_1100x695.png 424w, https://substackcdn.com/image/fetch/$s_!HeZ-!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa39bcb4e-dbc1-4993-a676-f4da98930e00_1100x695.png 848w, https://substackcdn.com/image/fetch/$s_!HeZ-!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa39bcb4e-dbc1-4993-a676-f4da98930e00_1100x695.png 1272w, https://substackcdn.com/image/fetch/$s_!HeZ-!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa39bcb4e-dbc1-4993-a676-f4da98930e00_1100x695.png 1456w" sizes="100vw"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>They don't need to understand every part deeply, a common misconception of "fullstack". Instead, they have a broad understanding of the available tools and deep experience <em>applying</em> those tools to build products.</p><p>Some qualities of a great Product Engineer:</p><ul><li><p><strong>Iterative:</strong> They value <a href="https://twitter.com/rauchg/status/1681694545845833729">iteration velocity</a> and incremental correctness. They'd rather ship, get feedback from customers, and adjust along the way&#8212;rather than building in a silo.</p></li><li><p><strong>Customer focused:</strong> They aren't afraid to engage with customers and learn how to improve the product. At a larger company, the Product Manager might specialize here, but should work closely with the engineer to <em>apply</em> those learnings to the correct solutions.</p></li><li><p><strong>Pragmatic:</strong> They understand that technology choices are all means to an end. Choosing the latest JavaScript library doesn't matter if no one uses your product. They are willing to remove tools if they aren't helping fulfill their product goals.</p></li></ul><p>Okay, so what does that leave the other half of the software engineers?</p><h2>Product vs. Platform Engineering</h2><p>While Product Engineers focus on building and enhancing features that solve end user problems, <a href="https://twitter.com/swyx/status/1097334440169107456">Platform Engineers</a> focus on the infrastructure that supports the product.</p><p>Platform Engineering teams are building or buying tools to support Product Engineers and make them more efficient, productive, and happier. They're typically making the infrastructure choices for the Product teams.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://twitter.com/sophiebits/status/1404914237437190144" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!FEPp!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9dbe6d86-6a6e-4cd6-88bb-f25a9480b760_1100x598.png 424w, https://substackcdn.com/image/fetch/$s_!FEPp!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9dbe6d86-6a6e-4cd6-88bb-f25a9480b760_1100x598.png 848w, https://substackcdn.com/image/fetch/$s_!FEPp!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9dbe6d86-6a6e-4cd6-88bb-f25a9480b760_1100x598.png 1272w, https://substackcdn.com/image/fetch/$s_!FEPp!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9dbe6d86-6a6e-4cd6-88bb-f25a9480b760_1100x598.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!FEPp!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9dbe6d86-6a6e-4cd6-88bb-f25a9480b760_1100x598.png" width="534" height="290.3018181818182" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/9dbe6d86-6a6e-4cd6-88bb-f25a9480b760_1100x598.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:598,&quot;width&quot;:1100,&quot;resizeWidth&quot;:534,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:&quot;https://twitter.com/sophiebits/status/1404914237437190144&quot;,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!FEPp!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9dbe6d86-6a6e-4cd6-88bb-f25a9480b760_1100x598.png 424w, https://substackcdn.com/image/fetch/$s_!FEPp!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9dbe6d86-6a6e-4cd6-88bb-f25a9480b760_1100x598.png 848w, https://substackcdn.com/image/fetch/$s_!FEPp!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9dbe6d86-6a6e-4cd6-88bb-f25a9480b760_1100x598.png 1272w, https://substackcdn.com/image/fetch/$s_!FEPp!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9dbe6d86-6a6e-4cd6-88bb-f25a9480b760_1100x598.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Typically, platforms teams were only something accessible for larger organizations. Increasingly, a small but effective group of Platform Engineers can make incredibly impactful decisions, even for smaller teams. They are thoughtful about evaluating and purchasing software to grow with the scale of their product team.</p><h2>Finding Talent</h2><p>Most startups are looking for Fullstack Engineers but <a href="https://www.linkedin.com/posts/mztwo_im-seeing-startups-value-a-different-kind-activity-7084894252568039424-eJ3M/">actually need Product Engineers</a>.</p><p>The best Product Engineers share two common qualities:</p><ol><li><p>A passion for <strong>building high-quality experiences</strong></p></li><li><p>A constant drive to <strong>learn and explore new ideas</strong></p></li></ol><p>They're not pixel-pushers; they're creators who take pride in their work and are not afraid to stand behind their products. They should have plenty of examples of their work, because so much of it is user facing.</p><p>They understand web fundamentals, such as browsers, accessibility, animations, CSS, HTML, and JavaScript. The tools they use&#8212;be it React, Svelte, or something else&#8212;are secondary. What truly matters is their ability to use these tools to build the best product possible.</p><blockquote><p>The title shift from Fullstack &#8594; Product Engineer has some prior art.</p><p>Design has seen a similar shift. Titles like UI/UX Designer and Graphic Designer are increasingly being replaced by Product Designer.</p></blockquote><p>Great Product Engineers don't stop at visually complete. They carefully consider the user journeys. What happens if there's slow (or no) internet connection? What if this API returns an error? How does this experience work on touch devices? What latency numbers should we strive for?</p><p>What seems like an excessive amount of polish to others is normal for these developers.</p><h2>Conclusion</h2><p>When I tweeted <a href="https://twitter.com/leeerob/status/1687444748884201473">"Product Enginner &gt; Fullstack Engineer"</a>, I was surprised by the response.</p><p>At <a href="http://vercel.com/">Vercel</a>, we updated our job descriptions to change references from Fullstack to Product Engineers. I've noticed others <a href="https://twitter.com/GergelyOrosz/status/1539926726515593217">doing the same</a>.</p><p>Product Engineers represent a shift in web development. Especially with the <a href="https://www.latent.space/p/ai-engineer">rise of AI</a>, there's a growing need for developers who do more than "just write code" &#8212; developers who understand the bigger picture, who can work across various technologies, and who focus on delivering exceptional products to the end user.</p><h3>Further Reading</h3><ul><li><p><a href="https://blog.pragmaticengineer.com/the-product-minded-engineer">The Product-Minded Software Engineer</a></p></li><li><p><a href="https://sherifmansour.medium.com/product-engineers-f424da766871">PM at Atlassian on Product Engineers</a></p></li><li><p><a href="https://medium.com/@michlimlim/stop-just-using-frontend-or-backend-to-describe-the-engineering-you-like-e8c392956ada">Stop just using &#8220;Frontend&#8221; or &#8220;Backend&#8221; to describe the Engineering you like</a></p></li></ul>]]></content:encoded></item><item><title><![CDATA[Make Something Developers Want]]></title><description><![CDATA[Five steps for building products developers love.]]></description><link>https://leerob.substack.com/p/make-something-developers-want</link><guid isPermaLink="false">https://leerob.substack.com/p/make-something-developers-want</guid><dc:creator><![CDATA[Lee Robinson]]></dc:creator><pubDate>Fri, 21 Jul 2023 01:23:39 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/075c2afa-b217-4be5-919d-7624fcb107d0_1920x1080.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Y Combinator is famous for their advice: make something people want.</p><p>It's simple and direct&#8212;relentlessly focus on your customer.</p><p>However, when those customers are developers, I'd argue the execution of this advice looks different and might be counter-intuitive from what people generally want.</p><p>Here's what you should do to make something developers want:</p><ol><li><p><strong>You should embrace open-source</strong></p></li><li><p><strong>You should have a free tier</strong></p></li><li><p><strong>You should have excellent documentation</strong></p></li><li><p><strong>You should be able to get support from developers</strong></p></li><li><p><strong>You should be part of a community</strong></p></li></ol><h2>1. You should embrace open-source</h2><p>Experienced developers love open-source products because they've seen the opposite. They've struggled to debug a &#8220;black box&#8221; closed-source tool, or worse, had a previously open tool change licenses or close its source code.</p><p>New developers love open-source products because they're learning skills that help them advance in their career&#8212;their knowledge transfers from company to company.</p><p>Open-source developer tools often have better documentation and educational material, too.</p><h2>2. You should have a free tier</h2><p>Developers don't want to talk to your sales team. They want to try out the product with as little friction as possible. Start free, then grow, once they've validated the product works and will solve their needs.</p><p>There are exceptions to this rule. You might have procured software that required months of negotiations, license keys to get access, and hand guided provisioning of resources from a human. But ask your developer&#8212;they would have preferred if they could have started self-serve.</p><p>&#8220;Try it before you buy it&#8221; isn't novel sales advice, but developers are outliers in this demographic.</p><h2>3. You should have excellent documentation</h2><p>The better the documentation, the more likely the developer is to try your product.</p><p>This isn't about specific UI features like dark mode or &#8984;+K search dialogs. How quickly can they find an answer to their problem? Great documentation is either a reference guide or an educational tool.</p><p>Educational material should be equally compelling for both beginners and experts. Experts love to hear concepts explained in a simple way. Beginners want new concepts progressively explained. Don't drop ten new concepts and five acronyms right away. Write like you're teaching beginners.</p><p>Here's more thoughts on <strong><a href="https://leerob.io/blog/developer-experience-examples">what makes a great developer experience</a></strong>.</p><h2>4. You should be able to get support from developers</h2><p>When developers can't find the answer in your documentation, they'll reach out to support.</p><p>They want to talk to someone who empathizes and understands their problems. It's easy to tell whether the support person actually &#8220;gets it&#8221;. Minimize the number of hops between problem and solution, ideally cutting out layers of humans in the middle.</p><p>This is why roles like Developer Advocates or Customer Success Engineers exist. Developers expect to talk to developers.</p><h2>5. You should be part of a community</h2><p>Where do developers go to hang out? Online and in-person communities.</p><p>This doesn't mean you need to <em>start</em> a community for your product. In fact, you probably shouldn't. Instead, look at where the developers are already spending time. Go there. Talk to them and contribute to the discourse.</p><p>Communities are not a transactional relationship. You are there to provide value. This is a long game, sometimes taking years to materialize value back, but then obvious in hindsight.</p>]]></content:encoded></item><item><title><![CDATA[2023 State of Databases for Serverless & Edge]]></title><description><![CDATA[There's been massive innovation in the database and backend space for developers building applications with serverless and edge compute. There are new tools, companies, and even programming models that simplify how developers store data. This post will be an overview of databases that pair well with modern application and compute providers.]]></description><link>https://leerob.substack.com/p/databases-serverless-edge</link><guid isPermaLink="false">https://leerob.substack.com/p/databases-serverless-edge</guid><dc:creator><![CDATA[Lee Robinson]]></dc:creator><pubDate>Sat, 28 Jan 2023 16:03:43 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/02adb5e2-efe0-457f-b275-32674f63597f_1280x720.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>There's been massive innovation in the database and backend space for developers building applications with serverless and edge compute. There are new tools, companies, and even programming models that simplify how developers store data.</p><p>This post will be an overview of databases that pair well with modern application and compute providers.</p><h2>Criteria</h2><p>I'll focus on <strong>transactional<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-1" href="#footnote-1" target="_self">1</a></strong> workloads instead of analytical<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-2" href="#footnote-2" target="_self">2</a> workloads.</p><p>The &#8220;backend&#8221; space is vast: search, analytics, data science, and more &#8211; so I'll niche down here. The primary criteria of this overview is:</p><ol><li><p>Services that work exceptionally well when paired with serverless and edge compute</p></li><li><p>Services that work with JavaScript and TypeScript codebases</p></li></ol><blockquote><p><em>Disclaimer: I work at Vercel, which partners with companies in this post. I also have personally used many of these tools for my own personal projects. My site currently uses PlanetScale and I'm also an angel investor in Supabase (mentioned below).</em></p></blockquote><h2>A new programming model</h2><p>Relational databases have been around for 25+ years.</p><p>While there are new companies creating <em>serverless-first</em> storage solutions, a new programming model is required for workloads to be compatible with serverless compute and modern runtimes.</p><p>These solutions must be:</p><ul><li><p><strong>Connectionless:</strong> Developers don't want to think about manual connection management. Traditional database protocols are stateful, whereas HTTP is mostly stateless, making it easier to use with scale to zero compute. Exposed through an SDK or HTTP API, &#8220;connectionless&#8221; solutions provide an abstraction over connection pooling.</p></li><li><p><strong>Web native:</strong> Browser data-fetching APIs (e.g. Web <code>fetch</code>) and protocols are eating the world. New databases use HTTP APIs or WebSockets, rather than opening direct connections to the database. This makes them compatible with all forms of compute (including the lighter runtime used in edge compute).</p></li><li><p><strong>Lightweight:</strong> Client libraries (and drivers) are becoming thin. Complexity is shifting to the database vendor, taking on the burden as part of their global infrastructure. For example, their gateway might handle connection pooling or provide caching infrastructure. This has led to a new wave of ORMs (i.e. abstractions to query data) both as standalone libraries and as integrated database SDKs.</p></li><li><p><strong>(Bonus) Type-safe:</strong> TypeScript developers are favoring databases or libraries which provide tooling to enable type-safe access to your data. For example: <a href="https://www.prisma.io/">Prisma</a>, <a href="https://github.com/koskimas/kysely">Kysely</a>, <a href="https://github.com/drizzle-team/drizzle-orm">Drizzle</a>, <a href="https://www.contentlayer.dev/">Contentlayer</a>, and <a href="https://jawj.github.io/zapatos/">Zapatos</a>.</p></li></ul><p>Consider databases like Postgres. New solutions like Neon and Supabase abstract connection management, providing you with a simple way to query and mutate data. In the case of Supabase, there's a client library that uses an HTTP API <a href="https://supabase.com/docs/guides/api">built on PostgREST</a>:</p><pre><code><code>import { createClient } from '@supabase/supabase-js'

let supabase = createClient('https://project.supabase.co', 'key')
let { data } = await supabase.from('countries').select()</code></code></pre><p>And for Neon:</p><pre><code><code>import { Client } from '@neondatabase/serverless'

let client = new Client(process.env.DATABASE_URL)
let { rows: [{ now }] } = await client.query('select now();')</code></code></pre><p>Neon's solution is <a href="https://neon.tech/blog/serverless-driver-for-postgres/">particularly interesting</a>.</p><blockquote><p><em>The basic premise is simple: our driver redirects the PostgreSQL wire protocol via a special proxy. Our driver connects from the edge function to the proxy over a WebSocket, telling the proxy which database host and port it wants to reach. The proxy opens a TCP connection to that host and port and relays traffic in both directions.</em></p></blockquote><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!e1E6!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feefc1aad-52b1-4d73-8a7e-4f18fdde2ca8_1600x840.webp" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!e1E6!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feefc1aad-52b1-4d73-8a7e-4f18fdde2ca8_1600x840.webp 424w, https://substackcdn.com/image/fetch/$s_!e1E6!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feefc1aad-52b1-4d73-8a7e-4f18fdde2ca8_1600x840.webp 848w, https://substackcdn.com/image/fetch/$s_!e1E6!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feefc1aad-52b1-4d73-8a7e-4f18fdde2ca8_1600x840.webp 1272w, https://substackcdn.com/image/fetch/$s_!e1E6!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feefc1aad-52b1-4d73-8a7e-4f18fdde2ca8_1600x840.webp 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!e1E6!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feefc1aad-52b1-4d73-8a7e-4f18fdde2ca8_1600x840.webp" width="1456" height="764" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/eefc1aad-52b1-4d73-8a7e-4f18fdde2ca8_1600x840.webp&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:764,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:78488,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/webp&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!e1E6!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feefc1aad-52b1-4d73-8a7e-4f18fdde2ca8_1600x840.webp 424w, https://substackcdn.com/image/fetch/$s_!e1E6!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feefc1aad-52b1-4d73-8a7e-4f18fdde2ca8_1600x840.webp 848w, https://substackcdn.com/image/fetch/$s_!e1E6!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feefc1aad-52b1-4d73-8a7e-4f18fdde2ca8_1600x840.webp 1272w, https://substackcdn.com/image/fetch/$s_!e1E6!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feefc1aad-52b1-4d73-8a7e-4f18fdde2ca8_1600x840.webp 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">An overview of Neon&#8217;s architecture using WebSockets.</figcaption></figure></div><blockquote><p><em>Using WebSockets, instead of HTTP, does have tradeoffs. There might be additional latency on the first request setup, but subsequent requests are faster. There's an <a href="https://www.rfc-editor.org/rfc/rfc9220.pdf">RFC</a> for WebSockets with HTTP/3 which would remove that extra network roundtrip.</em></p></blockquote><p>The connection management isn't going away &#8211; it's just being handled by the vendor now.</p><p>There's even solutions like PlanetScale which can handle up to <a href="https://planetscale.com/blog/one-million-connections">a million connections</a>, which also allows you to effectively never think about managing connections.</p><h2>Trends</h2><p>This new programming model has created emerging trends for database companies:</p><ul><li><p>Databases are increasingly becoming <strong>data platforms</strong>, including other adjacent solutions like full-text search and analytics.</p></li><li><p>The <strong>decoupling of storage and compute</strong>, popularized by Snowflake (and more), is enabling new players (e.g. Neon et al.) to massively reduce the cost of a &#8220;database at rest&#8221;. This pairs well with frontend git branch-based workflows, where you want to scale to zero when not being used.</p></li><li><p>Increasingly developers <strong>don't want to &#8220;dial the knobs&#8221;</strong>. Solutions like DynamoDB (and in some ways S3) provided infinite scale without needing to tweak memory, storage, CPU, clusters, and instances.</p></li><li><p>The dream of <strong>global data</strong> is here, but not how it was predicted. Trying to replicate <em>all</em> data to <em>every</em> network edge is probably not the correct solution most times. Instead, we're seeing specialized data APIs and the emergence of user-specific data stores (e.g. for shopping cart data).</p></li><li><p>More databases are <strong>embracing serverless</strong>, but what &#8220;serverless&#8221; means to them varies. There are different vectors of autoscaling: connections, storage, compute, and more.</p></li></ul><h2>Databases</h2><p>I'll bucket these into &#8220;established&#8221; and &#8220;rising&#8221; categories, serverless/serverful, as well as generally available (GA) and pre-GA. I'll also mostly talk about managed vendors.</p><p>For example, you can of course run MySQL or Postgres on major cloud providers like AWS. There's a long tail of niche data storage solutions, so some will definitely be missing that I haven't heard of or used.</p><h3>Established serverless solutions</h3><ul><li><p><strong>Firebase</strong></p><ul><li><p><strong><a href="https://firebase.google.com/docs/firestore">Firestore:</a></strong> Over 10 years old now, Firestore is a well-adopted document database. One unique advantage Firebase is its built-in support for authentication, real-time workloads, and cross-platform support for mobile.</p></li></ul></li><li><p><strong>MongoDB</strong></p><ul><li><p><strong><a href="https://www.mongodb.com/use-cases/serverless">Atlas Serverless:</a></strong> MongoDB has an entire data platform, including search / analytics / etc. Their recent investment into serverless is very exciting, with Atlas Serverless now <a href="https://www.mongodb.com/blog/post/embrace-benefits-serverless-development-atlas">generally available</a>. Their <a href="https://www.mongodb.com/docs/atlas/api/data-api/">Data API</a> makes them a fantastic pair for serverless / edge.</p></li></ul></li><li><p><strong>MySQL</strong></p><ul><li><p><strong><a href="https://planetscale.com/">PlanetScale</a></strong>: MySQL through <a href="https://planetscale.com/blog/all-the-tech-planetscale-replaces">Vitess</a>. Can easily handle quite a <a href="https://planetscale.com/blog/one-million-queries-per-second-with-mysql">large</a> <a href="https://planetscale.com/blog/one-million-connections">scale</a> and also includes some nice features to <a href="https://planetscale.com/blog/introducing-planetscale-boost">speed up</a> and <a href="https://planetscale.com/blog/introducing-planetscale-insights-advanced-query-monitoring">monitor</a> queries. Their <a href="https://planetscale.com/blog/introducing-the-planetscale-serverless-driver-for-javascript">serverless driver</a> (<code>@planetscale/database</code>) has been easy to use, but I prefer <a href="https://github.com/depot/kysely-planetscale">Kysely</a>.</p></li></ul></li><li><p><strong>PostgreSQL</strong></p><ul><li><p><strong><a href="https://aws.amazon.com/rds/aurora/serverless/">AWS Aurora</a></strong>: One of the first serverless Postgres offerings.</p></li><li><p><strong><a href="https://www.cockroachlabs.com/blog/announcing-cockroachdb-serverless/">CockroachDB</a></strong>: Autoscales and distributes data across multiple nodes. Focused on high data consistency and integrity. Supports most of Postgres but not stored procedures and extensions.</p></li></ul></li><li><p><strong>Redis</strong></p><ul><li><p><strong><a href="https://upstash.com/">Upstash</a></strong>: Offers durable/consistent Redis, global replication options, and Kafka. Fantastic <code>@upstash/redis</code> HTTP/REST client library.</p></li></ul></li></ul><h3>Rising serverless database solutions</h3><ul><li><p><strong>Generally Available (GA)</strong></p><ul><li><p><strong><a href="https://fauna.com/">Fauna</a></strong>: They were an early mover in the serverless data space. I've had success with their GraphQL APIs before, but feel mostly indifferent on <strong><a href="https://docs.fauna.com/fauna/current/api/fql/">FQL</a></strong>.</p></li></ul></li><li><p><strong>Pre-GA</strong></p><ul><li><p><strong><a href="https://www.convex.dev/">Convex</a></strong>: Fantastic for real-time workloads, but also a very simple, type-safe interface for querying/mutating data. You write (and think) in functions. Pairs well with React's mental model.</p></li><li><p><strong><a href="https://grafbase.com/">Grafbase</a></strong>: If you love GraphQL, Grafbase is worth exploring. Designed to integrate into branch-based workflows + fast reads globally. Realtime and full-text search are in the works.</p></li><li><p><strong><a href="https://neon.tech/">Neon:</a></strong> Postgres with separation of storage and compute. Uniquely designed for serverless and works with the native Postgres driver + supports database branching workflows.</p></li><li><p><strong><a href="https://supabase.com/">Supabase</a></strong>: Open-source, built-on pure Postgres. <a href="https://supabase.com/database">Database</a> + <a href="https://supabase.com/auth">Auth</a> + <a href="https://supabase.com/storage">Storage</a> and more. Scales up on pay-as-you-go, and <a href="https://supabase.com/blog/supabase-series-b#where-were-going">working on scale to zero</a>.</p></li><li><p><strong><a href="https://xata.io/">Xata</a></strong>: Not only a database, but search / analytics as well. I'm a fan of their spreadsheet-like UI, which is approachable for a wider audience.</p></li></ul></li><li><p><em>+ Long tail of new providers that can do DB hosting (Railway, Render, Fly, etc).</em></p></li></ul><h3>Stateful backends and other solutions</h3><ul><li><p><strong>Generally Available (GA)</strong></p><ul><li><p><strong><a href="https://cloud.google.com/alloydb">AlloyDB</a></strong>: Very exciting innovation coming from Google. AlloyDB is unique because it can handle both transactional and analytical workloads.</p></li><li><p><strong><a href="https://ably.com/">Ably</a></strong>: Realtime infrastructure with <a href="https://ably.com/docs/general/queues">queues support</a> and other message durability options.</p></li><li><p><strong><a href="https://www.crunchydata.com/">Crunchy Postgres</a></strong>: Crunchy Postgres is <a href="https://changelog.com/news/just-postgres-LWm4">"just Postgres"</a>. Focused on performance and availability, cost-effectiveness, and supporting all native Postgres features.</p></li><li><p><strong><a href="https://hasura.io/">Hasura:</a></strong> Makes it easy to connect data from many different sources and expose it as a GraphQL API. Can use Neon under-the-hood as the database provider.</p></li><li><p><strong><a href="https://liveblocks.io/">Liveblocks</a></strong>: Realtime building blocks, they have a &#8220;storageblock&#8221; in Beta, and offer persistence for presence. Beautiful design and documentation.</p></li><li><p><strong><a href="https://replicache.dev/">Replicache</a></strong>: More of a database <em>synchronization</em> <em>engine</em> that can be coupled with other solutions with no real-time solution. We use this for <a href="https://vercel.com/blog/making-live-reviews-a-reality-enhanced-preview-experience">comments on Vercel previews</a>.</p></li><li><p><strong><a href="https://www.timescale.com/">TimeScale</a></strong>: For both transactional and analytical workloads. Adds many features found in NoSQL databases to Postgres, and also integrates with S3 for storage.</p></li></ul></li><li><p><strong>Pre-GA</strong></p><ul><li><p><strong><a href="https://www.chiselstrike.com/">ChiselStrike</a></strong>: Write your TypeScript class, generate an API. Really leaning into the &#8220;infrastructure from code&#8221; approach, you write and think in functions, somewhat similar to Convex.</p></li><li><p><strong><a href="https://www.edgedb.com/">EdgeDB</a></strong>: EdgeDB is challenging the status quo. Particularly the &#8220;merger&#8221; between ORM / database, more than just a query builder, but a way to <em>optimize</em> queries.</p></li><li><p><strong><a href="https://nhost.io/">Nhost:</a></strong> Firebase, but with GraphQL. Built on Hasura, Postgres, and S3.</p></li><li><p><strong><a href="https://www.rowy.io/">Rowy:</a></strong> Low-code, spreadsheet-like backend (almost like an open-source Airtable) but they also have the ability to write functions to mutate data.</p></li><li><p><strong><a href="https://surrealdb.com/">SurrealDB:</a></strong> Has its own SQL-flavored syntax, also trying to provide the spreadsheet-like UI for viewing data (still waitlist access, haven't used it).</p></li><li><p><strong><a href="https://www.tigrisdata.com/">Tigris:</a></strong> Document database, focused on real-time and also includes full-text search.</p></li><li><p><strong><a href="https://wundergraph.com/">WunderGraph:</a></strong> Trying to lead with an excellent ORM / client library (end-to-end type safety, GraphQL support) and wedge into a cloud product (still waitlist access, haven't used it).</p></li></ul></li><li><p><strong>Other Solutions</strong></p><ul><li><p><strong>Caching Engines:</strong> <a href="https://stellate.co/">Stellate</a>, <a href="https://www.prisma.io/data-platform/accelerate">Prisma Accelerate</a>, <a href="https://readyset.io/">ReadySet</a>.</p></li><li><p><strong>Cloud Provider Offerings:</strong> AWS Dynamo, Azure SQL, Azure CosmosDB, Google Cloud SQL, Google BigTable, and more.</p></li><li><p><strong>Content Management (Headless CMS):</strong> These can still act as a database, just a different domain-specific solution. <a href="https://www.sanity.io/">Sanity</a>, <a href="https://www.contentful.com/">Contentful</a>, <a href="https://www.sitecore.com/">Sitecore</a>, and more.</p></li></ul></li></ul><p>Okay, that wraps up this overview. I&#8217;d love to hear your feedback &#8211; who did I miss? Of the services above, which have you tried, and what did you like about them? This is an early preview before I share this post on social media, suggestions are welcome!</p><div><hr></div><p><em>Thanks to <a href="https://twitter.com/rauchg">Guillermo Rauch</a>, <a href="https://twitter.com/kiwicopple">Paul Copplestone</a>, <a href="https://twitter.com/fbjork">Fredrik Bj&#246;rk</a>, <a href="https://twitter.com/anthonysheww">Anthony Shew</a>, <a href="https://twitter.com/craigkerstiens">Craig Kerstiens</a>, <a href="https://twitter.com/jamwt">Jamie Turner</a>, <a href="https://twitter.com/nikitabase">Nikita Shamgunov</a>, <a href="https://twitter.com/stuffyokodraws">Yoko Li</a>, <a href="https://twitter.com/177pc">Pratyush Choudhury</a>, <a href="https://twitter.com/kelvich">Stas Kelvich</a>, <a href="https://twitter.com/enesakar">Enes Akar</a>, and <a href="https://twitter.com/steventey">Steven Tey</a> for reviewing this post.</em></p><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-1" href="#footnote-anchor-1" class="footnote-number" contenteditable="false" target="_self">1</a><div class="footnote-content"><p>Commonly referred to as OLTP (Online <em>Transactional</em> Processing). These are for CRUD operations, most commonly the MySQL and Postgres databases of the world.</p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-2" href="#footnote-anchor-2" class="footnote-number" contenteditable="false" target="_self">2</a><div class="footnote-content"><p>Commonly referred to as OLAP (Online <em>Analytical</em> Processing). These are for your real-time data workloads, like <a href="https://clickhouse.com/">Clickhouse</a> (also <a href="https://www.tinybird.co/">Tinybird</a>), <a href="https://www.singlestore.com/">SingleStore</a>, <a href="https://www.timescale.com/">TimeScale</a>, and <a href="https://www.elastic.co/">ElasticSearch</a>.</p><p></p></div></div>]]></content:encoded></item><item><title><![CDATA[Why You Should Use a React Framework]]></title><description><![CDATA[And spend more time writing React code.]]></description><link>https://leerob.substack.com/p/react-frameworks</link><guid isPermaLink="false">https://leerob.substack.com/p/react-frameworks</guid><dc:creator><![CDATA[Lee Robinson]]></dc:creator><pubDate>Thu, 05 Jan 2023 14:28:07 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/h_600,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Faf4ae108-7af1-4d33-8847-106f0efbae8e_2560x1440.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Analyzing the <a href="https://majestic.com/reports/majestic-million">top 10,000 publicly accessible</a> websites on the internet, we see an interesting trend: <strong>~6%</strong> are now built with React frameworks<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-1" href="#footnote-1" target="_self">1</a>.</p><div class="twitter-embed" data-attrs="{&quot;url&quot;:&quot;https://twitter.com/rauchg/status/1604880119666905088&quot;,&quot;full_text&quot;:&quot;<span class=\&quot;tweet-fake-link\&quot;>@nextjs</span> is now the top #15 software repo on GitHub &#11088;&#65039;&#129327; &quot;,&quot;username&quot;:&quot;rauchg&quot;,&quot;name&quot;:&quot;Guillermo Rauch&quot;,&quot;profile_image_url&quot;:&quot;&quot;,&quot;date&quot;:&quot;Mon Dec 19 16:43:32 +0000 2022&quot;,&quot;photos&quot;:[{&quot;img_url&quot;:&quot;https://pbs.substack.com/media/FkWtTYLUcAUo9xw.jpg&quot;,&quot;link_url&quot;:&quot;https://t.co/hrL5onaknm&quot;,&quot;alt_text&quot;:null}],&quot;quoted_tweet&quot;:{},&quot;reply_count&quot;:0,&quot;retweet_count&quot;:108,&quot;like_count&quot;:1238,&quot;impression_count&quot;:0,&quot;expanded_url&quot;:{},&quot;video_url&quot;:null,&quot;belowTheFold&quot;:false}" data-component-name="Twitter2ToDOM"></div><p>You should consider using a framework (instead of building your own) because:</p><ol><li><p>Less time connecting tools, more time building products</p></li><li><p>Easier onboarding and training of new developers</p></li><li><p>Flexibility for different rendering strategies (server, client, or static)</p></li><li><p>Opinionated choices prevent bikeshedding</p></li><li><p>Deploy anywhere and incrementally adopt</p></li><li><p>It&#8217;s still just React</p></li></ol><p>But before we explore why you should use a framework, it&#8217;s helpful to step back and look at the evolution of React.</p><h2>React is evolving</h2><p>React changed how developers build for the web, popularizing breaking down UI into reusable components and emphasizing incremental adoption.</p><p>While it&#8217;s certainly still a library that can be <a href="https://beta.reactjs.org/learn/add-react-to-a-website">sprinkled into any webpage</a>, the <strong>React architecture</strong> is a blueprint for frameworks to follow to create interactive, resilient, and performant <a href="https://github.com/reactwg/react-18/discussions/37">frontend patterns</a>.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!KozG!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Faf4ae108-7af1-4d33-8847-106f0efbae8e_2560x1440.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!KozG!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Faf4ae108-7af1-4d33-8847-106f0efbae8e_2560x1440.png 424w, https://substackcdn.com/image/fetch/$s_!KozG!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Faf4ae108-7af1-4d33-8847-106f0efbae8e_2560x1440.png 848w, https://substackcdn.com/image/fetch/$s_!KozG!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Faf4ae108-7af1-4d33-8847-106f0efbae8e_2560x1440.png 1272w, https://substackcdn.com/image/fetch/$s_!KozG!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Faf4ae108-7af1-4d33-8847-106f0efbae8e_2560x1440.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!KozG!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Faf4ae108-7af1-4d33-8847-106f0efbae8e_2560x1440.png" width="576" height="324" data-attrs="{&quot;src&quot;:&quot;https://bucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com/public/images/af4ae108-7af1-4d33-8847-106f0efbae8e_2560x1440.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:819,&quot;width&quot;:1456,&quot;resizeWidth&quot;:576,&quot;bytes&quot;:88596,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!KozG!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Faf4ae108-7af1-4d33-8847-106f0efbae8e_2560x1440.png 424w, https://substackcdn.com/image/fetch/$s_!KozG!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Faf4ae108-7af1-4d33-8847-106f0efbae8e_2560x1440.png 848w, https://substackcdn.com/image/fetch/$s_!KozG!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Faf4ae108-7af1-4d33-8847-106f0efbae8e_2560x1440.png 1272w, https://substackcdn.com/image/fetch/$s_!KozG!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Faf4ae108-7af1-4d33-8847-106f0efbae8e_2560x1440.png 1456w" sizes="100vw" loading="lazy" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">React has evolved from a library to also provide an architecture for frameworks.</figcaption></figure></div><p>Consider <a href="https://beta.nextjs.org/docs/rendering/server-and-client-components">React Server Components</a>. This architecture is designed for frameworks to implement. Some aspects have been standardized between React frameworks (e.g. <a href="https://github.com/reactjs/rfcs/pull/227">"use client"</a>) and others are being incubated (e.g. <a href="https://github.com/reactjs/rfcs/pull/229">async/await in Server Components</a>) during <a href="https://nextjs.org/blog/next-13#new-app-directory-beta">beta periods</a>. Frameworks can still choose their own conventions (e.g. <a href="https://beta.nextjs.org/docs/routing/fundamentals#special-files">special filenames</a>).</p><p>Implementing the React architecture, and ultimately great UI patterns, requires deep integration between all parts of your frontend: data fetching and loading states, code splitting and bundling, routing and rendering, and more.</p><p>This is where frameworks come in.</p><div class="twitter-embed" data-attrs="{&quot;url&quot;:&quot;https://twitter.com/dan_abramov/status/1585076899126345728&quot;,&quot;full_text&quot;:&quot;so, Next.js 13 came out today with a first beta preview of React Server Components on by default. it&#8217;s a big deal. i want to thread a few personal reflections on this&quot;,&quot;username&quot;:&quot;dan_abramov&quot;,&quot;name&quot;:&quot;&#1076;&#1101;&#1085;&quot;,&quot;profile_image_url&quot;:&quot;&quot;,&quot;date&quot;:&quot;Wed Oct 26 01:12:36 +0000 2022&quot;,&quot;photos&quot;:[],&quot;quoted_tweet&quot;:{},&quot;reply_count&quot;:0,&quot;retweet_count&quot;:382,&quot;like_count&quot;:2922,&quot;impression_count&quot;:0,&quot;expanded_url&quot;:{},&quot;video_url&quot;:null,&quot;belowTheFold&quot;:true}" data-component-name="Twitter2ToDOM"></div><p><em>P.S. I talked about this evolution during the <a href="https://www.youtube.com/watch?v=XruemT74Nok&amp;list=PLRvKvw42Rc7O0eWo2m_guXdZsGTEQM_jj">Reactathon keynote</a>.</em></p><h2>Less time connecting tools, more time building products</h2><p>React has been around for almost <a href="https://en.wikipedia.org/wiki/React_(JavaScript_library)">10 years</a> &#8211; and the web has grown with it.</p><p>Every aspect of frontend development has seen innovation: linting, formatting, compiling, bundling, minifying, deploying, and more. Developers want to spend less time configuring their tooling and more time writing React code, while still taking advantage of these latest advancements.</p><p>Teams who build with React frameworks can focus on components and business logic, and lean on battle-tested open-source solutions for routing, rendering, data fetching, styling, authentication, testing, and more.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!cZqI!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F6920b240-bdba-40c5-af99-57bc964283c1_2478x2784.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!cZqI!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F6920b240-bdba-40c5-af99-57bc964283c1_2478x2784.png 424w, https://substackcdn.com/image/fetch/$s_!cZqI!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F6920b240-bdba-40c5-af99-57bc964283c1_2478x2784.png 848w, https://substackcdn.com/image/fetch/$s_!cZqI!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F6920b240-bdba-40c5-af99-57bc964283c1_2478x2784.png 1272w, https://substackcdn.com/image/fetch/$s_!cZqI!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F6920b240-bdba-40c5-af99-57bc964283c1_2478x2784.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!cZqI!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F6920b240-bdba-40c5-af99-57bc964283c1_2478x2784.png" width="530" height="595.521978021978" data-attrs="{&quot;src&quot;:&quot;https://bucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com/public/images/6920b240-bdba-40c5-af99-57bc964283c1_2478x2784.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1636,&quot;width&quot;:1456,&quot;resizeWidth&quot;:530,&quot;bytes&quot;:178053,&quot;alt&quot;:&quot;An iceberg of react frameworks, showing that while most of the time it seems it's just components and business logic, the reality is that under water there are many other things frameworks are handling for you, like authentication, rendering, routing, state management, i18n, styling, and more.&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="An iceberg of react frameworks, showing that while most of the time it seems it's just components and business logic, the reality is that under water there are many other things frameworks are handling for you, like authentication, rendering, routing, state management, i18n, styling, and more." title="An iceberg of react frameworks, showing that while most of the time it seems it's just components and business logic, the reality is that under water there are many other things frameworks are handling for you, like authentication, rendering, routing, state management, i18n, styling, and more." srcset="https://substackcdn.com/image/fetch/$s_!cZqI!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F6920b240-bdba-40c5-af99-57bc964283c1_2478x2784.png 424w, https://substackcdn.com/image/fetch/$s_!cZqI!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F6920b240-bdba-40c5-af99-57bc964283c1_2478x2784.png 848w, https://substackcdn.com/image/fetch/$s_!cZqI!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F6920b240-bdba-40c5-af99-57bc964283c1_2478x2784.png 1272w, https://substackcdn.com/image/fetch/$s_!cZqI!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F6920b240-bdba-40c5-af99-57bc964283c1_2478x2784.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Are you building application value? Or building undifferentiated infrastructure?</figcaption></figure></div><p>Further, teams can <a href="https://vercel.com/blog/upgrading-nextjs-for-instant-performance-improvements">update to the latest versions</a> of these frameworks and instantly receive both performance benefits and new tools to help them improve the end-user experience. In some cases, you <a href="https://twitter.com/leeerob/status/1607461222584291328">add a command line flag</a> and take advantage of multi-year and multi-million dollar investments into <a href="https://leerob.io/blog/rust">Rust-based JavaScript tooling</a>. It&#8217;s harder to justify investments into developer experience and tooling when building an internal framework (for most teams).</p><h2>Easier onboarding and training of new developers</h2><p>Frameworks help you <a href="https://twitter.com/getDanArias/status/1607727505217294341">spend more time writing React code</a>.</p><p>Compiling, bundling, minifying, code splitting, server-rendering, routing &#8211; by using a framework, your team spends less time building and maintaining solutions that are solved problems.</p><p>For example, <a href="https://vercel.com/blog/migrating-a-large-open-source-react-application-to-next-js-and-vercel">moving from React + Express + Webpack to a framework</a> resulted in removing <strong>20,000+ lines of code</strong> and <strong>30+ dependencies</strong> &#8211; while improving HMR (Hot Module Reloading) from <strong>1.3s to 131ms</strong>.</p><p>Companies who choose to build with open-source React frameworks benefit from:</p><ul><li><p><strong>Immediate knowledge ramp-up:</strong> New engineers can ship code on day one, bringing existing knowledge of popular frameworks from their last role or hobby projects.</p></li><li><p><strong>Up-to-date documentation:</strong> When developers are stuck, they&#8217;re able to reference documentation that&#8217;s updated daily or <a href="https://twitter.com/rauchg/status/1605997497976885248">easily search Stack Overflow for their stack trace</a><a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-2" href="#footnote-2" target="_self">2</a>.</p></li><li><p><strong>Best-in-class security:</strong> Bugs and security vulnerabilities are reported and patched by communities of <a href="https://twitter.com/rauchg/status/1604880119666905088">thousands of developers</a> instead of overworked platform teams.</p></li></ul><p>Meta (the creators of React) have their own closed-source web framework. Many innovations in React have come from years of dogfooding new features (like <a href="https://beta.nextjs.org/docs/rendering/server-and-client-components">Server Components</a>) on <a href="http://Facebook.com">Facebook.com</a> through this framework.</p><p>Using an open-source React framework, built on top of the latest React UI primitives, is the closest way to emulate the tools developers at Facebook use to build one of the largest web applications in the world.</p><h2>Flexibility for different rendering strategies</h2><p>React, by itself, is primarily used on the client (in the browser).</p><p>In a standard React application, the browser receives an empty HTML shell from the server along with JavaScript instructions to construct the UI. This is called&nbsp;<a href="https://nextjs.org/learn/foundations/how-nextjs-works/client-and-server">client-side rendering</a>&nbsp;because the initial rendering work happens on the user's device.</p><p>While this is a valid pattern for certain types of applications (especially those behind a login), React&#8217;s widespread usage for any type of web page made developers also want to <a href="https://nextjs.org/learn/foundations/how-nextjs-works/rendering">prerender</a> content from the server. The HTML is generated in advance rather than computed on the user's device.</p><p>Sending HTML from the server (or how most sites work) can provide some benefits:</p><ol><li><p>It&#8217;s typically faster to do expensive work on a powerful server than on a user&#8217;s device.</p></li><li><p>It&#8217;s a better user experience to see more content on the initial paint versus a loading spinner.</p></li><li><p>It&#8217;s a better user experience when running A/B tests or experiments, preventing layout shifts.</p></li></ol><p>React does give you <a href="https://beta.reactjs.org/reference/react-dom/server/renderToPipeableStream">APIs to render content to HTML</a>, which might work for your use case. However, the developer is still left with connecting many pieces together for the rest of the application experience. This is where frameworks provide a cohesive experience on top of React when you need to prerender content.</p><p>It might sound like all React apps should be server-rendered. The reality is, of course, more nuanced. For certain routes, it&#8217;s likely better to generate a static asset and distribute it around the world using an <a href="https://vercel.com/docs/concepts/edge-network/overview">Edge Network</a> (placing the assets closer to your users).</p><p>For other pages, the client-only model might be better. You can download the JavaScript for the page <em>once</em>, cache the assets on the device (which have a unique hash in the file name for the given version), and then quickly navigate between pages (giving you the single-page application feel). This might be a better solution for applications that require offline support.</p><p>There&#8217;s no silver bullet or single rendering strategy that works. Static, server, or client rendered &#8211; all are valid options depending on the requirements. Frameworks can empower you to make this decision on a per-route basis, rather than needing to make the <em>entire</em> application a static site or server-rendered.</p><h2>Opinionated choices prevent bikeshedding</h2><p>Picking React is <strong>one decision</strong> of <em>many</em> frontend architecture choices.</p><p>You will likely also need to consider:</p><ul><li><p>How should we handle routing? (i.e. using the file system or through some configuration)</p></li><li><p>How should we only load the JavaScript needed for the current page? (i.e. code splitting)</p></li><li><p>How should we prevent regressions in our codebase? (e.g. using conformance and linting)</p></li><li><p>How should we load data for a given page? (i.e. server or client)</p></li><li><p>How should we deploy our React application? (e.g. using Docker, Node.js, or static files)</p></li><li><p>How should we style our code? (e.g. CSS modules, Tailwind CSS, etc.)</p></li><li><p>How should we handle authentication?</p></li></ul><p>Frameworks make many of these decisions for you, as well as provide you with tools and components to solve common problems on the web. For example, you will probably need to use <a href="https://nextjs.org/docs/basic-features/image-optimization">images</a>, <a href="https://nextjs.org/docs/basic-features/font-optimization">fonts</a>, or <a href="https://nextjs.org/docs/basic-features/script">third-party scripts</a> when building a website. Frameworks can give you tools to help optimize your usage of these primitives, building on top of <a href="https://leerob.substack.com/p/why-im-optimistic-about-javascripts">React, JavaScript, and the web platform</a>.</p><p>Building with an open-source framework that&#8217;s well adopted gives you access to many contributors creating, maintaining, and documenting common issues &#8211; codifying them into conformance rules and <a href="https://nextjs.org/docs/basic-features/eslint#eslint-config">ESLint configurations</a>. There are sometimes even suggestions built into common web performance measurement tools like <a href="https://twitter.com/leeerob/status/1506332606371086338">Lighthouse</a>.</p><p>It&#8217;s important for frameworks to be opinionated, but still have ways to eject or give access to the underlying primitives to allow developers to not feel a loss of control. For example, maybe this is a <a href="https://github.com/vitejs/awesome-vite#plugins">robust plugin system</a>, or maybe this is running <a href="https://nextjs.org/docs/advanced-features/middleware">arbitrary routing logic before each request</a>.</p><h2>Deploy anywhere and incrementally adopt</h2><p>It&#8217;s not uncommon to have internal platform teams supporting the delivery of bespoke React applications at larger companies. Since their tooling is not built on established, open platforms, developers often end up wasting time working on solved infrastructure problems.</p><p>One of the benefits of using a framework is that they all have support for either self-hosting on your own infrastructure (either through Docker, Node.js, or other means like uploading static assets) or by using <a href="https://twitter.com/leeerob/status/1583529921153900545">managed platforms</a>, which automate every process of the <a href="https://techcrunch.com/2022/12/20/vercel-makes-it-easier-to-collaborate-on-preview-deployments">iteration</a> and delivery of the software.</p><p>Additionally, many React frameworks have extensive documentation on how to incrementally adopt their tool, including providing low-level features like <a href="https://nextjs.org/docs/advanced-features/middleware">URL proxies</a>, allowing you to rewrite some incoming requests to your new framework to your existing application.</p><h2>It&#8217;s still just React</h2><p>While frameworks provide abstractions and opinionated choices, at the end of the day, you spend the majority of your time writing React code. And that React code is portable between other React-based frameworks. This optionality is great for developers.</p><p>Further, React frameworks are increasingly aligned with the <a href="https://leerob.substack.com/p/why-im-optimistic-about-javascripts">web platform</a>. As patterns for building React applications have emerged and solidified, we&#8217;re now seeing stronger recommendations from both the <a href="https://beta.reactjs.org/">official React documentation</a>, as well as frameworks and libraries in the community.</p><p>While the innovation in the React and single-page application space has been abundant, we&#8217;re now seeing frameworks use the best of the client, and the best of the server, all while taking full advantage of the web platform.</p><h2>Conclusion</h2><p>React has evolved. It&#8217;s now a:</p><ul><li><p><strong>Library:</strong> Sprinkle interactivity into any webpage</p></li><li><p><strong>Architecture:</strong> UI patterns and primitives for frameworks to build with</p></li><li><p><strong>Community:</strong> Well used and documented, incrementally adoptable</p></li><li><p><strong>Ecosystem:</strong> Learn once, write everywhere (web, <a href="https://reactnative.dev/">native</a>, <a href="https://docs.pmnd.rs/react-three-fiber/getting-started/introduction">3D</a>, and more)</p></li></ul><p>If you&#8217;re building with React, <strong>you should probably use a framework</strong> to spend more time writing product code, and less time connecting toolchains. The specific framework you use doesn&#8217;t matter as much as the benefits from not reinventing the wheel building your own.</p><p></p><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-1" href="#footnote-anchor-1" class="footnote-number" contenteditable="false" target="_self">1</a><div class="footnote-content"><p>The two React frameworks in the 6% were Next.js and Gatsby. There are other React frameworks, but these were the only detected (looking at <code>__next</code> element for Next.js and <code>___gatsby</code> for Gatsby) in the top 10,000. Also, side note, Next.js <a href="https://twitter.com/github_tracker/status/1610165503468658689">has passed CRA</a> now.</p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-2" href="#footnote-anchor-2" class="footnote-number" contenteditable="false" target="_self">2</a><div class="footnote-content"><p>Or you can use ChatGPT (ironically <a href="https://twitter.com/rauchg/status/1598440645680828416">built with Next.js</a>) to <a href="https://twitter.com/leeerob/status/1598738172879437824">generate a React site</a> entirely with AI.</p></div></div>]]></content:encoded></item><item><title><![CDATA[Why I'm Optimistic About JavaScript's Future]]></title><description><![CDATA[Learn once, write everywhere &#8211; that's the JavaScript way.]]></description><link>https://leerob.substack.com/p/why-im-optimistic-about-javascripts</link><guid isPermaLink="false">https://leerob.substack.com/p/why-im-optimistic-about-javascripts</guid><dc:creator><![CDATA[Lee Robinson]]></dc:creator><pubDate>Sun, 18 Dec 2022 22:56:44 GMT</pubDate><enclosure url="https://bucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com/public/images/c7078013-fbff-431c-9560-a3f53560435a_2560x1440.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><em>Hey, I&#8217;m <a href="http://leerob.io/">Lee</a> &#8211; VP of Developer Experience at <a href="https://vercel.com/home">Vercel</a>. I write about things I&#8217;m optimistic about in web development. I used to run a newsletter on Revue (<a href="https://techcrunch.com/2022/12/14/twitter-shuts-down-revue-its-newsletter-platform/">RIP</a>), so I&#8217;m now on Substack. Welcome &#8211; subscribe if you want to be notified when I post.</em></p><div><hr></div><p>I&#8217;m optimistic about JavaScript.</p><p>Developers want to write JavaScript &#8211; and they want it to run everywhere &#8211; in the browser, on the server, or at the edge.</p><p>With all its quirks and imperfections, JavaScript continues to rise in adoption due to its built-in growth hack (it&#8217;s in the browser), its massive ecosystem of tools and libraries, and the continued growth and adoption of TypeScript. And increasingly, developers are able to learn an API (like <code>Request</code> or <code>Response</code>) and reuse that same knowledge <em>everywhere</em>.</p><p>Having an agreed-up common set of APIs (i.e. standards) and platforms that all support that same interface (e.g. cross-browser support) means web developers can now <strong>learn once, write everywhere</strong>.</p><p>This post will outline recent improvements to the web platform across the browser, server, and edge.</p><h2>JavaScript: In the Browser</h2><p>Web developers today spend less time writing vendor-specific JavaScript or vendor-specific CSS selectors than ever before.</p><pre><code>function isIE11() {
  return !!window.MSInputMethodContext &amp;&amp; !!document.documentMode;
}</code></pre><p>We&#8217;ve escaped the world of padding hacks for maintaining an element&#8217;s <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/aspect-ratio">aspect ratio</a>:</p><pre><code>@supports not (aspect-ratio: 16/9) {
  .aspectRatio {
    overflow: hidden;
    padding-bottom: 56.25%;
    height: 0;
  }
}</code></pre><p>Two converging trends are making this possible:</p><ul><li><p><strong>The death of Internet Explorer:</strong> Now that IE 11 is <a href="https://www.bleepingcomputer.com/news/microsoft/microsoft-edge-update-will-disable-internet-explorer-in-february/">officially retired</a>, web developers can write less vendor-specific CSS, leading to smaller stylesheets and fewer hacks.</p></li><li><p><strong>Browser engine alignment:</strong> The three major browser engines (Chromium/Chrome, Gecko/Firefox, and Webkit/Safari) now have the best cross-browser support for JavaScript, CSS, and Web APIs we&#8217;ve ever seen. Kudos to the <a href="https://web.dev/interop-2022/">Interop project</a>.</p></li></ul><p>Now, of course, it&#8217;s not perfect across browser engines, nor will it likely ever be. But it&#8217;s the best it&#8217;s been and I&#8217;m optimistic. Thousands (or millions) of developer hours will be saved cumulatively by not spending a week digging into esoteric IE bugs.</p><p>Here&#8217;s an example of how this alignment can benefit all web developers. Imagine you are a framework author trying to write a reusable image component to help thousands of developers achieve great performance when using images. In 2020, just a few years ago, you need to work <em>around</em> the web platform.</p><p>Loading an image without causing a <a href="https://web.dev/cls/">layout shift</a>, correctly maintaining the aspect ratio, and not degrading initial page load performance due to image size/weight was difficult to implement with support across all major browsers. This led to developers either ignoring the issues, or the frameworks writing component abstractions that produced code like:</p><pre><code>&lt;span&gt; &lt;-- needed to maintain aspect ratio
  &lt;span&gt; &lt;-- needed to maintain aspect ratio, CSS padding hacks
    &lt;img src="" style="" /&gt; &lt;-- inline styles to prevent layout shift
    &lt;noscript&gt;...&lt;/noscript&gt; &lt;-- JS needed for IntersectionObserver
  &lt;/span&gt;
&lt;/span&gt;</code></pre><p>It&#8217;s a different story in 2022. There&#8217;s cross-browser support for: <code>aspect-ratio</code>, <a href="https://web.dev/optimize-cls/">width/height attributes</a> to prevent layout shift, native <a href="https://developer.mozilla.org/en-US/docs/Web/Performance/Lazy_loading">image lazy-loading</a>, and pure CSS/SVG-based blur-up image placeholders. The above code can drop the wrapping elements and work without runtime JavaScript needed.</p><pre><code>&lt;img
  alt="A kitten"
  decoding="async"
  height="200"
  loading="lazy"
  src="https://placekitten.com/200/200"
  style="aspect-ratio: auto 1 / 1"
  width="200"
/&gt;</code></pre><h2>JavaScript: On the Server</h2><p>Isomorphic JavaScript, or code that could run on both the client and the server, has been the ideal state for many web developers. Learn once, write everywhere, right? Until recently, Node.js and the web platform were out of alignment.</p><p>Consider fetching data over HTTP. In the browser, we had the <a href="https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API">Web Fetch API</a>. Prior to Node.js 18, there was no built-in solution for fetching data. Making a <code>fetch</code> required using packages like <code>node-fetch</code> or <code>undici</code>, which had a similar but slightly different API, often in non-obvious ways.</p><p>This lack of alignment between platforms meant that tools for writing isomorphic JavaScript, like Next.js, needed to add polyfills so developers could use <code>fetch</code> on both the client and the server. With Node.js 18, these tools can now remove additional JavaScript needed to polyfill platform differences, ultimately resulting in less JavaScript needed.</p><p>I&#8217;m optimistic about JavaScript (and TypeScript) on the server. It&#8217;s not just <code>fetch</code>, either. It&#8217;s <code>Request</code>, <code>Response</code>, and <strong>100+ other APIs</strong> that are now available in both the browser and in Node.js. Browser vendors and companies building server infrastructure are now <a href="https://wintercg.org/">working closer than ever</a> to provide a standard set of APIs that can run everywhere, including edge computing platforms.</p><h2>JavaScript: At the Edge</h2><p>Edge computing, the often misunderstood and newest target for running JavaScript, has had the least standardization of the three (browser, server, edge).</p><p>It&#8217;s helpful to think about edge as the highest level of abstraction, where you&#8217;re spending all of your time on business logic.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!-3iq!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F5b450d64-66d2-4072-bdc0-4eb21d2cbdd5_3840x2160.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!-3iq!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F5b450d64-66d2-4072-bdc0-4eb21d2cbdd5_3840x2160.png 424w, https://substackcdn.com/image/fetch/$s_!-3iq!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F5b450d64-66d2-4072-bdc0-4eb21d2cbdd5_3840x2160.png 848w, https://substackcdn.com/image/fetch/$s_!-3iq!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F5b450d64-66d2-4072-bdc0-4eb21d2cbdd5_3840x2160.png 1272w, https://substackcdn.com/image/fetch/$s_!-3iq!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F5b450d64-66d2-4072-bdc0-4eb21d2cbdd5_3840x2160.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!-3iq!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F5b450d64-66d2-4072-bdc0-4eb21d2cbdd5_3840x2160.png" width="1456" height="819" data-attrs="{&quot;src&quot;:&quot;https://bucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com/public/images/5b450d64-66d2-4072-bdc0-4eb21d2cbdd5_3840x2160.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:819,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:409877,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!-3iq!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F5b450d64-66d2-4072-bdc0-4eb21d2cbdd5_3840x2160.png 424w, https://substackcdn.com/image/fetch/$s_!-3iq!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F5b450d64-66d2-4072-bdc0-4eb21d2cbdd5_3840x2160.png 848w, https://substackcdn.com/image/fetch/$s_!-3iq!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F5b450d64-66d2-4072-bdc0-4eb21d2cbdd5_3840x2160.png 1272w, https://substackcdn.com/image/fetch/$s_!-3iq!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F5b450d64-66d2-4072-bdc0-4eb21d2cbdd5_3840x2160.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Moving towards edge computing platforms means a tradeoff for more abstraction, but focusing exclusively on business logic.</figcaption></figure></div><p>Edge isn&#8217;t something completely new, but rather a deliberate and intentional tradeoff from the existing Node.js world.</p><p>You <em>want</em> to write JavaScript, but edge compute infrastructure requires a lighter subset of the (quite large) Node.js API surface area. By making this tradeoff for a subset of Node.js APIs that also run in the browser, you can have consistently fast cold boots and more cost-effective compute workloads. That sounds <em>pretty nice</em>.</p><p>Let&#8217;s look at an example. In this case, I&#8217;ll use a <a href="https://vercel.com/blog/edge-functions-generally-available">Vercel Edge Function</a>. But it could also be other edge computing platforms like Cloudflare or Deno. For me, the best part of this code is actually that it&#8217;s fairly boring. It looks like Node.js.</p><pre><code>export const config = {
  runtime: 'edge'
}

// Web standard Request API
export default function handler(req: Request) {
  // Web standard URL API
  const { searchParams } = new URL(req.url)
  const name = searchParams.get('name')

  // Web standard Fetch API
  const req = await fetch('https://...', { body: { name } })
  const data = await req.json()

  // Web standard Response (.json is new)
  // https://github.com/whatwg/fetch/issues/1389
  return Response.json(data);
}</code></pre><p>Here&#8217;s the kicker: it&#8217;s not just about the infrastructure. It&#8217;s about the frameworks that are embracing these same Web APIs and helping thousands of new developers learn once and write everywhere.</p><p>This code could work with Next.js. Or SvelteKit. Remix. Fresh. Or the next, new web framework that builds upon the same set of standard APIs.</p><p><strong>What an incredible time to be a web developer.</strong></p><div><hr></div><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://leerob.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading. If you enjoyed it, subscribe to get new posts in your inbox.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p></p>]]></content:encoded></item></channel></rss>