brandur.org

Atoms

Multimedia particles in the style of a tweet, also serving as a changelog to consolidate changes elsewhere in the site. Cross-posted to an atom feed. Frequently off topic.

Published fragment ICQ, on the end of the universe, coming June 26th.



Published fragment Notes on dark mode, which is not a dark mode tutorial, but collects a few notes on some specific refinements of a good dark mode implementation like tri-state instead of bi-state toggle, avoiding page flicker, and responding to theme changes from other tabs or the OS.


I rebuilt this site’s index page so it’s on the newer template system and can take advantage of dark mode. It’s not amazing, but I didn’t want to agonize over it for too long since likely few people will ever go there, so I just threw something together and published it.


From Notes on Japan, I found this last point was quite funny:

visiting Japan feels like visiting the 2000s

  • CD shops everywhere

  • malls are thriving

  • people use fat laptops

You’d have to pry by MacBook from my cold, dead hands, but I dearly miss the greater variety of hardware and form factors that we used to see twenty years ago. Like, I acknowledge that something like the Sony VAIO P (depicted below) probably has ergonomics on par with writing War and Peace via T9 text entry, but I still wish I’d owned one.

Japan seems to be one of the last places left where a market for weird consumer electronics still exists. A few weeks ago I admired a Japanese guy on BART’s incredible e-reader that was fully customized to the use of Japan’s vertical alphabets. Another guy on the 37 Corbett uses what must’ve been the only netbook left in San Francisco.

If you’re at a typical WeWork nowadays, there’s no easy way to tell that you didn’t accidentally walk into an Apple Store instead. The technology is beautiful – perfect geometry, flawless glass, and polished aluminum – but sterile. I’m glad there’s somewhere out there where weird, novel devices are still going strong.


I implemented dark mode for this website, which you should be able to enable using the toggle in the upper right. I figure that if even Google search can do it given what’s sure to be millions of lines worth of legacy code, then I should be able to as well.

I’ll write more about this soon, but by far the hardest part about dark mode is restyling. A site like this one not only has accumulated thousands of pages over the years, but also a dozen major templates of varying quality, each of which needs attention to support such a colossal change. I’ve slowly been moving over to Tailwind since last year and as I did, it gave me time to do some spring cleaning on the template system. Without Tailwind and that cleanup, adding dark mode would’ve been such a big job that I’m not sure I ever would’ve gotten around to it.

This site is a constant WIP and I’m sure I’ll be making some tweaks over the coming months, but if you notice any major usability problems that I missed, open a GitHub issue.


Published sequence 079, spring in Leipzig.


Soutaro (major Ruby typing contributor) has launched a new RBS::Inline project that enables Ruby type annotations in comments instead of a companion RBS file:

# rbs_inline: enabled

class Person
  attr_reader :name #:: String

  attr_reader :addresses #:: Array[String]

  # @rbs name: String
  # @rbs addresses: Array[String]
  # @rbs returns void
  def initialize(name:, addresses:)
    @name = name
    @addresses = addresses
  end

  def to_s #:: String
    "Person(name = #{name}, addresses = #{addresses.join(", ")})"
  end

  # @rbs yields (String) -> void
  def each_address(&block) #:: void
    addresses.each(&block)
  end
end

The README notes that if all goes well, this will be merged back upstream into the main RBS gem and become a first class feature. During a recent trial run of RBS I found having to have two tabs open for every Ruby file (the .rb and its companion .rbs) to be somewhat painful, and having an alternative is a welcome addition.


Slack is training AI on private customer data.

Years ago I had a conversation with a company that was building a Slack killer. I thought they were crazy. Slack was a beloved product that had built itself the perfect moat. Not through some exotic feature set that nobody else had, but by being feature complete, and a little better and a little more refined than any of its competitors.

Compared to HipChat (what we’d been using at the time), Slack was lightning fast, had a thoughtful UI that someone had obviously taken the time to get right, and prone to far fewer bugs and outages. It was hard to describe in few words why one chat app was so much better than the other chat apps, but anyone who used Slack for five minutes understood immediately.

A lot of ground’s been lost since then. Slack’s recent UI redesign is a textbook case of ignoring usability in favor of minor cosmetic benefit – a fundamental degradation in UX to save twenty pixels on the left side of the screen. When users expressed unhappiness, Slack doubled down. The alternative – a roll back and tarnishing of the reputation of some random, unqualified design VP at Salesforce – was obviously intolerable. Previously rock solid reliability has gotten steadily worse with long load times and frequent client desynchronization.

Telling paid users that their private communications is now property of Slack for AI training is more of the same. (But don’t worry, you can opt out by emailing support! They could’ve by all rights required that a paper TPS form be submitted by mail.)

If there’ll be one future lesson from Slack, it’ll be that it’s not only possible to lose your lead from competitors advancing, but by regressing yourself.


Thought provoking post from DDH on the broad failure of system tests, defined in this context as web UI tests, driven by a headless browser.

A good way to test UIs is a problem that people have been trying to solve since the moment I stepped out of university and into a software engineering job. Back then, despite the evasiveness of a good answer to date, I assumed that someone would eventually figure it out. Now, almost twenty years later and an even halfway good answer still elusive, I’m not so optimistic.

Our latest round of test strategy uses Playwright, which describes itself as “reliable end-to-end testing for modern web apps”. I haven’t found it particularly so:

  • Failures are totally opaque. The error you get is that there was a timeout waiting for a UI element to appear or a page to transition, telling you nothing about what actually happened, making debugging slow.
  • Tests are hard and time intensive to write, with the predictable result being that most developers don’t bother writing them.
  • Tests are slow. Even parallelized into four cooperating jobs, CI takes almost ten minutes to run.
  • The whole set up requires substantial configuration, heavy toolchain, and test scripts. It’s as far from a git clone && make test as you can get.
  • To make testing reproducible, we have a “fixture” setup is used in the backend that saves and replays API requests. Close to ~every test failure is the result of a fixture failure where a fixture needs to be recorded or re-recorded because something was updated.

All in all – and I’m trying my best to make sure that I’m not exaggerating – the false positive rate on failures is something like 99%. I actually don’t recall ever seeing a true positive in the sense that a test case caught something I broke by accident.

DHH’s prescription seems extreme at first glance:

HEY today has some 300-odd system tests. We’re going through a grand review to cut that number way down. The sunk cost fallacy has kept us running this brittle, cumbersome suite for too long.

But for a test suite that slows development and only prevents a regression once in a blue moon, isn’t it the only rational answer?


Published fragment Use of Go’s cmp.Or for multi-field sorting, on a more elegant way to sort on multiple fields using Go 1.22’s cmp.Or helper.


I recommend Return YouTube Dislike, a browser extension that brings back the dislike count on YouTube videos, and run as an open source project on GitHub. I had a vague idea that an extension like this might exist, but didn’t do anything about it until recently, when I saw a screenshot from somoene else who still had theirs intact.

It shows how much work Google has put into protecting the message from our heroic defenders-of-big-D-Democracy coastal wine-and-cheese chattering class, who naturally are the self-elected determiners of all that is tasteful and good. Best exemplified by Hollywood, the trailer for the new Rings of Power season that dropped yesterday is sitting at a highly skewed 👍71k/👎340k. The trailer for Disney’s upcoming Star Wars series The Acolyte is at 👍70k/👎159k. Of a half dozen of CNN’s most recent videos, the majority of them have more dislikes than likes. Merely knowing how widely this content is disliked is dangerous information that must be suppressed, and Google is more than happy to comply.


Published fragment ValOrDefault, on a pair of helper functions ValOrDefault and ValOrDefaultFunc that can help significantly to clean up Go code around assigning default values.

And actually, an update: Go 1.22’s cmp.Or supersedes this custom helper.


Published fragment Heroku on two standard dynos?, on contemplating whether the Heroku platform would fit on two standard 512 MB dynos if it could be ported from Ruby to Go.


Published fragment Activating cached feature flags instantly with notify triggers, on reflecting changes made to feature flags immediately, despite a local in-process cache, by firing sync notifications from triggers, and listening with the notifier pattern.