RIP Records & Tuples

Issue #385.April 18, 2025.2 Minute read.
Bytes

Today’s issue: Dan found the password to his blog, we go deep on structural equality, and Git turns 20.

Welcome to #385.


Eyeballs logo

The Main Thing

A lego man mourning another dead lego man

You were the chosen one

RIP Records & Tuples

The Bible clearly teaches us that TC39 giveth, and TC39 taketh away.

And the SpecLords tested our faith again this week when they killed the Records and Tuples proposal, which had promised to bring deep immutability and structural equality to complex data in JavaScript.

Now in their place stands a new Stage-1 proposal called Composites, which tries to solve similar problems with a very different approach.

Let’s take a closer look:

  • Composites are frozen, not deeply immutable – Composites can contain any value, so immutability is based on the contents. Since R&T could only contain primitives and other R&T, they had forced deep immutability by design.

  • Containment is unrestricted – Unlike R&T, Composites can hold objects, symbols, functions, and whatever else you throw at them.

  • Structural equality via Composite.equal(a, b) – This does a recursive deep comparison using SameValueZero, but you must call it manually because there’s no special behavior with ===.

  • No new syntax or typeof values – Composites are standard objects, so they integrate with your existing code and APIs (Map, Set, etc.) without language-level changes

  • Ergonomics take a hit – You lose the elegance of native === structural equality and literal syntax like #{}. Everything is more verbose and opt-in.

Bottom Line: Records & Tuples were clearly a more elegant solution, but Composites are more flexible. And as Scott Tolinski taught me at breakdance camp last summer, flexibility usually beats elegance.


Meticulous logo

Our Friends
(With Benefits)

A raccoon with a magic wand

Me after using AI to do the absolute worst part of my job

Meticulous AI gets your app full test coverage – without you writing any tests

Dropbox, Lattice, and hundreds of other engineering orgs all rely on Meticulous because it generates and maintains an exhaustive suite of E2E UI tests with zero developer effort.

That means you’ll never write, fix, or maintain a test ever again. Here’s how it works:

  • Add a script tag to your local development, staging, and preview URL environments, so Meticulous can monitor your daily interactions with your app

  • Their AI engine uses that info to generate a continuously evolving test suite that adds or removes tests as needed and eliminates flakes

  • Tests are heavily parallelized across a compute cluster, so you can test thousands of screens and get results in under 120 seconds.

Check it out – and see why one engineering leader at Dropbox said that “once we started using Meticulous, we couldn’t imagine working without it.”


Spot the Bug logo

Spot the Bug

Sponsored by Datadog

Their Synthetic Monitoring Brief shows you how to efficiently create browser and API tests that are intelligent and self-maintaining.

const people = [
  { firstName: "Aaron", lastName: "Smith" },
  { firstName: "Émile", lastName: "Zola" },
  { firstName: "Charlotte", lastName: "Brown" },
  { firstName: "Beyoncé", lastName: "Knowles" },
  { firstName: "Ólafur", lastName: "Arnalds" },
  { firstName: "David", lastName: "Jones" },
  { firstName: "Zoë", lastName: "Deschanel" },
];

function sortAlphabetically(arr) {
  return arr.sort((a, b) => {
    if (a.firstName < b.firstName) {
      return -1;
    }

    if (a.firstName > b.firstName) {
      return 1;
    }

    return 0;
  });
}

sortAlphabetically(people);

Cool Bits logo

Cool Bits

  1. Thorsten Ball wrote about how it’s not that hard to build a fully functioning, code-editing agent.

  2. Atriiy wrote about how Rolldown works.

  3. How to integrate Clerk with your Lovable application shows how you can use just a few prompts and config steps to add custom domains, streamlined auth flows, and waitlist-powered onboarding to your app. [sponsored]

  4. Mark Dalgleish wrote about React Router’s new API for faster lazy loading.

  5. Dan Abramov wrote about JSX over the Wire and shot us all back to 2019 by releasing two blog posts in one month.

  6. Isaac Maw from Apryse wrote about 4 document processing trends they saw in 2024 and what they predict will change the most in 2025. [sponsored]

  7. Git is turning 20 years old this month, and Linus Torvalds says that “it was never a big thing for me”. Classic Liney-T.

  8. Astro 5.7 now supports local SVG files as components.

  9. bit.cloud lets you use AI to build new full-stack features as reusable components that can be installed in your team’s existing applications. Bit’s HopeAI generates high quality code, based on your dev standards and tech stack. [sponsored]

  10. Fotis wrote about the new Cookie Store API that can finally help you put those Girl Scouts in their place.

  11. The Zod 4 beta is “faster, slimmer, and more tsc-efficient”, which is exactly how I felt after my 6th injection of Wegovy.

  12. Ondrej Velisek wrote about avoiding the State Synchronization trap.


Spot the Bug logo

Spot the Bug: Solution

Sponsored by Datadog

const people = [
  { firstName: "Aaron", lastName: "Smith" },
  { firstName: "Émile", lastName: "Zola" },
  { firstName: "Charlotte", lastName: "Brown" },
  { firstName: "Beyoncé", lastName: "Knowles" },
  { firstName: "Ólafur", lastName: "Arnalds" },
  { firstName: "David", lastName: "Jones" },
  { firstName: "Zoë", lastName: "Deschanel" },
];

function sortAlphabetically(arr) {
  return arr.sort((a, b) => {
    if (a.firstName < b.firstName) {
      return -1;
    }

    if (a.firstName > b.firstName) {
      return 1;
    }

    return 0;
  });
}

sortAlphabetically(people);

By default, string comparison in JavaScript is not language-sensitive (meaning it doesn’t take into account language-specific rules or special characters like accents), which results in the sorted list not being in the correct order.

The solution is to leverage Intl.Collator which enables language-sensitive string comparison.

function sortAlphabetically(arr) {
  const collator = new Intl.Collator("en", { sensitivity: "base" });
  return arr.sort((a, b) => collator.compare(a.firstName, b.firstName));
}