Sam Thorogood

https://samthor.au/

Hello there. I'm Sam.

フィード

記事のアイキャッチ画像
Kuto, a reverse JS bundler
Sam Thorogood
Kuto, a reverse JS bundlerKuto is a novel approach to shipping code on the web.It lets you re-use code a client already has for shipping updates.For a 'real-world' site with ~3mb of JS, updating the React dependency resulted in:71% smaller download28% faster start time (on a ~5yo old phone, a Pixel 3).…vs a single bundle, or any case where all the code is invalidated.Note that Kuto works really well on the final ESM bundles of real sites or apps, but probably not libraries themselves, even though Kuto's output will be valid.Kuto also works as a predictable 'chunk' generator for large bundles.If this is interesting to you—do you have too much JavaScript?—then do the thing, and do a Kuto on your code.(Is Kuto a verb?Who knows.I'm trying it out.) ✂️How does it work?Instead of focusing on minifying output or anything idempotent, Kuto takes a different route.On the first build:Kuto splits source JS into a 'main' part, and a normally larger 'corpus' of code which has no side effects.This cor
2ヶ月前
記事のアイキャッチ画像
CJS Equivalency
Sam Thorogood
CJS EquivalencyI've been working on bundler-like things and I've thought again about the classic CJS vs ESM interop issue.It occurs to me that one way to transform CJS to ESM is to treat each CJS file as a 'builder'.This isn't bundling, but perhaps a step on the way, or for a serving library which purely provides a transform.ExampleHere's a reductive example.We have a CJS file which has both a top-level and lazy import:const other = require("./other.js");module.exports.performAction = () => { const lazy = require("lazy"); return other.helper(lazy);};Can be thought of as, with the original code largely unchanged in internalBuild:import otherBuild from "./other.js";import lazyBuild from "./node_modules/lazy/index.js";const builders = { other: otherBuild, lazy: lazyBuild,};const require = (name) => builders[name]();function internalBuild(module) { const other = require("other"); module.exports.performAction = () => { const lazy = require("lazy"); return other.helper(lazy); };}let cache;ex
3ヶ月前
記事のアイキャッチ画像
December Tech Vibe Check
Sam Thorogood
December Tech Vibe CheckIt's the end of 2023.This isn't a "year in review", just some musings about what's been going on recently.Read on to find out more about:What I'm doing in the CloudWhat open-source code I'm publishingSome business plans and musings.Cloud Tech StackThere's been some cool updates in the way I build projects.Cloud RunI bit the bullet and moved this blog over to Cloud Run with all the bells and whistles.This is fine, but I end up paying for a myriad of various services just for what is a relatively simple site.I pay for:the Cloud Run itselfa LB in front of itthe V4 addressthe V6 addressthe http -> https redirector for both v4 and v6the CDN…yesh.It actually honestly works really well, but it just feels like a lot for a tiny blog. 🤯Also, it connects directly to my GitHub repository and builds whenever I push.This is 👌: I previously had the incredibly awkward credential delegation stuff set up so GitHub could deploy on my behalf.(I do need to also trigger regular bui...
5ヶ月前
記事のアイキャッチ画像
Baldur's Gate 3 Character Choice
Sam Thorogood
Baldur's Gate 3 Character ChoiceMy partner and I have been playing co-op Baldur's Gate 3 on PS5.It's good, although a bit clunky on controller, and while the split-screen mode is amazing that it works at all, it's also the source of pretty hefty FPS drops.When cross-play is added, one of us will probably swap to a Mac to keep playing.How Your Character Choice WorksWe found character creation confusing, especially as you're sort of dumped into it… what does it mean to pick an "origin character"—a premade character with backstory—versus a character you build yourself.If you choose an origin character, you play "as" them— they are your 'avatar'.This sounds reductive, but the differences are simple:There are absolutely no voiced lines from your character: you imagine yourself saying things as you choose dialogue optionsYou get additional backstory in the form of extra choices, cutscenes during rest, etc.(This choice is especially confusing because the character picker has a small cutscene
8ヶ月前
記事のアイキャッチ画像
Opinions On Sydney Rail Transport
Sam Thorogood
Opinions On Sydney Rail TransportThis is a post containing my niche opinions on Sydney's public transport system.If you're from Sydney and a transport nerd, this is probably interesting.Otherwise, maybe read something else.I'm not going to give background, suffice to say that Sydney is Australia's biggest city and has the highest number of commuters who use public transport.Without further ado, some spicy takes. 🌶️TakesIsolate the Eastern Suburbs lineSydney has an interesting history in that all its rails are standard gauge, making the entire train network actually useful for interstate travel and freight. (This is unlike every other city with trains in Australia, which all have a largely isolated narrow or wide gauge network.)However, parts of the network are useless for this because they're so isolated.Proposal: The Eastern Suburbs line from Redfern to Central should be "cut off" from the rest of the network.It makes no sense that I can get on a train at 9:13 from Bondi Junction and...
1年前
記事のアイキャッチ画像
AWS Amplify Is A Grift
Sam Thorogood
AWS Amplify Is A GriftYes, this is a punchy headline, but if you'll join me on this journey, you'll see how. 👀So, here's the context.For the past year or so, I was at a renewable energy startup.It was a great experience, but I recently resigned: I'm having another child, and I just don't need to work full-time—so I'm not going to.To be clear, this post is entirely my opinion: as of writing, I'm only employed by myself.The startup heavily used Amplify—I inherited that decision—but one of my major initatives was removing as much of it as possible.This took hundreds of engineering hours away from actual work.So, let me be as clear as possible, and you can quote me personally on this: "AWS Amplify is actively harmful", primarily because of its database choice.Consider using just a normal database—personally for me that would be Firestore (Google-hosted) or MongoDB (basically self-hotsed), because I strongly prefer scalable NoSQL databases.But for most people, it's probably going to litera...
1年前
記事のアイキャッチ画像
AbortController for expiry
Sam Thorogood
AbortController for expiryMy last post on AbortController continues to be my most read blog post, ever.This one probably won't do quite as well, but that's fine—it's a bit more niche, but it's definitely also an extension of that one.Today, the problem I'll solve is:you have a token, key, or some object (e.g., some key to access a system)it's valid only for some time (e.g., an hour) - it has a lifetimeyou want to ensure users of that object don't overstay their welcome. 💥BackgroundThe idea of AbortController is to generate an AbortSignal you can pass in to some object or system to later shut it down.For example, you can have one signal for an entire request, but shut down all its dependent work with one single call to .abort().Create an AbortController, do some stuff, get outThis background might feel a bit needless, but it actually reminds you that the AbortController has a very close relationship to the idea of "context" as it pertains to RPCs, network requests and so on: whenever y...
1年前
記事のアイキャッチ画像
Practical Python Modules
Sam Thorogood
Practical Python ModulesI grew up writing a lot of Python, which I guess speaks to how old and unorganized it is.And since then, I've been primarily a JS developer—that ecosystem has its failings, but the rules around how things are imported inside "node_modules" are mostly well established, even despite its quirks.For modules, though—Python is a mess.But I think there's some simple knowledge that you might be missing. 🤔So, You're Building A PackageJust some package that has some behavior or functionality.It does whatever.It might have some dependencies.But you want to structure it in a sensible way. 💡You Want Module Mode!Unless you're writing a single script file, I assert that you almost always want to run your code as a module.# where "path/to/module.py" existspython -m path.to.moduleIf "path/to/module" is a folder, Python will look for "__main__.py" within that folder.If it's a file, it'll run it directly.In both cases, __name__ == "__main__": you can still use that old trick to ...
2年前
記事のアイキャッチ画像
Focus Management in 2022 📺
Sam Thorogood
Focus Management in 2022 📺I recently gave a talk!It's on <dialog> and the inert attribute, with a mention of <fieldset disabled>, too.You can find the talk on JSNation, who graciously hosted me and motivated me to get this done. 🎉But also, it's just on YouTube here (albeit without my face and a snazzy intro/outro):This talk combines one of my quite popular blogposts—In Defence Of Dialog, including some great demos—with a bunch more new content about the other focus primitives you have available to you.Inert Release / Fieldset DisabledEverything I mentioned in my talk can work today, except…As of July 2022, Firefox still hasn't shipped the inert attribute.This is something that I called out in my talk.It's completely implemented, but for some reason, Firefox won't throw it over the wall.This is amazing considering even Safari has come to the table.I'll update this section when or if they do.But if you want some basic workarounds, consider <fieldset disabled> for forms.If you have a HT...
2年前
記事のアイキャッチ画像
Event-Driven JavaScript Development
Sam Thorogood
Event-Driven JavaScript DevelopmentEvery JS developer has written code to handle "click" listeners—press a button, tap a link—and you'll do something interesting.Events are core to the way we write for the web and use the DOM. 👆But what if I were to tell you that you should and could be using event-driven development for yourself—not even in the DOM? 🤔To do that, we can now subclass EventTarget, giving your code access to fire its own events, and listen to those events using .addEventListener.This lets you properly encapsulate behavior—your abstraction can do some work, and other parts of your code can simply listen to when it's done.Here's a simplified version of the demo's code:class SomethingController extends EventTarget { async doSomething() { await someLongTask(); this.dispatchEvent(new Event('amazing')); }}// now:const c = new SomethingController();c.addEventListener('amazing', () => ...);Great! 🎉Of course, I could have created the cute effect any way I liked—I didn't need ev...
2年前
記事のアイキャッチ画像
Cross-Tab Title Hints
Sam Thorogood
Cross-Tab Title HintsLet's say you've built a website that shows items—maybe things you can buy online, or a catalogue of TV episodes.Your users are opening lots of tabs at once 📈 to compare and contrast, or decide what to buy, or whatever.Turns out, we can use just the hidden tabs as a surface to help the user out.But note that this is really a desktop-only feature, because tabs aren't really visible on mobile. 📱So first!Play with the demo:And check out the video if you'd like to hear about it in <2 minutes:The very short version of this is: if your users are trying to compare and contrast items, you can give your users visual cues by taking over hidden tabs' titles (and favicons, but that's an exercise for the reader) and showing them relevant information–without having to switch tabs.To put it differently:I know how to be cool with the kids and meme it upBuild ItLet's talk about what makes this work and how to build it!Broadcast ChannelFirst, let's learn about the humble Broadcast...
2年前
記事のアイキャッチ画像
AbortController is your friend
Sam Thorogood
AbortController is your friendAlso available in: [한국어 번역]One of my favorite new features of JS is the humble AbortController, and its AbortSignal.It enables some new development patterns, which I'll cover below, but first: the canonical demo.It's to use AbortController to provide a fetch() you can abort early:And here's a simplified version of the demo's code:fetchButton.onclick = async () => { const controller = new AbortController(); // wire up abort button abortButton.onclick = () => controller.abort(); try { const r = await fetch('/json', { signal: controller.signal }); const json = await r.json(); // TODO: do something 🤷 } catch (e) { const isUserAbort = (e.name === 'AbortError'); // TODO: show err 🧨 // this will be a DOMException named AbortError if aborted 🎉 }};This example is important because it shows something that wasn't possible before AbortController came along: that is, aggressively cancelling a network request.The browser will stop fetching early, potentially saving t...
2年前
記事のアイキャッチ画像
Unit Testing React without Jest
Sam Thorogood
Unit Testing React without JestJest is clearly a polished testing tool, but with the advent of Node.js 18, we really don't need it any more.Jest also has a huge surface area, is perhaps inexorably linked to Webpack, and needs its own binary—you can't just "run the tests" as a script.So it has some modern challenges.This blog post will teach you how to set up your code, rather than provide an all-in-one solution, and is not about React testing itself—just the environment where you can do it.We'll still be using the @testing-library/react dependency, and esbuild to do our build.As it's just a set up guide, you might still want to write a small wrapper or use a library.But by the time you've finished reading, you should have a better sense of what's going on—unit testing isn't magic ✨, but it is important.The StepsLet's get right into it. 🤠0. Build with esbuildThis guide assumes you're building with esbuild, but you could swap this out for another build tool.You should be able to build y...
2年前
記事のアイキャッチ画像
Three Fun Facts on Australian Federal Elections
Sam Thorogood
Three Fun Facts on Australian Federal ElectionsSome fun facts on voting in Australia, one of the world's few democracies to enforce (lightly) compulsary voting.(That isn't a fun fact—it's well-known and not really that interesting.)The Facts1. We're a federation or federalist stateThe federal government exists at the behest of the states, not the other way around.It's conceivable but highly improbable that a state could secede: if all states seceeded, then the federation would arguably cease to exist.This seems incredibly unlikely and I'm quite sure that I've oversimplified this possibility. 🤔2. How to vote cards used to serve a purposeOn your way to vote in-person, you'll be hounded by what feels like hundreds of overeager volunteers who want to present you with a guide on how to vote for their candidate.Some large number of people (anywhere between 10-50%) vote in a way that matches the guide: yes, put your preferred party first, and number the rest of the boxes as suggested.But bal...
2年前
記事のアイキャッチ画像
Builtin Node.js Testing
Sam Thorogood
Builtin Node.js TestingAs of Node.js 18, which became stable in April 2022, you can test your backend code without installing any dependencies.This works because of the new node:test package, along with the existing node:assert package—which is from the very first releases of Node.Here's the tl;dr.First, write a script like this, naming it e.g., "test.js":import test from 'node:test';import assert from 'node:assert';test('check something' () => { // check that 1+1 equals 2 assert.equal(1 + 1, 2);});And then run "node test.js".It'll spit out some information on your tests: hooray! 🎊If your tests have failed, Node will let you know in the output—and the status code will be non-zero, which'll count as a failure for other tools like automated test runners.The Test FlagWhile it's cute to run a single file with Node and have it spit out its results, most of the time, you'll want to use the "node --test" flag.When used without any further arguments, this will automatically find JS files insi...
2年前
記事のアイキャッチ画像
(P)react vs Web Components: a Xoogler's perspective
Sam Thorogood
(P)react vs Web Components: a Xoogler's perspectiveI left Google at the end of 2021, as part of a bit of a great resignation from my team—Surma, Rob and a bunch of others left too.Not for any common reason.One common thing I think all of us Xooglers are hitting is having to learn about JSX and functional components, which of course are technologies that—as a Googler—I was basically told had already won the web development race.So I'm going to talk through some of the interesting things I've learned in swapping over.In this post, I'll refer to Web Components as Elements, but (P)react's provided components as Components.BackgroundThis section is short.If you want to hear more about Web Components, you can see my talk from JSNation.Needless to say, Web Components describes Custom Elements (CEs) and Shadow DOM (SD):CEs are about loading code—if your browser sees <my-foo> element, it can be told to load your codeSD is about wrapping scoped CSS and HTML inside another element—a good example
2年前
記事のアイキャッチ画像
Major Node Changes
Sam Thorogood
Major Node ChangesThis document is a short list of changes in Node.js' major releases.It does not cover point releases, just e.g., v13, v14 and so on.v22 (2024)v8 12.4Including Array.fromAsync, additional Set methodsExperimental support for require() on ESMNode itself can --run <script> from "package.json"Native WebSocketGlob functions inside node:fsv21 (2023)Experimental support (behind a flag) fornative WebSocketdefault ESM supportAdded global navigator objectv8 11.8, including array groupByv20 (2023)V8 11.3, including change array by-copy methods such as toSortedExperimental permission model (e.g., restrict FS access)Marks the test runner as stablev19 (2022)Adds node --watchV8 10.7v18 (2022, LTS until 2025-04-30)Adds built-in Fetch API (fetch)Includes Headers, FormData, Blob and so onAdds Web Streams API including ReadableStream and friendsAdds BroadcastChannelAdds experimental test runnerV8 10.1Includes Array.findLast() and Array.findLastIndex()Built-in test runnerv17 (2021)V8 9.5v
2年前
記事のアイキャッチ画像
Foot Pedal For Mute
Sam Thorogood
Foot Pedal For MuteI use a novel control scheme to mute and unmute my microphone during meetings—a foot pedal.The benefits for meetings, I feel are obvious, but you know, for SEO let's list them anyway:you can keep typing while you meetyou can keep gesticulating wildly and still interject easilyyou don't need to find the browser tab that has the meeting in it in order to contributeyou can idly play with it and think you're a rockstar 🧑‍🎤I use Google Meet for my meetings, but this guide probably works for other providers, too.Read on! 👇What You NeedI bought this footpedal (Amazon) and recommend it for its simplicity.You can also find it on Newegg, or a bunch of other places, and like all random USB devices, it is sold under a variety of brand names.(Please @-me on Twitter if you find it somewhere really cheap.)This one! It might have a different brand name for you.At the same time, here's a style I tried out which you should not get.The low profile style here means that pressing it t...
2年前
記事のアイキャッチ画像
Code golf & the art of CSV parsing
Sam Thorogood
Code golf & the art of CSV parsingSo, I recently released but-csv, an absolutely micro package to parse and generate CSVs, at (as of writing), 484 bytes.Yet, it started at almost double that—864 bytes—just a few days ago.This post is about a kind of code golf in JavaScript—for real shippable software, while not trading off performance.I'll show some tips and tricks that the tools can't do for you.But first!A graph of progress from a naïve starting point, over a weekend, of making this code small.(The blue line is after being passed through esbuild --minify, and the red is additionally compressed with gzip.)The size got pretty smallThe gzip'ed version of the starting point actually ended up being larger than the final version's actual source.The compressed version tracks the original, but its line isn't as steep—at the start, gzip saved about 40%, but it reduced to 20% by the end.(gzip is interesting but not really indicative of how a whole project will compress, since a real project wo
2年前
記事のアイキャッチ画像
Thoughts on Google and what's next
Sam Thorogood
Thoughts on Google and what's nextSo, I announced in December that I'm leaving Google.And it seems as if the topic "people leaving Google" really drives engagement, so here I am to follow up.I was at Google for more than ⅓ of my life—I started in 2009, when I was 22.I grew up there!I was always an employee of Google Australia based in Sydney, but I got to travel the world, including a few couple-month stints in London and San Fransisco. 🎡🌁In that huge window of my life, I got married, had an adorable child, bought and paid off a house (thanks, $GOOG), and so on.And, I confess, my personal identity throughout felt tied to Google—it's still hard to decouple!My partner and many friends still work there, I have a cupboard full of Google t-shirts, and so on. 👕What are you doing next?I'll get to the good stuff first. 🥇I'm joining a tiny startup, here in Australia, called Gridcognition, as their CTO.(Yes, here's the obligatory we're hiring hint, but focusing in Australia/NZ for now.)We're...
2年前