Personal blog

Category: Programming (Page 1 of 3)

Friendship ended with RVM, now rbenv is my best friend

After about 10 years, I had to break up with RVM – mostly because a third party (Claude Code) wouldn’t cooperate.

RVM works through shell functions. When you install it, it wraps your cd command with a custom function that checks .ruby-version on every directory change. This only works in interactive shells that have loaded .zshrc, where RVM had a chance to set this up.

rbenv works through shims. It puts a directory of tiny wrapper scripts (~/.rbenv/shims/) at the front of your PATH. Every time you call ruby, bundle, rake, or any ruby binary, you’re actually calling a shim. The shim reads .ruby-version from the current directory (walking up the tree if needed) and delegates to the right ruby version. It relies on standard PATH behavior rather than shell hooks.

Where this starts to matter

Any tool that spawns subshells – CI runners, editors, AI coding assistants – often uses non-interactive shells that skip .zshrc. With RVM, that means version switching won’t happen unless you explicitly source it. With rbenv, as long as ~/.rbenv/shims is in PATH, everything behaves the same as in your terminal.

This was the original motivation: Claude Code spawns a fresh non-interactive subshell per Bash call, so RVM’s setup never ran and version switching simply didn’t happen.

webfilterproxyd causes bundler SSL errors

So, I had this ongoing issue with bundler:

Errno::ECONNRESET: Connection reset by peer - SSL_connect
(...)/.rbenv/versions/3.4.1/lib/ruby/gems/3.4.0/gems/net-protocol-0.2.2/lib/net/protocol.rb:46:in 'OpenSSL::SSL::SSLSocket#connect_nonblock'
(...)/.rbenv/versions/3.4.1/lib/ruby/gems/3.4.0/gems/net-protocol-0.2.2/lib/net/protocol.rb:46:in 'Net::Protocol#ssl_socket_connect'
(...)/.rbenv/versions/3.4.1/lib/ruby/3.4.0/net/http.rb:1736:in 'Net::HTTP#connect'
(...)/.rbenv/versions/3.4.1/lib/ruby/3.4.0/net/http.rb:1636:in 'Net::HTTP#do_start'
(...)/.rbenv/versions/3.4.1/lib/ruby/3.4.0/net/http.rb:1631:in 'Net::HTTP#start'

TL;DR

I asked Claude Code to fix that for me and, quite surprisingly, it did.

The culprit was webfilterproxyd which macOS sets up for when you enable web content filtering (in System Settings).

I’ve seen plenty of resources about bundler SSL errors, but I haven’t found any that link them to webfilterproxyd. So I’m leaving this here in case it helps someone else.

Continue reading

Tests should give confidence

This is the most important, yet the most unspoken trait of good tests. Tests should be the oracle.

If all tests are green – the app will work on prod. If at least one test is red – the app won’t work.

Think Continuous Delivery. Every step in the process has to be automated. When tests are green, there is no room for extra human checks before going prod. The decision (deploy – no deploy) is handed over to your tests.

Do you trust your tests this much? Is this even possible to be this confident about tests?

Continue reading

The simplest dependency injection

Consider a method that calculates some ETA:

class Calendar {
  add3WorkingDays(): Date {
    const result = new Date();
    let daysAdded = 0;
    while (daysAdded < 3) {
      result.setDate(result.getDate() + 1);
      if (this.isWorkingDay(result)) {
        daysAdded++;
      }
    }
    return result;
  }

  isWorkingDay(date: Date): boolean {
    const day = date.getDay();
    return day !== 0 && day !== 6;
  }
}

Notice how add3WorkingDays depends on the time when it’s called (result is initially now). Because of that, you’d have trouble writing reliable unit tests for this.

My recommendation: change the implicit dependency of now to explicit one and inject it:

  add3WorkingDays(date: Date): Date {
    const result = new Date(date);
    // no changes from here

It takes only so much to implement dependency injection, the most overcomplicated technique in the history of programming. You don’t need frameworks, containers, service locators, interfaces, or decorators. You only need to know how to parameterize a method.

Oh, by the way, we changed the interface of the method too. We have to change all the callers: calendar.add3WorkingDays() -> calendar.add3WorkingDays(new Date())

Does this mean you don’t want to introduce dependency injection later in the project, to avoid risky refactors? No. You can still have DI without changing the interface using the default parameter:

  add3WorkingDays(date: Date = new Date())
Continue reading

Unit tests can (and should!) go through many classes

We often design our code so that one procedure comprises many classes, often grouped by layers. For example, in web dev, that could be controllers, models, repositories, etc:

Layers are good. Often at least two layers are desirable for maintainability: domain and low-level. If you’re like me, you like 2-5 layers but not too many.

Now how do you test such code? It comes naturally to want to test each component separately and mock out the collaborators from other layers. I believe it’s because unit tests are said to be run “in isolation”:

My advice – don’t automatically do this. Aim for this instead:

Why is it better?

Continue reading

🎉 Puzzle Balls Release 🎉

I have the pleasure to announce: the playable release of Puzzle Balls is out! Go ahead, play it! Prepare your arrow keys and space bar, and connect 3 or more; fullscreen recommended!

Puzzle Balls early release gameplay preview

The game is not developed only by me. I collaborate with a dear friend of mine, Michał Hołowaty.

Keep in mind it’s a very early release. There is not much depth to the gameplay, plus many assets are just a placeholder, and many features are in the early stages. Although the game is fully functional and can be highly addictive! You have been warned!

We keep having fun playing and developing the game, so save the link in your bookmarks and come back to it once in a while. You are likely to encounter new features soon!

Continue reading
« Older posts

© 2026 Bartosz Krajka

Theme by Anders NorenUp ↑