This week, we’ve got a new way to feel something test your code, some bundler fan fiction, and the hardest diss of the 18th Century.
Welcome to #102.
My first (and last) testing conference
Version 3.0.0 of fast-check
, a “property-based testing” library for JavaScript, came out last week. If you’re not familiar, be thankful you’re hearing about it from us and not the obnoxious guy (yes guy) on your team who spends more time arguing about functional programming than getting work done.
What’s Property-Based Testing? Most developers write tests like they do estimates: give the best case scenario and hope it all works out. These lies hardcoded inputs (🥸) are called “example based tests”.
With property-based tests, your test is run hundreds of times with different autogenerated inputs. It’s kind of like having an automated QA person that fact checks your code. fast-check
is the most popular library for doing this with JavaScript (and TypeScript).
How does it work? fast-check
provides a bunch of utilities for defining the “properties” of your code (things you assume will always be true). It uses them to generate random inputs to test against your code. When a test fails, fast-check
helps narrow down the (💩 ton of) failed cases to a single reproducible case that you can “replay” to verify the fix.
Ngl, it’s a lot to take in but they provide a handy set of tips and interactive examples to help you get get familiar.
Property based testing might be for you if you:
Bottom Line: If you want to stop wasting your time with terrible tests, you can A) delete them all (galaxy brain) or B) go all in and use property based testing. We’re big fans of A), but if math is your kink, fast-check
might be worth your time.
Don’t be like Elmo. [sponsored]
Tracking “developer productivity” is stupid.
Two reasons:
That’s why Sleuth is the best. It doesn’t track developer productivity — it only measures team output. And it actually works.
That’s because it captures your team’s DORA metrics — which multiple studies have shown are the only stats that actually matter. No worthless vanity metrics and no individual stats.
Sleuth automatically collects all that data for you (including deploys), unlike most other services that only collect git or issue tracker data. So you can trust that the numbers are legit.
It’s also worth noting that Atlassian uses Sleuth. That’s like being Chef Gordon Ramsey’s favorite restaurant — if it works for them, it’ll probably help you too.
Try it free and help your team reach its goals faster.
Y’all still looking for a bundler?
Back in 2018, Airbnb did what lots of us have done when thinking about upgrading to a new version of Webpack — give up.
Unfortunately for them, this was before The Great Build Tool Renaissance™️ of the last couple years — esbuild and Rollup weren’t around yet, Parcel wasn’t as robust as it is today, and Rome was still just a European city with lots of roads.
So they turned to Metro, Facebook’s open-source JavaScript bundler for React Native.
This was a surprising choice for Airbnb’s web app, and it required them to work directly with the Metro team to create their own custom flavor of Metro (yum) that could handle bundling all of Airbnb’s web properties. The 4-year odyssey finally ended last week with an interesting retro outlining the entire process.
So let’s dive a little deeper into what makes Metro unique, why Airbnb decided to use it, and how it stacks up to some of the newer bundlers we have today.
There are two defining Metro features:
For most projects, implementing these optimizations would probably feel like extreme hair-splitting (my favorite Winter X-Games event). But for a huge app like Airbnb with 49k+ JS modules, it produced great results, including 80% faster TTI (Time to Interactive) and 55% faster compiling of the slowest prod build. 👏
Should you try this at home? Probably not, tbh. In The Year of Our Build 2022, we have better options for powerful, flexible bundlers that don’t require years of customization (esbuild is probably the gold standard for most projects).
But you could always just wait around for a couple years until we inevitably get something even faster.
Flightcontrol recently completed YCombinator and raised $3.3m. They’re a fully remote team that’s focused on solving the huge gap between Heroku and AWS, and they’re led by Brandon Bayer, the creator of Blitz.js.
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.
Yeti Labs is a human-centered frontend studio designing and building web apps for DeFi protocols. We love UI animations, innovative UXs, best practices, reusing our code, improving our workflow and learning new things. Come join our crew as we solve interesting challenges while having fun.
CarbonQA provides QA services geared for dev teams. Let us lift your dev team’s morale by breaking their code. We work in your tools, talk with your team in Slack, and let devs be devs. Leave the testing to us.
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 } });
}
}
Curious about what’s happening in Java-land? New Relic’s got you covered with their 2022 State of the Java Ecosystem Report. [sponsored]
The new React docs got some performance upgrades after Evan You and Dan Abramov went head to head in a battle of the subtweets. I’m sad that neither of them found a way to use the hardest diss of the 18th Century, but there’s always next time.
Speaking of Twitter beefs between framework authors, Next.js just shared their new Layouts RFC — which is basically just Remix (Taylor’s Guillermo’s Version). Here’s a quick TLDR we made to catch you up to speed.
Austin Gil wrote an in-depth article about Why Edge Compute is kind of like knitting dog hats. Personally, I think it’s a lot more like crocheting, but that’s just one person’s opinion.
Tailwind 1.6 was just released with a new Tailwind language mode for VS Code and some other cool features to help you feel good at design.
Danny Hermes wrote a great article about What npm Can Learn from Go and that has us thinking, why is “Can” capitalized but not “From”?
Digital Ocean just announced DO Functions and officially entered the serverless computing battle royale.
Ken Kantzer wrote up his learnings from 5 years of doing tech startup code audits. Specializing in “code audits for startups” sounds about as much fun as specializing in “behavior management for middle schoolers”, but I’m glad there are good people out there doing both.
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 🙂.