<?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:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by Vaughan Rouesnel on Medium]]></title>
        <description><![CDATA[Stories by Vaughan Rouesnel on Medium]]></description>
        <link>https://medium.com/@vjpr?source=rss-f83a5f1790da------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/0*y1OZLP-CHgcBjyIu.jpg</url>
            <title>Stories by Vaughan Rouesnel on Medium</title>
            <link>https://medium.com/@vjpr?source=rss-f83a5f1790da------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Sat, 24 Jul 2021 03:01:46 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@vjpr/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com" rel="hub"/>
        <item>
            <title><![CDATA[Don’t we all just want to use SQL on the frontend?]]></title>
            <link>https://vjpr.medium.com/dont-we-all-just-want-to-use-sql-on-the-frontend-6b9d38c08146?source=rss-f83a5f1790da------2</link>
            <guid isPermaLink="false">https://medium.com/p/6b9d38c08146</guid>
            <category><![CDATA[javascript]]></category>
            <category><![CDATA[sql]]></category>
            <category><![CDATA[frontend]]></category>
            <category><![CDATA[graphql]]></category>
            <category><![CDATA[react]]></category>
            <dc:creator><![CDATA[Vaughan Rouesnel]]></dc:creator>
            <pubDate>Thu, 15 Apr 2021 16:43:50 GMT</pubDate>
            <atom:updated>2021-04-15T17:25:42.432Z</atom:updated>
            <content:encoded><![CDATA[<p>When we consume REST, GraphQL, or RPC APIs from the frontend, most of the time these api calls just end up being translated into SQL statements on the backend.</p><p>So why don’t we just write SQL in the frontend to begin with?</p><p>I’m serious.</p><p>To ensure a snappy app, we usually need a normalized cache on the frontend. When we start trying to do optimistic updates is when things get complicated really quickly. Unless our frontend data model maps exactly to our backend model, we have to do a bunch of quirky, manual cache updates. Just check out Apollo’s guide for <a href="https://www.apollographql.com/docs/react/performance/optimistic-ui">optimistic UI</a> — its intense! <a href="https://github.com/vercel/swr">Swr</a> and <a href="https://react-query.tanstack.com/guides/optimistic-updates#_top">react-query</a> also leave the manual query invalidation and cache updating to you.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*eGjC2opoHcEC6gzpv2LhtA.png" /><figcaption>Apollo GraphQL Optimistic UI Guide — <a href="https://www.apollographql.com/docs/react/performance/optimistic-ui/#adding-to-a-list">Adding to a list</a></figcaption></figure><p>The hard truth is that if we want perfect optimistic UI updates, we are going to need to replicate our backend data model in our frontend. And if we are using an SQL database with relational data, then we should have an SQL database in the frontend. If you can make this work you also get offline support for free.</p><p>Think about all the code you normally write to process API responses on the frontend. I usually end up with a bunch of Lodash (groupBy, filter, map, reduce) to shape the data I get from the server. This always becomes unwieldy and I always end up wishing I could just use SQL.</p><p>An example might be a task management app like Asana, with a sidebar of Projects, and a list of Tasks next to it, with a task count showing next to each project.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/494/1*Xn5d_UESXYDqQAqRRU5zKA.png" /></figure><p>We can pull all the data for the view in one query:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*DHBymo-bNeJ7yE5FbYPejA.png" /></figure><p>If you add a new task to a project, you need to update the project count in the sidebar. Ideally, you simply want to add a new Tasks entity and associate it with a Project. This is one line of SQL.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*_XPxVO0tM0vDuZm1mv8cSQ.png" /></figure><p>Then we run all our queries again, and our interface will be automatically consistent with our data model.</p><p>But if our API is not derived by an underlying relational model, and we want optimistic updates, we have to manually update our local model as seen in the Apollo example above — whereby the new comment is manually added to the cached query response. We also have to be careful to only re-trigger dependent queries because they will result in new fetches.</p><p>Whenever we render or modify a local entity that exists in one or more places in our UI, we want it to be consistent.</p><h3>Why hasn’t this been tried?</h3><p>We used to be told our APIs must be REST. So that’s what we did.</p><p>Then GraphQL liberated us from REST, and said: it’s okay to do RPC again over a single endpoint. And the same people had their own protocol, so that’s what we did.</p><p>But now that it’s okay to do RPC again, maybe we can complete the loop, and it becomes okay to do…SQL again…just like we did when we built desktop apps.</p><p>Security concerns are probably also a significant reason why people may have brushed it aside. Without built-in safety it can be super dangerous.</p><h3>How is this going to work?</h3><p>We already have have <a href="https://github.com/sql-js/sql.js/">SQLite in the browser</a> via wasm that we could use for this. A pure SQLite JS implementation would be cool though.</p><p>We need a restrictive SQL parser to run server-side to restrict what can be run and prevent SQL injection. Maybe we need a query-builder/ORM to generate a safe intermediary SQL language in JSON so that we can validate it. Maybe taking some inspiration from <a href="https://www.prisma.io/docs/concepts/components/prisma-client">Prisma’s type-safe data-mapper client</a> could be used to help people write safe queries that won’t fail server-side validation unexpectedly.</p><p>On the server, maybe views and row-level security as <a href="http://twitter.com/unodgs">@unodgs</a> has written about <a href="https://medium.com/@unodgs/sql-on-the-frontend-react-postgres-and-rls-part-1-76bbe4f97353">here</a> could help. Don’t forget that GraphQL can also have over-fetching security vulnerabilities if resolvers are not carefully implemented, so its not as crazy as it seems.</p><p>For mutations, because they are quite dangerous and require a lot of validation, we could fallback to REST, GraphQL mutations, RPC. Unless there is great value in allowing bulk INSERT/UPDATE as a sub-query of a SELECT, its probably better to avoid sending them server-side, and only use them to update our local data model.</p><h3>Interesting</h3><p>Maybe SQL could be our unified data model if we can get Postgres’ foreign data wrapper extension to work in the browser, wrapping LocalStorage, Chrome Extension APIs, IndexedDB, etc. Even perhaps third-party APIs — in a similar way that GraphQL federation works.</p><p>An advantage of a local <em>effortlessly</em>-normalized cache is we don’t have to worry so much about how we write our queries, because they will most likely hit the cache, and we can rely on some smart logic to retrieve the data that is missing.</p><p>With the WAL (write ahead log) we also have the ability to time-travel like Redux promised us. This would also come in handy perhaps in efficiently syncing changes, and rolling back optimistic UI updates on server error.</p><p>Maybe things like SSR and PJAX will mean we rely less on client-side state in the future though, and UI updates will come as HTML over WebSockets or something like that.</p><p>Could all a user’s data be represented by a single SQLite database synced frontend/backend that can be downloaded to provide a completely offline experience or data takeout feature?</p><p>Maybe we could also explore local-state as SQL too, à la <a href="https://www.apollographql.com/docs/react/local-state/local-state-management/">Apollo local-state</a>.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*iJ-_YD4fc1tKzeiAhLXk3A.jpeg" /><figcaption>Obligatory, all the things meme.</figcaption></figure><h3>Other approaches</h3><p>There is so much tooling and momentum around GraphQL, it would be good to salvage it. Maybe we could build a local SQL-based cache for GraphQL queries, and use something like <a href="https://www.prisma.io/docs/concepts/components/prisma-schema/data-model/">Prisma’s data model definition schema</a> to map our GraphQL queries to our local SQLite database.</p><p>Something other than SQLite. It’s sad that <a href="https://softwareengineering.stackexchange.com/questions/220254/why-is-web-sql-database-deprecated">WebSQL disappeared</a> but as I recently heard Jake Archibald mention on <a href="https://devchat.tv/js-jabber/jsj-478-browser-standards-rampage-can-we-have-nice-things/">a JS Jabber podcast</a> episode, what would be better is a byte-level storage api so that people can implement their own efficient SQL engines in the browser.</p><h3>Why now?</h3><p>SQL is everywhere today. No-code tools are all embracing SQL. Google Sheets, Airtable, Retool. The best way to unleash your non-technical employees is to get them access to your data via SQL. Everyone wants it. That’s why there is such a plethora of new tools out there.</p><p>SQLite is being mentioned a lot too recently, which is one of the reasons I thought about writing this down. If more people are comfortable using it</p><h3>Thoughts?</h3><p>What am I missing? I’m keen to play around with this to see if it’s workable, because the one thing for sure is that the way we currently do optimistic UI updates with Apollo and react-query is untenable.</p><p>—</p><p>HN Discussion: <a href="https://news.ycombinator.com/item?id=26822884">https://news.ycombinator.com/item?id=26822884</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=6b9d38c08146" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[The Multi-Monorepo]]></title>
            <link>https://vjpr.medium.com/the-multi-monorepo-209041932fbf?source=rss-f83a5f1790da------2</link>
            <guid isPermaLink="false">https://medium.com/p/209041932fbf</guid>
            <category><![CDATA[monorepo]]></category>
            <category><![CDATA[nodejs]]></category>
            <category><![CDATA[programming]]></category>
            <category><![CDATA[javascript]]></category>
            <category><![CDATA[software-development]]></category>
            <dc:creator><![CDATA[Vaughan Rouesnel]]></dc:creator>
            <pubDate>Tue, 16 Feb 2021 02:02:49 GMT</pubDate>
            <atom:updated>2021-02-16T02:02:49.840Z</atom:updated>
            <content:encoded><![CDATA[<p><em>tl;dr: Use top-level folders to group your codebase based on access rights (such as open source vs proprietary) and use a `git-subtree` approach to sync these folders to their own fully-functional monorepos.</em></p><p>In this post I will walk through the options and drawbacks of working with many-repos, and introduce the idea of the <em>multi-monorepo</em>.</p><h3>Monorepos are great</h3><p>You checkout a single repo. Tooling configuration (lint, style, etc.) can be shared across all packages. Make a change to some code in a package/service. Use your IDE’s refactoring to find all usages of the modified code. All dependent packages’ tests are run. Publishable packages are rebuilt and published. Deployable packages are deployed. And all this can be done in a feature branch. You’ve heard this all before.</p><p>In a “many-repo” setup, the need to modify code in just one external repo quickly becomes a nightmare.</p><h3>Problems with “many-repos”</h3><p>Let’s take the example of needing to make a breaking API change to a transitive dependency of a service.</p><p><em>E.g. </em><strong><em>Service</em></strong><em> depends on </em><strong><em>Foo</em></strong><em> which depends on </em><strong><em>Bar</em></strong><em>. You make a change in </em><strong><em>Bar</em></strong><em>, which requires a change to </em><strong><em>Foo</em></strong><em>, which requires a change to </em><strong><em>Service</em></strong><em>.</em></p><pre><strong>Service</strong> <em>depends on </em><strong>Foo</strong> <em>depends on </em><strong>Bar</strong></pre><p>The key is being able to make these three changes on an atomic feature branch that is deployable to a test environment and can be checked-out, manually tested and reviewed on a co-workers computer, and discussed in an online PR tool.</p><p>To access dependencies in other repos your options are:</p><ul><li><strong>git submodules</strong><br>Your repo stores a reference to a commit of another repo and pulls in the files via git cli commands.</li><li><strong>many-repo management cli tool<br></strong>You checkout multiple repos and symlink the packages between repos. These cli tools allow you to sync changes.<br>E.g. <a href="https://github.com/mateodelnorte/meta">meta</a>, <a href="https://github.com/nosarthur/gita">gita</a>, <a href="https://github.com/mixu/gr">gr</a></li><li><strong>package registries</strong><br>Importing published packages via a package registry like npm.</li><li><strong>git subtrees<br></strong>Store a copy of the code from the other repo in your git tree and sync changes back and forth.</li></ul><h4><strong>Git submodules</strong></h4><p>Only store a reference to a commit so if you wanted to make a feature branch, you would need to manually create that branch in two other repos, and keep them in sync. This can be tedious and error-prone.</p><h4><strong>Many-repo management tools + symlinks</strong></h4><p>If you checkout multiple repos, you can use the aforementioned tools to branch multiple repos with one command. You would then symlink the packages together.</p><p>Symlinks in Node.js present many difficulties due to the way that the Node.js module resolver uses the `realpath` rather than the logical path to resolve dependencies, breaking peer dependency resolution in some cases.</p><p>And what happens if you need to branch from a branch? Or pull a deployed tag when fixing a production bug? It starts getting insane rather quickly. Whereas with a single repo everything is extremely simple.</p><h4><strong>Package registries</strong></h4><p>This is the most painful way. Sharing code via package registries means you would probably check out two repos for Foo and Bar, have a script to symlink everything together, make your changes, run tests in three separate repos, then unlink, publish alpha versions, and hope everything works.</p><p>The mentioned issues with symlinks above leads to tons of risk that what you have tested and was working locally will break when using published packages. If a bug appears that wasn’t covered by a test, then you have two more packages to publish and you will lean heavily on your tooling for this. If anything goes wrong, you are in trouble.</p><h4><strong>Git subtrees</strong></h4><p>Git subtrees involve syncing commits from other repos inside your actual repo. When a new commit is made in another repo, you run a command that pulls in those commits to a sub-folder in your code base. When you want to push updates back out, you filter all commits that touched that sub-folder and merge them back.</p><p>The advantage here is that there is a single commit SHA identifying all your deployed code and its dependencies. If you need to make changes to Foo and Bar, then you simply make the changes as one commit, test, push, deploy, done. No complicated tooling to think about, or anything that will prevent you from fixing your bug.</p><p>The question to be asked at this point though, is what is the point of us even having other repos? Why can’t we use just a monorepo?</p><h3>What can’t we use just a monorepo?</h3><p>Monorepos have their disadvantages too. Noisy commit logs, slower CI pipelines, confusing and unfriendly for open-source contributors (must checkout and build entire codebase usually).</p><p>But there is one main thing they cannot do. Access rights per package/service.</p><p>The most common example would be a company open-sourcing a package that is a dependency in their private codebase.</p><p>When this is necessary it brings us back to all the downfalls of many-repos mentioned above. I would argue that this is a huge disincentive to more companies open-sourcing more stuff.</p><h3>The “multi-monorepo”</h3><p>The multi-monorepo approach is simply a monorepo, with one or more other repos synced in using git-subtree.</p><p>You would create a private repository similar to this:</p><pre>- package.json<br>- repos<br>  - <strong>org</strong><br>    - services<br>      - service-a<br>    - libs<br>      - foo<br>      - bar<br>  - <strong>org-open-source</strong><br>    - libs<br>      - some-open-source-thing<br>  - <strong>org-experiments<br></strong>    - libs<br>      - some-unstable-thing<br>  - <strong>client-project</strong></pre><p>The idea is to separate your code into top-level folders based upon access rights and sync these to other monorepos. I keep these folders in a folder called repos. (NOTE: Some may not necessarily be separate git repos as I mention below but I couldn’t think of a better name.)</p><p>Splitting at the top-level makes it clear to your team what is open-source and what is confidential, as well as to simple tooling to verify no confidential code leaks into open-source world.</p><p>There are other reasons to split code at the top-level, not all of which would need to be synced to other monorepos, but more-so to replicate “many-repo” ergonomics (vague word but will make sense when you read below). These reasons include:</p><ul><li><strong>Code stability/quality.</strong> In a many-repo world you would create a new repo for code experiments. Experiments might not require as high a level of test coverage or code quality checks as the rest of your code base. You would probably keep this in an ‘experiments’ branch and rebase often. Even so, the mental load is greatly reduced from having an entire other folder and the code quality rules can easily be set to ignore code in this folder.</li><li><strong>Working with other companies. </strong>Say your company is a web design agency. Maybe you have some libraries or a framework in active development that you use to support the projects you build for clients. You could work on your client’s project in your main monorepo alongside your framework or internal tooling. This framework or tooling would be published to a package registry. You would then sync your client’s code to a repo for them to use which would reference your open-source framework packages from the package registry.</li><li><strong>Solo developer working on many projects. </strong>Say you have a few private side-projects you are working on outside of your day job. You probably will want to share some code or tooling at one point between them. For example say you have 5 small websites and you don’t want to rewrite the same authentication code over and over. You could create a “repo” folder for each of the projects. If one starts to take off and you decide to hire someone to work on it — you just sync that to another repo.</li></ul><h3>Implementation</h3><p>Currently I use the excellent <a href="https://github.com/ingydotnet/git-subrepo">git-subrepo</a> project which is a shell script similar to subtree but easier to use. It’s quite a complicated script and when things go wrong it can be quite difficult to repair without spending a good bit of time grokking how things work. For this reason I was planning on rewriting it in TypeScript to make it easier to recover from bad state, and more helpful error messages — but I recently discovered <a href="https://github.com/adeira/shipit">adeira/shipit</a> which I will try first.</p><p>Some other interesting similar tools:</p><ul><li>Facebook built <a href="http://FBShipIt">FBShipIt</a> to help them open-source code from their monorepo.</li><li>I recently found <a href="https://github.com/adeira/shipit">adeira/shipit</a> (from Kiwi.com devs) which is a FBShipIt implementation in JavaScript which I am very eager to try out. The project is active since Jun 2020.<br><em>They call the master source-of-truth repo the “universe” repository.</em></li><li>The PHP Symphony framework uses <a href="https://github.com/splitsh/lite">splitsh/lite</a> shell script. There is a good presentation on that page about this topic and lots of good thinking.<br><em>They call it a “mono/manyrepo”.</em></li><li><a href="https://github.com/korfuri/awesome-monorepo#repository-management-tools">korfuri/awesome-monorepo</a> has a great list of monorepo tools that include some that allow repo syncing.</li><li>Other cool monorepo tools you should checkout include <a href="https://github.com/microsoft/rushstack">Rush</a> (from Microsoft), <a href="https://nx.dev/">Nx</a>, and the upcoming <a href="https://turborepo.com/">TurboRepo</a>.</li></ul><h3>Where to next?</h3><p>Monorepos have been growing in popularity for a while, yet the tooling is still not there yet, and they are only accessible to larger dev shops with the resources to invest in the tooling.</p><p>My hope is that multi-monorepo tooling will continue to improve over the coming years to allow more companies to easily open-source parts of their stack, and that all developers can reap the benefits of monorepos whilst remaining able to open-source their work, and to build more projects with more easily shared common code. IDE integration is also needed to allow seamless workflows.</p><p>I am working on a simple monorepo framework myself called Live, built upon the <a href="https://pnpm.js.org/">pnpm</a> package manager and hope to open-source everything soon. The goal is to achieve a seamless multi-monorepo workflow with a simple CLI and IDE integrations for WebStorm and VSCode to allow developers to share more code between projects and with the open-source community.</p><p>Keen to hear your thoughts!</p><ul><li><a href="https://twitter.com/vrouesnel">https://twitter.com/vrouesnel</a></li><li><a href="https://github.com/vjpr">https://github.com/vjpr</a></li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=209041932fbf" width="1" height="1" alt="">]]></content:encoded>
        </item>
    </channel>
</rss>