TIL.
Things I didn’t know yesterday but thought were worth sharing today.
-
Bah, heisenbug!
View "Bah, heisenbug!"A heisenbug is a software bug that seems to disappear or alter its behavior the moment you try to study it. Sometimes adding a log statement, stepping through a debugger, or making an unrelated code change is enough to make the problem vanish.
The name puns on Werner Heisenberg’s uncertainty principle—the rule in quantum mechanics that certain pairs of physical properties, such as a particle’s position and momentum, cannot both be known precisely at once. It’s also a reference to the observer effect, which states that the act of measuring a system inevitably disturbs it. So it is with a heisenbug—the moment you try to observe it, you change the conditions that produced it, and the bug slips away.
-
Cleaner conditional CSS classes with Rails’
class_namesView "Cleaner conditional CSS classes with Rails’class_names(Rails 6.1+, fromActionView::Helpers::TagHelper) builds CSS class lists from strings, arrays, and hashes: hash keys are class names, and their values decide whether each is included. It’s an alias oftoken_list, so use whichever reads better to you.Instead of cramming conditionals into a string, you pass a hash:
# Before: the pattern you see in a lot of Rails apps "nav-link #{active ? "nav-link-active" : ""}" # => "nav-link nav-link-active" (when active is true) # => "nav-link " (when active is false) — note the trailing space # After: class_names("nav-link", "nav-link-active": active) # => "nav-link nav-link-active" (when active is true) # => "nav-link" (when active is false) — no trailing spaceAnd the best part? Rails’ tag builder applies the same logic to any
class:option, so tag-producing helpers—tag,content_tag,link_to,button_to, and friends—accept conditional hashes for free. Here’s a real example from JobJournal’s_filter_dropdownpartial:link_to "...", class: [ "dropdown-option", "md:hidden": link[:mobile_only] ]class_names" -
Humans are surprisingly sensitive to audio-visual lag
View "Humans are surprisingly sensitive to audio-visual lag"Our senses forgive a late sound far more readily than an early one. Sound arriving before its image unsettles us; sound arriving slightly after barely registers. Most people start to notice the mismatch when audio leads the image by roughly 20–50 milliseconds—yet audio can lag as much as 80–150 milliseconds behind the image before it’s equally jarring. That’s roughly three times the tolerance in the other direction.
The asymmetry comes from how the brain assembles a “moment”. It doesn’t judge each sound and image independently; it briefly holds signals from the eyes and ears, fusing those that arrive close together into a single perceived event. Inside that window, everything feels perfectly in sync, and a small audio delay goes unnoticed. Push past that window and the illusion breaks.
This bias runs one way by design: the brain is calibrated to a world where sound always arrives a step behind. Light travels 874,000 times faster than sound—about 300 million m/s versus 343 m/s in air—so we always see an event before we hear it. Thunder trails lightning; the bang of a distant firework follows its flash. Sound arriving late is the pattern our senses already expect.
-
Overmind: A better foreman
View "Overmind: A better foreman"I’ve used Foreman for years to run Rails applications from a
Procfile, but recently switched to Overmind.Overmind is largely compatible with Foreman, so most existing
Procfile-based setups work without modification:brew install tmux overmind overmind startThe main advantage I’ve found is debugging. Since Overmind runs each process in its own
tmuxpane, you can connect directly to a process when needed:overmind connect webThis is particularly useful when a Rails process hits a
debuggerbreakpoint.A few other commands I’ve found useful:
overmind restart overmind quitOvermind also provides single-letter aliases for many commands:
overmind s # start overmind r # restart overmind c web # connect overmind q # quitI also added an
omshell alias forovermindto make the commands even quicker:om s om r om c web om qFor Rails applications that already use
foreman start, replacing Foreman with Overmind has been mostly seamless. -
Case-insensitive string comparison in Ruby
View "Case-insensitive string comparison in Ruby"You can’t call yourself a programmer if you’ve never needed to compare strings without regard to case. Most Ruby developers (myself included) reach for
String#downcaseto normalize both strings before comparing:string.downcase == another_string.downcase # => trueIt works, but Ruby already has built-in methods made for exactly this purpose:
String#casecmpandString#casecmp?.If you just need a boolean,
casecmp?is the cleanest option:"image".casecmp?("IMAGE") # => true "image".casecmp?("video") # => falsecasecmpbehaves similarly, but returns comparison values like the spaceship operator (<=>):"image".casecmp("IMAGE") # => 0 "apple".casecmp("banana") # => -1 "zebra".casecmp("apple") # => 1They’re more expressive than manually normalizing case and avoid allocating a lowercase copy of the string. A small Ruby feature, but a nice one to know.
-
Oh My Zsh’s
takecommandView "Oh My Zsh’sThe Oh My Zsh
takecommand is basically a shortcut for “set something up and immediatelycdinto it.” At its simplest, it wrapsmkdir -p+cd, but it extends the same idea to git repositories and remote archives.For local work, it behaves like a smarter
mkdirthat keeps you in context. So instead of creating a folder and then moving into it manually, you just do:take my/new/projectThis creates the full path if needed and drops you into the final directory.
It also supports git repositories. If you pass a git URL, it will clone the repo and immediately enter it:
take [email protected]:rails/rails.gitYou end up inside the cloned repository right away, which removes the usual “
git clone→cd” sequence. It’s especially useful when quickly inspecting or experimenting with a repo.There’s similar handling for remote archives. If the argument looks like a
.tar.gz,.tar.xz, or similar tarball, it will download it, extract it, and then move into the extracted directory:take https://example.com/project.tar.gzThis is basically a “download source distribution and enter workspace” shortcut, assuming the archive has a sensible top-level directory structure.
ZIP files are handled similarly. A ZIP URL gets downloaded and unzipped into the current directory, and
takethen tries to infer the extracted root folder andcdinto it:take https://example.com/project.zipThis one is a bit more heuristic than the tar handling, since ZIP archives are less consistent in how they structure top-level directories.
Under the hood,
takeis just a dispatcher that decides which of these behaviors to trigger based on the input pattern: local paths go tomkcd/takedir, git URLs go totakegit, tar archives go totakeurl, and zip archives go totakezip. The consistent idea across all of them is eliminating the repeated setup steps, so every operation ends with you already inside the working directory.takecommand" -
Imaginary and complex numbers in Ruby
View "Imaginary and complex numbers in Ruby"Ruby has built-in support for complex numbers via the
Complexclass. It’s a small feature, but it makes working with imaginary numbers feel surprisingly natural.You can create complex numbers explicitly using the
Complexconstructor. This is the most direct and readable approach when both real and imaginary parts are variables or come from calculations.Complex(2, 3) # => (2+3i)Ruby also supports a literal syntax using the
isuffix, which makes imaginary numbers feel first-class in the language. This is often the most convenient form when writing quick expressions or math-like code.2 + 3i # => (2+3i)Once created, complex numbers behave like numeric types and support standard arithmetic operations out of the box. Addition, subtraction, multiplication, and division all follow mathematical rules without needing any special handling.
# math with Complex numbers is easy (2 + 3i) + (1 - 2i) # => (3+1i) (2 + 3i) - (1 - 2i) # => (1+5i) (2 + 3i) * (1 - 2i) # => (8-1i) (2 + 3i) / (1 - 2i) # => ((-4/5)+(7/5)*i) # works with Integers and Floats too (2 + 3i) + 5 # => (7+3i) (2 + 3i) - 1.5 # => (0.5+3i) (2 + 3i) * 0.5 # => (1.0+1.5i) (2 + 3i) / 2 # => (1+(3/2)*i)Ruby also includes some useful methods for switching between rectangular and polar representations of a complex number.
Calling
polaron a complex number returns an array containing the magnitude (distance from the origin) and angle in radians, effectively decomposing the complex number into its polar coordinates:z = 2i # shorter form of z = 0 + 2i z.polar # => [2, 1.5707963267948966]If you want to go the other direction,
Complex.polarconstructs a complex number from a magnitude and angle:z = Complex.polar(2, Math::PI / 2) # => (0.0+2i)These two methods are essentially mirrors of each other:
z.polardecomposes a complex number into[magnitude, angle]Complex.polarbuilds a complex number from[magnitude, angle]
z = 2i magnitude, angle = z.polar Complex.polar(magnitude, angle) # => (0.0+2i)You can also get the rectangular coordinates of a complex number using
rectangular(or its shorter alias,rect), which returns the real and imaginary parts as an array:z = Complex.polar(2, Math::PI / 2) z.rect # => [0.0, 2]For the record, Ruby also exposes
Complex.rectangular(along with its alias,Complex.rect) for constructing complex numbers from rectangular coordinates, but these are really just explicit versions of the defaultComplex(real, imaginary)constructor:Complex(2, 3) # => (2+3i) Complex.rectangular(2, 3) # => (2+3i) Complex.rect(2, 3) # => (2+3i)The
Complexclass is part of Ruby’s core library, so no external dependencies are required. -
Ruby’s
%notation is more powerful than you thinkView "Ruby’sI already knew the basics—
%wfor word arrays,%ifor symbol arrays,%q/%Qfor strings,%rfor regexps—but there’s more to the%notation than I realized.%()defaults to interpolated strings%Qis the interpolated string literal, but you can drop theQentirely.%()is equivalent to%Q(), which is equivalent to double-quoted strings:path = "/some/path" %(filename="#{path}") # => "filename=\"/some/path\""This is great when your string contains quotes and you don’t want to escape them.
Capital letters = interpolation
The lowercase/uppercase pattern is consistent: lowercase is non-interpolated, uppercase is interpolated.
language = "Ruby" # Non-interpolated (lowercase) %w[hello #{language}] # => ["hello", "\#{language}"] %i[hello #{language}] # => [:hello, :"\#{language}"] # Interpolated (uppercase) %W[hello #{language}] # => ["hello", "Ruby"] %I[hello #{language}] # => [:hello, :Ruby]%xfor shell commands%xworks like backticks but with the%delimiter flexibility:%x(echo "hello from the shell") # => "hello from the shell\n"%sfor non-interpolated symbols%s(hello world) # => :"hello world" %s(#{nope}) # => :"\#{nope}"Any non-alphanumeric character works as a delimiter
I’d only ever used
(),[],{},||, and<>, but you can use any single non-alphanumeric character—even whitespace:%w#foo bar# # => ["foo", "bar"] (array of words with hashes as delimiters) %i!foo bar! # => [:foo, :bar] (array of symbols with exclamation marks as delimiters) %r.foo bar. # => /foo bar/ (regex with periods as delimiters) % test # => "test" (string with spaces as delimiters) % foo\ bar # => "foo bar" (string with spaces as delimiters, with an escaped space inside—please don't)Paired delimiters allow unescaped nesting
If you use
(),[],{}, or<>, you can include those same characters unescaped inside the literal—as long as they appear in balanced pairs:%(string (syntax) is pretty flexible) # => "string (syntax) is pretty flexible" %w[one [two three] four] # => ["one", "[two", "three]", "four"]%notation is more powerful than you think" -
Ruby’s
gsubaccepts a block via&:shorthand syntaxView "Ruby’sYou may already know you can pass a block to
gsublike this:"the html spec and some css tricks".gsub(/\b(html|css)\b/) { it.upcase } # => "the HTML spec and some CSS tricks"But you can make this even simpler by using the
&:shorthand syntax:"the html spec and some css tricks".gsub(/\b(html|css)\b/, &:upcase) # => "the HTML spec and some CSS tricks"Isn’t Ruby just beautiful?
gsubaccepts a block via&:shorthand syntax" -
Ruby allows passing a Hash to
gsubView "Ruby allows passing a Hash toInstead of chaining multiple
gsubcalls, you can pass a hash as the replacement. Each match is replaced by its corresponding value (or removed if no key exists).Say you’re normalizing “smart” punctuation into plain ASCII. (This is a job
trcan’t do: an em dash becomes two characters.)text = "He said — “that’s ‘fine’…”" # Before: text.gsub("“", '"').gsub("”", '"').gsub("‘", "'").gsub("’", "'").gsub("—", "--") # => "He said -- \"that's 'fine'…\"" # After: replacements = { "“" => '"', "”" => '"', "‘" => "'", "’" => "'", "—" => "--" } text.gsub(/[“”‘’—]/, replacements) # => "He said -- \"that's 'fine'…\"" # Matches with no corresponding key are removed (here, the ellipsis): text.gsub(/[“”‘’—…]/, replacements) # => "He said -- \"that's 'fine'\""You can also define the hash inline. In typical Ruby fashion, the outer
{}can be omitted:text.gsub(/[“”]/, { "“" => '"', "”" => '"' }) text.gsub(/[“”]/, "“" => '"', "”" => '"')Cleaner, more readable, and more performant than chaining multiple
gsubcalls.gsub" -
Subtle dark-mode surfaces with
color-mix()View "Subtle dark-mode surfaces withThe blog post cards on this site need a card background that’s a touch lighter than the page in dark mode, but plain white in light mode. Instead of defining a third color variable, I mix the existing
--color-surfacehalfway into--color-bgin the dark slot oflight-dark():.blog-post-card { background: light-dark( var(--color-bg), color-mix(in oklch, var(--color-surface) 50%, var(--color-bg)) ); }The
in oklchinterpolation keeps the perceived lightness even, so the card reads as “a step up from the page” without ever looking muddy. Change the theme’s surface or background tokens and every card adjusts for free.color-mix()"