Wtf is a monorepo

Issue #98.May 2, 2022.2 Minute read.
Bytes

🚨 We’re hiring part-time writers to help us tell the stories of the JavaScript ecosystem on the ui.dev YouTube channel.

If you like writing, money, and light hazing rituals a great company culture, check out our job listing.


This week, we’ve got mono(repos), JavaScript reptiles, and a whole bag of zags.

Welcome to #98.


Clowns carrying a casket

Developers still using Lerna

RIP in peace, Lerna

Last week, Lerna officially gave up the ghost and declared that it’s no longer an actively maintained project. It’s always painful to lose a friend, but at least Lerna died doing what it loved — making us wait for it to build.

Let’s take a closer look at what monorepos *actually* do, what happened to Lerna, and where we go from here in a post-Lerna world.

Wtf is a monorepo? Monorepos let you split up large codebases into separate, independently versioned packages. This allows for easy code sharing between packages — which saves a lot of time, overhead, and complexity when working on larger projects with a big team.

That’s why they got their start at big enterprises (Google’s entire codebase is still housed inside one single monorepo megarepo). Lerna brought monorepos to the masses by providing an easy way for developers to use them in their JavaScript and React projects.

Why did Lerna die? The simple answer — it was really slow. One problem with monorepos is that whenever you make a code change, you have to rebuild everything that depends on the module you changed. For huge codebases, that was a huge PITA.

The more complicated answer — the project lacked consistent leadership and maintainers. Other monorepo tools (like NX and Turborepo) solved the slow build problem with creative solutions like remote caching. But without consistent contributors, Lerna wasn’t able to follow suit (it’s not anyone’s fault, OSS is just hard).

The bottom line: Let’s drink to the good times and pour one out for our fallen friend — and the 40,000 GitHub repos that still have Lerna as a dependency. Stay strong.


Patrick Star Meme

That’s one powerful back (end as a service) [sponsored]

Appwrite wants to write your backend for you

Congrats! Your team’s app made it to the top of Hacker News, and now you’ve got traffic coming out your eyeballs 🎉. There’s only one problem…

You went with a “usage-based” pricing plan for your hosting — and all that traffic just ate up the rest of the cash in your company bank account (RIP in peace).

Should’ve used Appwrite.

Appwrite is a self-hosted BAAS (backend as a service) platform that gives you a collection of easy-to-use REST APIs that abstract away all the complex and repetitive parts of building a secure backend.

And since it’s packaged as a set of (open-source) Docker microservices, you can host it however you want and not have to worry about getting burned by usage-based pricing.

It handles all of hairy stuff for you, so you can easily integrate your app with multiple user auth methods, set up a DB for storing and querying user data, and a lot more.

I guess that’s why they called it Appwrite. Finally makes sense now.

Check it out.


The Rock holding a large bag

Did somebody order a whole bag of zags?

Zag.js can save us from design system hell

The king of design systems, Sengun Adebayo, was back at it again last week with Zag.js — a framework agnostic toolkit for building complex interactions.

How did we get here? Sengun created Chakra, one of the most popular component libraries for React. It got popular enough that developers started asking them to support other frameworks, because if you give a mouse a cookie, he’s always gonna ask for Vue.

But it turns out that supporting UI interactions across frameworks is… not easy. That’s where Zag comes in. It allows you to share the “behavior” of the component while leaving the rest of the UI (styles and markup) to the framework of your choice.

To make that magic happen, Zag does some pretty cool stuff under the hood:

  • State Machines — helps nerds you feel good that your UI components will account for all of the mathematically possible states

  • Accessibility — takes care of the tricky details related to keyboard interactions, focus management, aria roles and attributes

  • Headless — provides the behavior and leaves the styles to you (letting devs handle CSS seems risky but yolo)

  • Modular — You can adopt them one at a time if you’re not ready for the whole Bag of Zags™️

The bottom line: If you’re lucky/unlucky enough to be working on design systems, Zag seems like a godsend. For the rest of us, we’re wondering if web components will ever find love after facing yet another cross framework rejection.


Jobs

Loops is looking for founding full-stack Next.js engineers

Most email platforms are showing their age — heck, this email was sent via a platform that is 9 years old now! Loops is a modern and beautiful alternative for SaaS companies. Our stack is Next.js, Postgres, and Tailwind. We just wrapped YC and our seed funding raise led by Craft Ventures. Come join our core engineering team!

Senior or Staff Frontend Engineer - React (100% Remote)

Close.com is looking for 3 experienced individuals that have a solid understanding of React and want to help design, implement and launch major user-facing features. Close is a 100% globally distributed team of ~55 high-performing, happy people that are dedicated to building a product our customers love.


🔬 Spot the Bug — Sponsored by Sema Software

Sema just published this white paper on why code reviews matter and how to integrate them effectively into your team and organization. It’s a great read.

class User {
  async constructor(userId) {
    const user = await getUser(userId)
    this.id = user.id;
    this.name = user.name;
    this.email = user.email;
  }
  ...
}

Cool Bits

  1. I’m convinced that there are less than ten people in the world who actually understand the new features in React 18. Lucky for you, I spent about 50 hours becoming one of those people and making this video, The Story of Concurrent React, so now you can become one of those people in about 12 minutes. Never say I don’t love you.

  2. The KendoReact team wrote this tutorial on how to generate a PDF from HTML in React — which guides you through the PDF rendering labyrinth. Hopefully we’ll run into David Bowie (and his tights) along the way. [sponsored]

  3. Ian Kettlewell wrote a great article about WebAssembly vs. JavaScript that’ll help you feel more informed when your low-level programming friend (who’s been reading a tad too much Nietzsche) goes on their weekly rant about how “JavaScript is mankind’s greatest disease.”

  4. EdgeDB is a devtool startup name created by GPT-3 new kind of database that claims to combine the best parts of relational DBs, graph DBs, and ORMs. Should’ve named it FrankenBased.

  5. Some Google developers created this free course on building progressive web apps. No idea how they had time to do that in between attending Google’s private Lizzo concert and riding around on their free electric scooters, but thank you for your service.

  6. Chris Garrett wrote about the Four Eras of JavaScript Frameworks. But I’m a little confused why they left out the Mesozoic Era, since many reptiles have helped to create the JS frameworks we know and love.

  7. Zaplib was a startup that aimed to make your apps faster by leveraging Rust and Wasm. Emphasis on was because Zaplib is now dead and there are some interesting technical reasons as to why.

  8. Taskless is an easy way to turn your edge functions into serverless queues. And yes, there are a lot of good “taskless” jokes, but we’ve already made enough fun of Googlers this issue.


🔬 Spot the Bug Solution — Sponsored by Sema Software

JavaScript doesn’t allow class constructors to be async. We have to do any async actions outside of a constructor. Static class methods can help with this.

class User {
  static async init(userId) {
    const user = await getUser(userId);
    return new User(user);
  }
  constructor(user) {
    this.id = user.id;
    this.name = user.name;
    this.email = user.email;
  }
} 

const me = await User.init(1)