A while back I posted about building EmailViews, my native email client for the Haiku operating system, with no prior coding experience. That post was mostly about what I built. This one is about how I’ve been working, because the more interesting story, at least to me, is how much my workflow has changed since then as I learn more about the code base and how Claude works best.
From “code it for me” to “show me the diff first”
When I started, my workflow was embarrassingly simple: “I want this, code it for me.” I didn’t look at the code at all (what for? I would not understand it anyway!). Claude would write it, I’d compile it, and if it worked, great. I was a passenger.
As the code base reached a certain size, and the app’s feature set settled, I was doing more debugging or enhancements on smaller and more focused parts of the code. This brought a single biggest change in my workflow: I started asking Claude Code to show me the diffs before writing anything to the local repo (yeah, I know, what was I waiting for?). This actually made a bigger difference than I had thought.
Reviewing diffs forced me to actually read the code. Reading the code made me familiar with it. I would notice a class name that didn’t match the one three files over, or an approach that smelled off, and I’d stop and ask: is this right? Can we rename this for consistency? Why are we doing it this way?
Familiarity made me inquisitive. I went from passenger to, if not quite the driver, at least someone with hands on the wheel. And catching mistakes stopped being luck.
The great refactoring
Once I started actually reading the code, I couldn’t ignore the inconsistencies. EmailViews is really four codebases stitched together: the original UI app, the email reader/composer (a fork of Haiku’s Mail app), a small fork of Haiku’s mail daemon, an original libetpan-based IMAP daemon, and an HTML preview that uses litehtml as the backend. Each came with its own conventions, and the seams showed — filenames, class names, things living in the wrong place.
So I spent a good amount of time refactoring. Normalizing names. Moving things to where they logically belonged: the email preferences, for instance, started life in the reader (from Haiku’s Mail app), but as they grew into app-wide settings, they migrated to the main app. The account creation/configuration UI was moved from the imap_daemon, which is a separate process, to the client itself. We also moved the app’s configuration from flat plain-text files to flattened BMessages (I thought that migration had gone cleanly, but not quite; more on that below).
Cleaning up the slop
Later I found myself in a very long debug session trying to figure out why the reader wasn’t saving the “From” attribute on draft emails. The symptom looked like a drafting problem. The cause was something else entirely, though: a single configuration call in the reader — reading the account’s from-address when saving an draft email — had never been migrated to the new flattened BMessage scheme, so it was coming up empty. One straggler, out of an otherwise clean sweep.
The lesson stuck with me. Diff review is fantastic for targeted changes — you can eyeball every line. But a big mechanical migration across dozens of files produces diffs too large to truly read, and that’s exactly where the one you miss hides, only to surface much later as a symptom that looks unrelated to the cause. For sweeps like that, eyeballing isn’t enough; you want the model to enumerate every call site up front, and you grep to confirm none slipped through.
A brief “tango” with Fable (bear with me, I am from Argentina)
Now the part I was most curious to try. EmailViews has a custom, file-attribute-based list view (EmailListView) that Claude originally built for me to keep the UI smooth even when a query view holds tens of thousands of emails — the equivalent of a mail folder. We developed it as a separate test-bed app first, then folded it in to replace Haiku’s BColumnListView, which just wasn’t performant enough at that scale.
It worked well. But I’d read in so many places how strong Fable was, so I decided to run the list-view code through it to see if it could find performance wins. It could, and it did.
There was a wrinkle: my Claude Code is pinned to an older build to run on Haiku, so I couldn’t drive Fable from inside it. So I went to Claude’s web UI, uploaded the email list view source files, and had Fable review them. It came back with a structured, prioritized task list — correctness bugs first, then incremental performance work, then the bigger architectural wins — each task with the file, the problem, the proposed fix, and an acceptance test.
Then came the fun part. I fed that list to Claude Code (on Opus 4.6) and had it implement one task at a time, while Fable verified each step Claude proposed and corrected it when needed. One model as architect and reviewer, the other as implementer, with me orchestrating the loop. The result was a meaningfully faster list view — de-duplicated loader batches, a unified sort comparator that killed a visible reshuffle bug, a batched merge-insertion path instead of per-item inserts, and a few deadlock and redraw-throttle fixes along the way.
Unfortunately, Fable is currently unavailable, so I had to park a couple of the bigger tasks on that until it’s back. But to be honest, I feel the gains already achieved in UI responsiveness and smoothness are more than enough.
Where I’ve landed
In my previous post I described Claude as a collaborator, not an oracle. I still believe that, but the picture has gotten richer. It’s not one collaborator anymore — it’s a small team I direct: one model proposing and reviewing architecture, another implementing, and me reading every diff, asking the annoying questions, and making the calls. The tools got better over these months, sure. But the bigger change was me learning to actually steer.
For those interested in EmailViews, I still need to tie up some loose ends so it may take a few more weeks before release. I will make a call for testers before then, so keep an I eye on this forum for news.