justin․searls․co

Gurman with the scoop, summarized by MacRumors:

An updated version of the Mac Studio could launch as soon as this week, reports Bloomberg's Mark Gurman. The new machine is expected to be equipped with the M4 Max chip that we first saw in the 2024 MacBook Pro models, but Apple apparently does not have an M4 Ultra chip ready to go. Instead, there could be a version of the ‌Mac Studio‌ that uses an M3 Ultra chip. Apple didn't release an M3 Ultra chip alongside the M3 chip lineup, so it would be a new chip even though it's not part of the current M4 family. The current ‌Mac Studio‌ has an M2 Ultra chip, as does the Mac Pro.

Releasing a previous-generation, higher-end chip is utterly routine from every other manufacturer, but Apple doesn't sell chips, it sells computers.

Offering a Mac Studio in M4 Max and M3 Ultra configurations would give Apple’s marketing team a really fucking narrow needle to thread. One imagines the Ultra will be better for massive video exports and the Max will be better for literally every other workflow. Woof.

Breaking Change artwork

v32 - Large Lemon Model

Breaking Change

Turns out I'd accidentally been letting some of your e-mail get filtered as junk mail. I want to be clear that I don't think you're junk, Apple does. Write in with your feelings to podcast@searls.co and I'll read it, assuming I get it.

Video of this edition of the show is up on YouTube.

Without further ado, links!

Show those show notes…

When I drive a Tesla with Full-Self Driving enabled it feels just like riding a bike with no hands—I'm simultaneously amazed by what is happening but so nervous it'll kill me that I'm ready to take over in an instant.

I've found real value in Open AI's "Deep Research" mode, so today I added the 10th(!) multimedia type to my website (called "Slops") to embed AI-generated content, including my prompt, stylized citations, and metadata indicating which content is AI-generated justin.searls.co/slops/

What to do when you can't sleep in hot, stale air?

Prompt:

I have a real struggle that is hugely impacting my quality of life: whenever I'm in a room with less than stellar airflow, higher than ideal temperature, or a bed that traps heat, I will often wake up within a couple hours of going to sleep in a panic that I'm not getting enough to breathe.

I've been tested for apnea and it appears to not be that. I have an Eight Sleep at home and that's also very helpful. When I'm traveling, I try to bring a USB-powered fan or borrow one from a hotel, but neither are fool-proof.

What is going on here and what can I do?

Here's what the computer said:
(Generated by OpenAI o3-mini-high)

Waking up suddenly feeling you “can’t get enough air” can be scary. This nighttime air hunger often results from a mix of physiological factors (like how your body senses CO₂ or airway resistance), environmental conditions (stuffy room, temperature, humidity), and psychological triggers (anxiety or learned associations). Below, we’ll explore possible causes and practical strategies to manage and prevent these episodes, both at home and while traveling.

Physiological Factors Behind Nighttime Air Hunger

  • CO₂ Sensitivity (False Suffocation Alarm): Some people have an over-sensitive response to rising CO₂ levels in the blood. Even a mild buildup of CO₂ (for example, in a poorly ventilated room) can trigger the brain’s “suffocation alarm,” causing you to wake up gasping. Research on panic disorder shows that patients with prominent respiratory symptoms are more sensitive to CO₂ and prone to nocturnal panic attacks​ scielo.br. In other words, your body might overreact to normal fluctuations in CO₂ at night, jolting you awake with a feeling of dread or need for air. One theory (Klein’s false suffocation alarm) suggests this sensitivity leads to a cycle of over-breathing and breath-holding that triggers panic during sleep​ scielo.br.

And then what happened?…

The thing I can't stand about Vegas is that even when I eat right, exercise, and go to bed early, I still feel like absolute garbage by the end.

If my body's gonna feel this wrecked, I should at least get to enjoy way harder drugs and kinkier shit than I actually get up to.

Calling private methods without losing sleep at night

Today, I'm going to show you a simple way to commit crimes with a clean conscience.

First, two things I strive for when writing software:

  1. Making my code do the right thing, even when it requires doing the wrong thing
  2. Finding out that my shit is broken before all my sins are exposed in production

Today, I was working on a custom wrapper of Rails' built-in static file server, and deemed that it'd be wiser to rely on its internal logic for mapping URL paths (e.g. /index.html) to file paths (e.g. ~/app/public/index.html) than to reinvent that wheel myself.

The only problem? The method I need, ActionDispatch::FileHandler#find_file is private, meaning that I really "shouldn't" be calling it. But also, it's a free country, so whatever. I wrote this and it worked:

filepath, _ = @file_handler.send(:find_file,
  request.path_info, accept_encoding: request.accept_encoding)

If you don't know Ruby, send is a sneaky backdoor way of calling private methods. Encountering send is almost always a red flag that the code is violating the intent of whatever is being invoked. It also means the code carries the risk that it will quietly break someday. Because I'm calling a private API, no one on the Rails team will cry for me when this stops working.

So, anyway, I got my thing working and I felt supremely victorious… for 10 whole seconds. Then, the doubts crept in. "Hmm, I'm gonna really be fucked if 3 years from now Rails changes this method signature." After 30 seconds hemming and hawing over whether I should inline the functionality and preemptively take ownership of it—which would separately run the risk of missing out on any improvements or security fixes Rails makes down the road—I remembered the answer:

I can solve this by codifying my assumptions at boot-time.

A little thing I tend to do whenever I make a dangerous assumption is to find a way to pull forward the risk of that assumption being violated as early as possible. It's one reason I first made a name for myself in automated testing—if the tests fail, the code doesn't deploy, and nothing breaks. Of course, I could write a test to ensure this method still works, but I didn't want to give this method even more of my time. So instead, I codified this assumption in an initializer:

# config/initializers/invariant_assumptions.rb
Rails.application.config.after_initialize do
  next if Rails.env.production?

  # Used by lib/middleware/conditional_get_file_handler.rb
  unless ActionDispatch::FileHandler.instance_method(:find_file).parameters == [[:req, :path_info], [:keyreq, :accept_encoding]]
    raise "Our assumptions about a private method call we're making to ActionDispatch::FileHandler have been violated! Bailing."
  end
end

Now, if I update Rails and try to launch my dev server or run my tests, everything will fail immediately if my assumptions are violated. If a future version of Rails changes this method's signature, this blows up. And every time I engage in risky business in the future, I can just add a stanza to this initializer. My own bespoke early warning system.

Writing this note took 20 times longer than the fix itself, by the way. The things I do for you people.

Welp, I just did the thing I promised I'd never do and read the Hacker News comments for this otherwise lovely post pointing out the durable relevance of Ruby on Rails twenty years later. One comment stood out as so wrong, however, I couldn't help but clap back.

It started when someone wrote, presumably in praise of Rails, "I really like web apps that are just CRUD forms." CRUD is shorthand for the basic operations of "create, read, update, and delete", and those four verbs can express the vast majority of what anyone has ever wanted to do with a computer. It's why the spreadsheet was the killer app of the 80s and early 90s. It's why Rails 2's embrace of REST as a way to encapsulate CRUD over HTTP redefined how pretty much everyone has made web applications ever since.

Anyway, in response to the fellow above said he enjoys simple CRUD apps, somebody else wrote:

I really like easy problems too. Unfortunately, creating database records is hardly a business. With a pure CRUD system you're only one step away from Excel really. The business will be done somewhere else and won't be software driven at all but rather in people's heads and if you're lucky written in "SOP" type documents.

This struck me as pants-on-head levels of upside down. Father forgive me, but I hit reply with this:

As someone who co-founded one of the most successful Ruby on Rails consultancies in the world: building CRUD apps is a fantastic business.

There are two types of complexity: essential and incidental. Sometimes, a straightforward CRUD app won't work because the product's essential complexity demands it. But at least as often, apps (and architectures, and engineering orgs, and businesses) are really just CRUD apps with a bunch of incidental complexity cluttering up the joint and making everything confusing, painful, and expensive.

I've served dozens of clients over my career, and I can count on one hand the number of times I've found a company whose problem couldn't more or less be solved with "CRUD app plus zero-to-one interesting features." No technologist wants to think they're just building a series of straightforward CRUD apps, so they find ways to complicate it. No businessperson wants to believe their company isn't a unique snowflake, so they find ways to complicate it. No investor wants to pour their money into yet another CRUD app, so they invent a story to complicate it.

IME, >=90% of application developers working today are either building CRUD apps or would be better off if they realized they were building CRUD apps. To a certain extent, we're all just putting spreadsheets on the Internet. I think this—more than anything else—explains Rails' staying power. I remember giving this interview on Changelog and the host Adam asking about the threat Next.js posed to Rails, and—maybe I'd just seen this movie too many times since 2005—it didn't even register as a possible contender.

Any framework that doesn't absolutely nail a batteries-included CRUD feature-set as THE primary concern will inevitably see each app hobbled with so much baggage trying to roundaboutly back into CRUD that it'll fall over on itself.

Anyway, I believe in this point enough that I figured I should write it down someplace that isn't a deeply-nested comment at a place where I'd recommend you not read the comments, so that's how you and I wound up here just now.

I used to think I loved to travel, but it turns out I just hated where I lived.

Now that I’m where I want to be, travel has just become an expensive way to feel inconvenienced and come home sick.

What is Refactoring?

Realized I managed to shit out a decent definition of the word "refactoring" in 30 seconds when I was editing this longer video on career advice, so may as well clip it for posterity. Both were pulled from v31 of Breaking Change.

I hate to spoil the end of this wonderful post for you, but:

In short, if your priority is developer experience, you won’t find a better option than Heroku.

Heroku's greatest sin wasn't in selling out (in fact, its Salesforce acquisition was one of the least disruptive in the history of SaaS), it was in well and truly solving the problem it set out to address. Then, after that, …they didn't have much new and shiny to talk about. Everything Just Worked™.

There are a handful of rough edges that they're still sanding down—HTTP 2 support was the big one, but they've recently covered that. I take the fact that I reacted to their banner about a next generation paltform with worry it might make something worse as a GOOD sign. Those among us who actually give a shit about working software should do a better job of rewarding the handful of companies that pull it off.

(And if you do use Heroku, Judoscale's founder Adam is an old friend and a Searls-certified badass and you should check them out.)

Namanyay Goel with a post that appears to be lighting the Internet on fire and is making me feel pretty smart about emphasizing the generational transition at which the software world finds itself:

We're at this weird inflection point in software development. Every junior dev I talk to has Copilot or Claude or GPT running 24/7. They're shipping code faster than ever. But when I dig deeper into their understanding of what they’re shipping? That's where things get concerning.

Sure, the code works, but ask why it works that way instead of another way? Crickets. Ask about edge cases? Blank stares.

Imagine if your doctor had skipped med school, but—don't worry!—the AI tool built into his Epic tablet gave him the right answers to every question that happened to come up during your intake consult and your first physical.

Hopefully you never need anything serious.

I see that the React team has sunset Create React App, a position I’ve been advocating for since the day they launched Create React App!

Generating and committing a bunch of files people don’t understand that will be woefully out-of-date in 3 months was never a great idea.

Turning your audio podcast into a video-first production

I was chatting with Adam Stacoviak over at Changelog a couple weeks back, and he mentioned that this year they've taken their podcast "video-first" via their YouTube channel.

I hadn't heard the phrase "video-first" before, but I could imagine he meant, "you record the show for video—which is more complex than recording audio alone—and then the audio is a downstream artifact from that video production." Of course, key to my personal brand is failing to demonstrate curiosity in the moment by simply asking Adam what he does, and instead going on an individual two-week-long spirit quest to invent all the wheels myself based on the possibly incorrect assumption of what he meant in the first place.

Anyway, as of v31 of Breaking Change, my podcast is now, apparently, a video-first production. I figured I'd share my notes on the initial changes to the workflow, along with links to the products I'm using.

Here's the video:

And here's the extremely simple and easy 10 step process that got me there (with affiliate links throughout):

  1. Record audio and video in OBS
    • Video is recorded in 4k@60fps in 8-bit HEVC as an MKV file (because MKV files, unlike MOV, can be interrupted by a crash without losing the entire recording)
    • I use a Sony a7 mark IV over HDMI with Elgato Camlink 4K mounted via the Elgato Master Mount system and flanked on either side by Elgato Key Lights Air. I also have an Elgato Prompter in front of the lens that displays two windows, side-by-side: OBS on the left and my Things project with my show topics on the right
    • I record audio tracks from an SSL 2+ USB interface
      • Track 1 is reserved for the first XLR input, which has a bog-standard SM7b microphone plugged into it
      • Track 2 is L&R of the loopback interface for music and stingers (here's my guide on setting up SSL2+ for loopback that allowed me to avoid software like Audio Hijack)
    • Music and stingers are played manually from shortcuts on my Stream Deck XL
    • While recording, if I need a break, I only hit PAUSE/UNPAUSE instead of STOP/START to ensure only one file is created
    • When finished, leave it recording and then LEAVE THE ROOM for a minute to create some dead air I can later use to sample the room noise with iZotope's RX Spectral Denoise plugin

Content warning: more content…

Breaking Change artwork

v31 - Full Searls Driving

Breaking Change

Update: This is the first version of Breaking Change available in 4K video. Check it out on YouTube.

The one where Justin picks the perfect time to buy his first Tesla vehicle.

Sometimes the things I say and do make people feel things. Share those feelings with me at this e-mail address: podcast@searls.co. Whatever you're feeling, I'll read it and do my best to acknowledge it. I also may read it on air. Unless you ask me not to, in which case I won't.

Okay, so anyway, there are links to be lunked:

Show those show notes…

A script to validate videos for the Instagram API

If you are publishing videos via the Instagram API (as I do for my feed2gram gem and for Beckygram), one of the first things you notice is that it is a lot less forgiving than their app is.

From their docs that spell this out:

The following are the specifications for Reels:

  • Container: MOV or MP4 (MPEG-4 Part 14), no edit lists, moov atom at the front of the file.
  • Audio codec: AAC, 48khz sample rate maximum, 1 or 2 channels (mono or stereo).
  • Video codec: HEVC or H264, progressive scan, closed GOP, 4:2:0 chroma subsampling.
  • Frame rate: 23-60 FPS.
  • Picture size:
  • Maximum columns (horizontal pixels): 1920
  • Required aspect ratio is between 0.01:1 and 10:1 but we recommend 9:16 to avoid cropping or blank space.
  • Video bitrate: VBR, 25Mbps maximum
  • Audio bitrate: 128kbps
  • Duration: 15 mins maximum, 3 seconds minimum
  • File size: 300MB maximum

If you get this wrong, you'll receive a mostly-unhelpful-but-better-than-nothing-error message that looks like this:

{
  "message": "The video file you selected is in a format that we don't support.",
  "type": "OAuthException",
  "code": 352,
  "error_subcode": 2207026,
  "is_transient": false,
  "error_user_title": "Unsupported format",
  "error_user_msg": "The video format is not supported. Please check spec for supported CodedException format",
  "fbtrace_id": "AvU9fEFKlA8Z7RLRlZ1j9w_"
}

I was sick of hobbling together the same half dozen ffprobe commands and then eyeballing the results (which are typically inscrutable if you don't know what you're looking for), so I wrote a script to test this for me.

For example, a recent clip failed to syndicate to Instagram and I wondered why that was, so I ran this little script, which I put on my PATH and named validate_video_for_instagram. It output:

Validating video: /Users/justin/Documents/podcast/clips/v30-the-startup-shell-game.mp4

✅ container
✅ audio_codec
✅ max_audio_sample_rate
✅ video_codecs
✅ color_space
✅ min_frame_rate
✅ max_frame_rate
❌ max_horizontal_pixels - Maximum columns (horizontal pixels): 1920 required; got: 2160
✅ min_aspect_ratio
✅ max_aspect_ratio
✅ max_video_bitrate_mbps
❌ max_audio_bitrate_kbps - Audio bitrate: 128kbps maximum required; got: 256.073
✅ min_duration_seconds
✅ max_duration_seconds
✅ max_size_megabytes

❌ Video had 2 error(s) preventing API upload to Instagram.
Docs: https://developers.facebook.com/docs/instagram-platform/instagram-graph-api/reference/ig-user/media

Is it surprising that the Instagram API won't accept 4K video? Yes. Especially since the videos weighs in at less than 100MB.

Want this for yourself?

To run this, you'll need a modern Ruby installed and ffprobe installed (on a Mac with homebrew, brew install ffprobe should do).

What happens next will shock you…

After 15 years of sugar-coating it, I'm upgrading from kid gloves to… regular gloves, I guess?

The truth is and has always been:

  1. That the best way to get better at programming computers is to spend time programming computers
  2. That a lot of people don't do this, but expect the same salaries as those that do
  3. That one highly-competent developer can often run laps around entire software teams at the typical company

I'm done pretending this isn't true.

The Wrong Programmers

I'm as big a fan of searching for environmental and systemic explanations when expectations are missed, but that doesn't mean that sometimes the blame lies with the skill of the people doing the job. Decided I'm going to stop pretending as if that's never the case.