👉 Right now, I’m writing The Go Optimization Guide, helping developers build faster, more efficient Go applications.

I cancelled my JetBrains subscription

Sulutor Lakes. The only gate up here is the weather, and it's the only one that actually gets enforced.

Today I cancelled my JetBrains subscription. Cancelling subscriptions is a popular trend right now, except the ones people announce dropping are Claude, ChatGPT, whichever model annoyed them this week. I went the other way. The models stay. The IDE goes. For a line item on a credit card statement it feels more symbolic than it should.

I have always been a Vim person. That never changed, and it isn't changing now. But I have been paying JetBrains since 2012, because for a long time Vim lost on two things. Navigation was the real gap: before language servers you had ctags, which is a text index pretending to understand code. No types, no scopes, five candidates for every common name. The other gap was visual. An IDE highlights faster and shows more, errors and types painted onto the code before you ask for them. Language servers closed the first gap. The second one JetBrains still wins, and the rest of this post is about why that no longer buys anything.

Somewhere along the way the reason changed. The products stopped being the thing I was buying and started being the thing I was supporting. If you want a good product to keep existing, the most honest thing you can do is pay for it, so I kept paying. Fourteen years of that.

Over-engineering used to be bounded by boredom

Belogorka waterfall in the Sokuluk gorge

Belogorka waterfall, Sokuluk gorge. Two hours from "the office" and nothing there needs a daemon.

A feature I'd sized at roughly 200 lines came back as 8,391 added and 692 removed, 3,431 of those lines being tests.

The requirement was one sentence. A CI job needs one of the two physical devices wired to the test rig, so: enumerate what's attached, pick one that's online, hand it back at teardown. What shipped was a forked daemon holding a flock for the lifetime of the job, PID identity verification against /proc start times, a SIGTERM to SIGKILL termination ladder, orphan sweeps, a readiness pipe, file-descriptor relocation, stdio detach, signal reset, and the tests to keep all of that honest.

Nothing there is an agent going off the rails. Every review finding was correct, and every guard defends against something that can really happen. The design is defensible option by option, which is the problem: the failure isn't visible anywhere except in the total. One incident, examined afterward against a census of my own configuration, so n is one.

Vibe Coding experiment

A bit more interesting data on vibe-coding vs structured assistant-coding.

Yesterday I ran an experiment. Had a task that will never touch production (local utility), and I'd been reading yet another piece about some engineer cranking out 10 MRs per day with AI. Good enough reason to actually try the dump-everything-at-once approach.

So I described the full scope to Claude upfront and let it run. Two things stood out. It took longer and produced more frustrating dead ends than my usual flow (detailed design first, then feature-by-feature implementation1). And it burned roughly twice the tokens compared to the structured approach. March 24 alone hit $35 after a session involving all three models, versus $10 on a more focused day. If you want to track your own numbers, npx ccusage@latest does the job.

I've seen the "feed it the whole task at once" question pop up a lot lately. Based on this, my answer is: don't. At least not yet. The model doesn't have the context to make the right trade-offs upfront, and you end up steering it through corrections that a proper design phase would have avoided entirely. And if you think vibe-coded output is going into a proper review pipeline, that's a separate problem2.

Vibe-coding makes for great demos. In practice, it's just paying more for worse results.

Making GenAI code review actually useful

Alamedin Gorge is one of my favorite places for a short weekend walk.

I ran a single Claude reviewer agent against an approximately 1000-line C++ change last month (yes, it's too big for a normal workflow, but we are in the GenAI era and norms are different)—some pretty important video pipeline updates, but nothing exotic. The review came back with 14 findings. Three were real. The rest included a phantom race condition in code that runs on a single thread, two style nitpicks elevated to High severity, and a complaint about missing error handling on a function that already returns std::expected. The signal-to-noise ratio was bad enough that I almost closed the tab.

This is the dirty secret of GenAI-assisted code review. The models are good enough to spot real issues, sometimes ones a tired human would miss. But they also hallucinate problems with enough confidence to waste your time, and the false positives are not random. They cluster around the same blind spots every run because they come from the same weights, the same training distribution, the same biases baked into one model family.

I wrote about the false positive problem briefly in my earlier piece on development processes in the GenAI era1, but I didn't have a concrete solution at the time. Now I do, and it's been running in my workflow for a few weeks. The short version: stop asking one agent. Ask three, and only keep what two of them agree on.

IPO Season and the Death of Software Engineering (Again)

Our Perseids campsite at Song Kol. No cell signal, no Blind, no LinkedIn panic.

I check Blind the way some people check horoscopes: every morning, expecting drama, occasionally finding truth. The Economist I read for the opposite reason, it's calm, structured, backed by actual data. These two don't usually agree on much. But over the past couple of months, they've converged on the same narrative, and that's when I start paying attention.

December 2025: the quiet IPO prep

Most people missed the December signal. No press conference, no earnings call. Just a Schumpeter column in The Economist1 noting that SpaceX, OpenAI, and Anthropic are all circling public listings. Anthropic hired Wilson Sonsini, the firm that took Google and LinkedIn public. Valuation tripled in six months to $183 billion2. Revenue reportedly went up ninefold in a year3.

Development processes in the GenAI era

The current debate around GenAI and C++ is a good illustration of the real problem. Many engineers report that models are worse than juniors. Others report dramatic speedups on the same language and problem space. Both observations are correct.

The difference is not the model. It is the absence or presence of the state.

Most GenAI usage today is stateless. A model is dropped into an editor with a partial view of the codebase, no durable memory, no record of prior decisions, no history of failed attempts, and no awareness of long-running context. In that mode, the model behaves exactly like an amnesic junior engineer. It repeats mistakes, ignores constraints, and proposes changes without understanding downstream consequences.

When engineers conclude that “AI is not there yet for C++”, they are often reacting to this stateless setup.

At the same time, GenAI does not elevate engineering skill. It does not turn a junior into a senior. What it does is amplify the level at which an engineer already operates. A senior engineer using GenAI effectively becomes a faster senior, and a junior becomes a faster junior. Judgment is not transferred, and the gap does not close automatically.

These two facts are tightly coupled. In stateless, unstructured usage, GenAI amplifies noise. In a stateful, constrained workflow with explicit ownership and review, it amplifies competence.

This is why reported productivity gains vary so widely. Claims of 200–300% speedup are achievable, but only locally and only within the bounds of the user’s existing competence. Drafting, exploration, task decomposition, and mechanical transformation accelerate sharply. End-to-end throughput increases are lower because planning, integration, validation, and responsibility remain human-bound.

The question, then, is not whether GenAI is “good enough”. The question is what kind of system you embed it into.

Note

Everything I'll explain below is only applicable to the Stateful GenAI setup.

Today I learned... git shallow

Sometimes you need to understand why something exists, and instead, you’re staring at a mystery. It feels like magic for a moment. But there is no magic in IT. There is always a reason, and usually it’s painfully concrete.

Today I learned that if git blame suddenly claims I wrote the entire million-line project, it might be lying 🙂

I ran into a situation where my local git blame attributed every line to a single recent commit, while GitLab showed the correct historical authors. At first glance, it looked like history had been rewritten, which is odd and incorrect.

Dealing with ThreadSanitizer Fails on Startup

Usually, you need just a few lines to initialize TSan in your project: you compile with the sanitizer flags, run the tests, and get a clear report of which threads touched which memory locations. On a modern Linux system, that simple expectation can fail in a very non-obvious way.

FATAL: ThreadSanitizer: unexpected memory mapping 0x...

In my case, I attached TSan to a not-so-young C++ codebase and immediately encountered a fatal runtime error from the sanitizer, long before any of the project's code executed. No race report, no helpful stack trace, just a hard abort complaining about an "unexpected memory mapping."

If you can upgrade your toolchain to LLVM 18.1 or newer, this problem effectively disappears, because newer TSan builds know how to recover from the incompatible memory layout. Suppose you are pinned to an older LLVM (by CI images, production constraints, or corporate distro policy). In that case, you are in the same situation I was: you have to understand what the sanitizer is trying to do with the address space, and work around the failure mode yourself.

Cross-Compiling Rust for Raspberry Pi

I just started a new embedded pet project on the Raspberry Pi, and I expect it'll be a pretty big one, so I've been thinking about the technology from the beginning. The overall goal is to create a glass-to-glass video pipeline example. Let's see how it's going. For now, I'm using a USB V4L2 camera while waiting for the native Pi modules to arrive, but it's enough to sketch the capture loop and start testing the build pipeline. The application itself is minimal—open /dev/video0, request YUYV at 1280x720, set up MMAP buffers, and iterate over frames—but the real challenge occurs when v4l triggers bindgen, and the build must cross-compile cleanly for aarch64

The language choice immediately becomes part of the equation right away. Go is my favorite and, usually, is not considered as an option by many embedded developers. But it's a good choice for small embedded utilities because its cross-compilation story is nearly effortless. Need an ARM binary? One command and you have it!

GOOS=linux GOARCH=arm64 go build

What Actually Drove the Tech Layoffs

A 10-minute ride and you have such a view from Chon Aryk hills.

I truly enjoy reading Blind and Levels. There is so much internal drama, messy details, and unexpected insights that you almost do not need reality shows anymore. And if you ever feel bored, you can always drop a mildly toxic comment into a thread and watch the whole thing ignite. It fits the overall style of Blind a little too well, but that is part of the fun. And considering that mix of casual toxicity and surprisingly rational takes you see there, you would expect people to look at layoffs with a bit more perspective. But when the topic comes up, the conversation usually drifts to the same explanation. People blame AI. People say their jobs vanished because a model wrote some code. And while I understand the frustration, the logic never sits right with me. Nobody complained during the hiring boom of 2020 and 2021, when companies doubled their headcount like it was nothing. That part gets forgotten. Now that the correction is here, many want a simple villain. AI fits the story, but it does not fit the data.