I started bitwise for a petty reason. I spend my days in low-level code, and I was tired of reaching for the mouse to open the KDE calculator every time I needed to know which bit of a register dump was set. A terminal is already open. Why am I leaving it?

One number in, every representation out. This is the itch.
Seven years later the thing is in Debian, Fedora, openSUSE, Void, Alpine, FreeBSD, OpenBSD, Guix, Nix, Homebrew, MacPorts, the AUR, MSYS2, Buildroot and Yocto. Twenty-one other people have sent patches. It has a few hundred stars.
Writing it was the easy part. This is the story of the rest.
The first three weeks
Anyone who has written an ncurses form will recognise the shape of those first weeks: long stretches where nothing updates, then a sudden moment where the whole screen starts behaving and you can’t entirely explain why. Bit toggling took me two attempts and one evening of swearing.

Once it worked I added vim movement keys — hjkl to walk the binary row — almost as an afterthought. That afterthought turned out to be the feature people would compliment most often. Around the same time I pulled the curses code out of main so there could be a plain command-line mode, which is now how the majority of people actually use the tool. Neither decision felt important at the time. Both were.
I also threw away my hand-written Makefile for autotools and wrote a snapcraft file — the first of many hours spent on the packaging rather than the program, though I didn’t know that yet.
Then I tagged a release, wrote a short blog post, and posted it to Hacker News.
Four points. No comments.
The ten weeks nobody saw
That could have been the end of it. What’s interesting in hindsight is that bitwise became genuinely good during the stretch when nobody was watching.
That’s when it got a real expression parser, so you could type (0x40 | 3) << 2 instead of clicking bits:

That’s when it got unit tests. A history window. A $ symbol that evaluates to your last result, which is the feature I personally use most. ASCII output. The distinction between KiB and kB, because 1024 and 1000 are not the same number and it bothered me.
None of that came from feedback, because there wasn’t any. It came from using my own tool every day and being annoyed by it.
Ten weeks after the first attempt I posted it to Hacker News again. Same link. New title — the first one had described what the program was, and the new one described what it did for you.
113 points. Front page.
I don’t have a clever theory about this. Same project, same URL. The title was more concrete and the software was considerably better than it had been. Both probably mattered. So did luck.
What the front page actually feels like
It is not a victory lap. It’s a firehose of people telling you, politely, everything that’s wrong with your program. And it is wonderful.
Ten issues opened that day. Strangers found real bugs within hours — not misunderstandings of the UI, actual defects I’d never hit because I only ever ran the program on my own machine, in my own terminal, in my own locale, at my own word size. I spent the day fixing integer format specifiers, missing includes, and colour handling on terminals I’d never seen. Every one of those was someone else’s computer telling me I’d been writing for an audience of one.
Three moments from that thread have stayed with me.
The compliment that was really about work. Someone wrote that they were forever rebuilding the same throwaway scripts in a REPL or in /tmp, and that generalising one of them into an actual tool “takes tons of work.” That’s the truest thing anyone has said about this project. Everybody has the hacky version. The distance between the hacky version and something a stranger can install is the entire job, and it is almost all of the effort.
The joke that came back as a patch. My README had a row in the install table for Windows that was essentially a shrug and a question mark. Someone quoted it back at me in the thread and suggested a serious approach. Someone else skipped the discussion entirely and just sent a pull request titled “Stopped be facecious about windows,” with a fixed build script attached. Point taken. It took until last year for another contributor to make the msys2 build genuinely work, and there’s a Windows CI job now.
The packaging failure, live on stage. That same day, somebody following my own README on Ubuntu under WSL got an uninstallable package: my PPA build had picked up the wrong readline. The most-read day in the project’s life, and the install instructions were broken.
That was my introduction to the part of this project that has been consistently harder than the code.
Distribution is the hard part
bitwise took a weekend to be useful and a few months to be good. Getting it to users has taken seven years and isn’t finished.
The channels I control were easy. A snap package took an afternoon. A PPA is a shell script. A release tarball is a git tag.
Everything else requires another human being — and to be clear, those human beings showed up. In most cases somebody packaged bitwise for their distribution without asking me, and then sent a pull request adding a line to my README so I’d know it existed. Fedora, Void, Nix, Alpine, the BSDs, Homebrew, MacPorts: all of them arrived that way. Repology lists around sixty repository entries now. I asked for almost none of it, and the generosity still surprises me.
Getting in was never the problem. Staying current is.
Upstream is at 0.70. Of those sixty-odd entries, the overwhelming majority say 0.50 — including rolling distributions that are supposed to be current. Ubuntu 20.04 still ships a binary I built in 2019. When I fixed a buffer overflow in the parser last month, I fixed it for the small number of people on Debian unstable or Homebrew. For everybody else that fix does not exist yet, and in some distributions it won’t for another year.
That gap — between “I fixed it” and “you have the fix” — is the number that actually matters for a project like this, and it’s the number I have the least control over.
Debian, specifically
Debian is where I’ve put the most effort, and it’s worth being precise about why it’s hard, because the difficulty is structural rather than anybody’s fault.
I filed an ITP — Intent To Package — back in 2019, and I’m listed as the package Maintainer. What I’m not is a Debian Developer.
That one fact shapes everything downstream. I cannot upload. Every release of bitwise that has ever entered Debian was signed by somebody else. The loop, each time, is: build the source package in a clean unstable environment, upload it to mentors.debian.net, file a request-for-sponsorship bug, and then wait for a Debian Developer to have a free evening, read your diff, and put their own name and reputation on your work.
They do it for free, for strangers, and they’re right to be careful about it. But the queue is long, and the latency compounds. One of my releases took the better part of a year to get from a git tag to Debian testing. Another I simply never uploaded at all — by the time I had the energy to deal with the process, the next version was nearly ready, so the archive skipped a release entirely.
And then Debian freezes. Each stable release ships whatever version of bitwise happened to be current when the freeze landed, and there it stays for years. If you’re on the current Debian stable, the program you can install is the one I considered finished a while ago, and nothing I do upstream changes that.
None of this is a complaint about Debian. The stability is the product. But it does mean that for a small upstream, “being in Debian” and “Debian users having your software” are two quite different achievements, and only the first one is a milestone you get to celebrate.
Coming back
After the last release the project went quiet for about three years — not because I’d lost interest, but because the to-do list had curdled. Everything left on it was the kind of task that’s tedious rather than hard: wire up sanitizers, write a fuzzer harness, replace CI that had been dead so long I’d stopped seeing the failure emails, fill in the files a project is supposed to have and mine never did. None of it was interesting. All of it was overdue. So for three years I opened the list, looked at it, and closed it again.
This summer I worked through most of that backlog with Claude, and the honest summary is that it collapsed the activation energy rather than the difficulty. The work was never beyond me; it was just never worth the evening. Having something to hand the boring half of each task to changed the maths — suddenly the tedious-but-overdue items cost little enough that they got done at all.
So they got done. Sanitizer builds, a fuzzer pointed at the expression parser, CI that actually runs on more than one platform, a rewritten README and man page, and the handful of genuine memory-safety bugs that turned up once something finally went looking — a buffer overflow in the tokenizer, undefined shifts, a few assumptions about signedness and byte order that had been wrong since the beginning and had simply never been triggered by the inputs I happened to type. I’d have found them eventually. I’d have found them in 2031.
The vim plugin got the same treatment, except there I threw the old one away. I created that repository two days after the Hacker News post, in a burst of enthusiasm, and then let it sit for seven years — and when I opened it again the original design was simply wrong. You invoked a command, it ran bitwise, and it put the answer in a buffer for you to go and read. That’s a lot of deliberate effort for a question you only half-wanted answered, and it’s why I never used my own plugin.
So vim-bitwise is a rewrite from scratch, and the whole thing is now a hover window. Rest the cursor on a numeric literal and its decimal, hex, octal and binary forms appear beside it — a floating window in Neovim, a popup in Vim — and it disappears the moment you move on. You never ask for it. It’s just there when you pause, which is exactly when you wanted it.

It reads the literals real code contains, digit separators and size suffixes included, and it has the sense to stay quiet about identifiers that merely happen to have digits in them. The old <Leader>b operator is still there for when you want the full tool on a whole expression rather than a glance at one number.
That’s the plugin I should have written in 2019. It mostly took looking at it again with fresh eyes, and by this summer the eyes weren’t only mine.
Then the fun part. It turned out &, ^ and | had had the wrong precedence relative to each other for the entire life of the project. Nobody had reported it. Now they match C, which means an expression you lift straight out of a source file evaluates to the same thing in bitwise as it does in your compiler — which was always the point.
And I finally implemented this:
Verilog bit select. The field comes back right-aligned, the way a datasheet means it, instead of making you work out (0xdeadbeef >> 16) & 0xffff by hand. It’s the one place where the syntax deliberately isn’t C’s, and for a tool whose whole purpose is decoding registers it’s obviously correct.
Somebody requested that three and a half years ago, in a two-line issue with no explanation, and then waited. They were right, too.
What I’d tell myself at the start
Almost nobody gets this program from me. The release tarballs on GitHub are downloaded in the thousands; the distributions serve far more people than that, quietly, without ever telling me. That’s the goal — and it’s also exactly why the lag between fixing something and shipping it is the metric that matters.
So: build the thing for yourself first and keep using it, because the ten weeks when nobody was watching are what made it worth watching. Take the bug reports from strangers as the gift they are. Say yes to README pull requests from people packaging you for distributions you’ve never run.
And be patient with the last mile. The code is the part you control; getting it onto other people’s machines is a slower, more human process, and it keeps going long after the interesting problems are solved.
bitwise is on GitHub, GPL-3.0-or-later. apt install bitwise, dnf install bitwise, brew install bitwise — and if you’re on a stable release, 0.70 will reach you eventually.