Porting NEXTSPACE to FreeBSD: A Seven-Phase Journey
- 8 minutes read - 1654 wordsThis is Part 2 of a three-part series on my 2025 obsession with NeXTSTEP, OpenStep, and NEXTSPACE. In Part 1, I introduced the project and what drove me to port NEXTSPACE to FreeBSD. Here, I’ll walk through the technical journey itself.
The Slow-Boiled Frog
We do these things not because they are easy, but because we thought they would be easy.
Attributed to Maciej Ceglowski
Starting Off with Claude
As of this summer, I had been trying various new laptops and putting them through their paces as far as being my new “Just Focus” machine. While testing various hardware platforms for FreeBSD support, I started leaning on Claude’s chatbot to get information about how to get diagnostics or correct bugs as I was getting oriented in the platform. Claude’s responses were largely correct and allowed me, in my small sessions to make progress.
While I was comfortable with my Wayland + Sway desktop environment, I started to want a lightweight, but also helpful desktop—something that could set my clock, change system volume, pick a screen saver. But I also wanted the interfaces to be unobtrusive and easily customized (eventually) for FreeBSD. What I really wanted was that NeXT experience.
While I was looking for that, I came across Sergeii’s NEXTSPACE…for Linux. I was struck by the quality and clarity of Sergeii’s codebase and figured that it couldn’t be that hard to port an awesome Linux application to FreeBSD ("because we thought they would be easy").
Phase I: Installer shell scripts
In this initial phase, I was using my knowledge of shell scripting and Claude’s web chat to migrate and debug NEXTSPACE’s installer shell scripts. This was very much the style of development I’ve done for years. I edit something, I keep my wiki and reference documentation handy. Effectively, the locus of knowledge is my head (or notes). But with the help of AI, I was able to remember long-forgotten patterns of string testing, return status checking, etc. I didn’t have to keep documentation open to make tiny changes
Additionally, Sergeii had written the scripts in bash shell and FreeBSD
prefers the portable sh shell. Again, Claude chat came to the rescue here to
port the idioms of one standard to another. That was pretty great, it was very
empowering, and it was work that I could accomplish in the duration of a
toddler’s nap.
Phase II: Embrace the hoosegow: jail-based development
As I’ve written several installers for Unix systems, I found that one critical problem gets in the way like none other: given a pristine machine, your first (failed) attempt to install will mean you can’t truly test your code on a clean machine again. You spend as much time trying to get back to guaranteed clean as you spent trying to change the system state to a new direction. Fortunately, FreeBSD provides jails. These are basically isolated file trees on your “host” FreeBSD that look like a Unix system. In short, they start off as pristine virtual machines. If I could get the code building and running in the jail, I could throw it away. Or if I got something wrong, it wouldn’t hurt my host (main laptop) system.
Phase III: Snapshots
The most common file-system used in FreeBSD is zfs (zed-eff-ess) which
features a really powerful feature: you can snapshot file system trees and name
them. In short, you can make “savepoints” in the evolution of a BSD jail.
Phase IV: Peanut butter meets jelly
Hopefully you’re excited by imagining what’s possible with the tools I just laid out.
So the workflow I discovered was: edit code on my host operating system. Share
that file tree into the file tree of the jail. Snapshot the jail. Edit / Debug
/ Test. Roll back to the pristine snapshot. Try again. When the installer
script worked correctly, I snapshotted the system e.g. after-script-2 to save
my work and then keep iterating. This was a powerful workflow that anyone using
AI really ought to leverage.
Phase V: Chase the Bugs
Once I got this development harness in place, I could dive into the real work of making sure these installing scripts did the right thing. And here’s where Claude really started to show value. You see, the NEXTSPACE codebase is divided into a number of applications written in Objective-C. It’s the language I chased many years ago while learning to program. I’ve never shipped anything of size in it.
It’s also the case that the window manager was written in C. This is a language that I have an understanding of, but no real comfort. I’ve never shipped anything of size in it.
And it just so happens that both of these codebases inside NEXTSPACE were
assembled by GNU Make. Now I’m not even that comfortable with regular BSD
Make that I’ve been, uh, using but not really understanding for decades. So
here’s a different dialect (say, Italian to Spanish) that I had never even
worked with.
And recall, all that is on top of shell scripting which I, by chance, happen to be decent at.
These areas of familiarity shortfall should have cost me lots of time. It
should have made this project impossible. But it didn’t. Why? Because I
had search results better than Google; I had a peer that could help me parse
dense man pages and generate instructive examples; I had something that could
take GNU documentation out of their bat-shit preferred documentation format
.info; I had Claude.
Slowly, by chat, by edit, by commit, by rollback, by roll-forward, I kept falling forward to success.
Phase VI: Integration is the Hard Part
While this human “stayed in the loop” the whole time, once all the pieces were installed, there were some real snags. Many of them had to do with library dependence order, installing fonts and handling the eventing queue. By mid-August, I had a working proof of concept.
First flickers of success
It took another 10 weeks or so, but eventually I got some of the hardest bugs worked out and got to this milestone.
All the applications running on a single thread
I thought I was pretty much home free at this point. But then I realized I’d made an error: I had all the work happening on a single thread. This meant that certain applications weren’t going to work. That is if I dragged a file into a recycling bin on the main user interface thread, the background thread that actually does the removal wouldn’t run. I found that I’d disabled this in development to keep moving forward. Whoops. So turning it back on shouldn’t be a big deal…but, oh, I was wrong. So wrong.
Phase VII: The Demons of Multithreading
And that’s when I started having multithreading bugs. As a class of bugs, these
are some of the hardest to battle because news of what failed and when it
failed not order may in the correct arrive – er may not arrive in the
correct order. Honestly, I was completely despairing at this point.
Under the old model of development and support, I was going to have to browse a thousand condescending forums and email lists. I was going to have to, I figured, go back and learn those three giant areas of knowledge so that I could have enough context and kudos to be able to ask for help. It was really dispiriting. I think my mental state comes through in this update:
WindowMaker [the underlying desktop package NEXTSPACE rests on] is from a single-threaded era and, in a number of points it tries to flush changes via X (Xlib, XCB, whatever). It doesn’t know that another process in another thread may have done something that it doesn’t know how to deal with.
I don’t know how this wasn’t a problem on Linux. And for whatever reason, in my prior hack-it-together approach, I somehow didn’t hit thread errors. Maybe I installed something wrong.
Interlude: Claude Code
In May, Anthropic released Claude Code. It was a text-based user interface
(yay) application that one could, in its earliest days, use to process output
and introspect into files. It cut down the non-ergonomic experience of running
code, compiling, seeing errors, copy and pasting into a browser, seeing
results, implemeting them while reading the web browser, and then retrying. I
started working with Claude Code and started realizing it could be a force
multiplier. Running it in my source code root, I simply said: “I think I have a
multithreading bug in this file, add print messages everywhere so I can see
what’s going on.” While adding tons of these isn’t hard for a human, it’s
certainly not fun. Claude made the execution path chatty like a preschool. It
made it non-painful for me to start accumulating data.
Having accumulated the data, I realized it was a really hard problem. I posted this update. It was a cry for help but also the moment before I started leaning even more on Claude Code. I was so close, but I just was not going to have the time to level up in all the required disciplines necessary in order to be able to make this work before the new year.
But helped by Claude Code, having it instrument all the code paths, having it build solutions and make edits (a step toward “agentic” or “autonomous” code patterns) I had a release ready. Roughly a week later, I had a developer release ready.
NEXTSPACE running on FreeBSD
Before the month was out, Sergeii chimed in to recognize my effort and even used my codebase to try running his creation on FreeBSD as well. He’s been exceedingly gracious and we’re going to see if we can merge my work with his architecture to have Linux and FreeBSD as supported platforms!
In Part 3, I reflect on the broader implications of AI-assisted development and make the positive case for this new way of working.