Unlike all the other emails in your inbox today, we come in peace with nothing to sell you (but click on our ads pls).
Today, we’ve got a pig named Cheddar, a medieval calligraphist, and Tim McGraw’s BFF.
Welcome to #140 – here’s the web version.
When you play the game of Monorepo-opoly, you win or you die.
Last week, we mentioned that Nrwl/Nx raised $8.6m in VC to “take monorepos mainstream.” What we didn’t mention, is that their pitch deck was just a single slide of CEO Jeff Cross’ pig named Cheddar holding up a sign that said, “let’s bring home the bacon.” (Not really, but I bet A16Z would’ve loved that.)
So let’s take a closer look at what problems monorepos solve, what makes Nx a unique snowflake in mono-land, and where they’ll go from here, now that they’ve sold their souls raised some serious cash.
Monorepos 101: Monorepos let you split up large code bases into separate, independently versioned packages. This gives you easy code sharing between packages — which saves a lot of time, overhead, and complexity when working on large projects with a big team.
Large enterprises like Google helped popularize this approach, but early open-source monorepos for JavaScript (like Lerna) tortured developers with incredibly slow builds. Every time you made a code change, you had to rebuild everything that depends on the module you changed.
Nx launched its first monorepo back in 2017 with the explicit goal of speeding up those build times and brining monorepos to the masses. Here’s how they did it:
Their “computation cache” uses techniques like remote caching and local caching to make sure you never rebuild the same code twice.
A special affected
command makes a graph of your code, figures out where the changes have been made, and only builds the parts of the app/package that are affected by the current PR.
The Distributed Task Execution runs all necessary tasks in parallel to make sure they’re executed as fast as possible and in the correct order.
Where do they go from here? Like most VC-backed OSS projects, the name of the game for Nx is to, “get as many companies as possible to use our OSS, and hopefully upsell them on premium enterprise features later.” And so far, it seems to be going well.
Their npm downloads grew 5x this year to 3m per week, and by taking over active management of Lerna, they now control 75% of all JS monorepo tooling. At this point, Turborepo (recently acquired by Vercel) is the only thing standing in their way of a full monorepo-opoly.
Bottom line: The next step for Nx is to continue talking its book, and hope that they can get monorepos to catch on with developers faster than mono caught on in my freshman dorm.
That's one powerful back (end)
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.
Senior or Staff Front-end Engineer | |||
| |||
Close.com is looking for an experienced React developer to help design and implement major user-facing features. Close is a 100% globally distributed team of 65 happy people, dedicated to building a product our customers love. |
Senior Full Stack Engineer | ||||
| ||||
Motion is looking for experienced TypeScript engineers to build the next generation of productivity tools. You'll inform key frontend architectural decisions that help Motion scale the next 2 order of magnitudes, create delightful user-facing features, and improve application performance. We are an ambitious team of 15 people distributed remotely across North America. |
Experienced TypeScript + React Developer | ||||
| ||||
Beatgrid is looking for an experienced TS + React developer to help build next-gen web apps. We are a Netherlands-based technology scale-up growing exponentially each year. Join the adventure and grow with us! |
Software Engineer - Frontend | ||||
| ||||
As a frontend engineer at Phantom, you’ll help create delightful user experiences, contribute to cross-platform client infrastructure, and craft web3 developer SDKs for Phantom's crypto wallet that's used by millions. |
Want to level up your skills with the most powerful state management library out there? Check out the official course.
import db from "./db";
import admin from "firebase-admin";
import { getUserClaimsFromCookie } from "./claims";
const { arrayUnion, arrayRemove } = admin.firestore.FieldValue
export default async function updateLessonComplete(req, res) {
try {
const { lessonId, completed } = req.body
if (!lessonId) {
throw new Error("lessonId is a required parameter");
}
if (!completed) {
throw new Error("completed is a required parameter");
}
const claims = await getUserClaimsFromCookie(req, res);
if (!claims) {
throw new Error('user must be logged in')
}
await db.collection('lessons').doc(lessonId)
.update({
completed: completed === true
? arrayUnion(claims.uid)
: arrayRemove(claims.uid)
})
res.send({ status: "success" });
} catch (e) {
res.status(400);
res.send({ error: { message: e.message } });
}
}
Sanna Jammeh created TW Classed, which combines CSS-in-JS with Tailwind CSS in a type-safe way. It’s the biggest mashup we’ve seen since Tim McGraw and Nelly.
The Datadog team created this Synthetic Monitoring One-Pager to help you launch new features in production without worrying that you’re going to break literally everything. It’s a great read. [sponsored]
Moritz Klack wrote about How we lost our slick new npm package name (and then got it back) for their popular library, React Flow. It’s a lot like the movie How Stella Got Her Groove Back, but with slightly less Whoopi Goldberg.
Gabriele Svelto wrote on the Mozilla blog about Improving Firefox stability with this one weird trick. I also was able to improve my own stability with this one weird trick called, “stop eating like a 9 year old trapped inside a 7-11 and maybe get less than 19 hours of screen time per day.” Surprisingly effective.
Bay.js is an “easy to use, lightweight library for Web Components” that uses standard JavaScript string literal syntax for binding data to HTML. All together now: Down by the bay, where the web components grow, back to my code, I dare not go…
Haseeb Anwar made Create React Package so you can create React packages without having to worry about configuring build tools like Rollup, Babel, or ESLint (because it pre-configures and hides them for you). Now there’s nothing stopping you from becoming a famous OSS author and making hundreds of dollars a month by selling backlinks to online casinos and weed sites.
David ”🐐” East and Eduardo San Martin Morote wrote on the Firebase Blog about Building a better Vue and Firebase experience, including support for Nuxt.
Stable Diffusion just released v2 of its open source, text-to-image AI engine. This is just one more example of how much easier kids have it today — they can just tell the AI to forge a note with their mom’s signature to excuse an absence from school whenever they want to skip class. Back in my day, I had to painstakingly practice her handwriting and perfect the art of signature fraud like a medieval calligraphist.
import db from "./db";
import admin from "firebase-admin";
import { getUserClaimsFromCookie } from "./claims";
const { arrayUnion, arrayRemove } = admin.firestore.FieldValue
export default async function updateLessonComplete(req, res) {
try {
const { lessonId, completed } = req.body
if (!lessonId) {
throw new Error("lessonId is a required parameter");
}
if (!completed) {
throw new Error("completed is a required parameter");
}
const claims = await getUserClaimsFromCookie(req, res);
if (!claims) {
throw new Error('user must be logged in')
}
await db.collection('lessons').doc(lessonId)
.update({
completed: completed === true
? arrayUnion(claims.uid)
: arrayRemove(claims.uid)
})
res.send({ status: "success" });
} catch (e) {
res.status(400);
res.send({ error: { message: e.message } });
}
}
There’s a lot of misdirection here. The bug happens with our if (!completed)
check. completed
is a boolean, meaning when completed
is false
(making it falsy), our code will throw an error. Instead, we can check if completed
is of type boolean
, not if it’s falsy.
if (typeof completed !== 'boolean') {
throw new Error("completed is a required parameter");
}
and before you say it, yes, I know TypeScript prevents this 🙂.