Skip to content

← Writing

Treating a personal tool like production code

Career OS is a job tracker only I use. I still gave its Go backend the same layered structure I'd defend in a code review — here's why that wasn't overkill.

Most personal tools are throwaway. A script that runs once, a notebook nobody opens again, a “v1” that stays v1 forever because nobody but you ever has to maintain it. When I decided to replace my job-application spreadsheet with real software, that instinct was right there: it’s for one person, so why not just wire up the fastest thing that works?

I didn’t do that. And the reason wasn’t discipline for its own sake — it was Go.

The reason to pick Go

I’d been meaning to learn Go properly for a while — not read the syntax, but write it the way a Go engineer would, in something real enough that sloppy code would actually cost me something later. Go keeps showing up as a language worth having for the market I’m targeting, and “make something work” isn’t the same skill as “write idiomatic Go.” A throwaway CRUD script wouldn’t have taught me the difference. A real application, one I’d keep opening every day, would.

That single decision — build this to actually learn Go, not to hit a deadline — is what pulled the rest of the architecture along with it.

Why the backend isn’t a thin CRUD wrapper

Every entity in the app — job applications, interview rounds, leads — follows the same four-file shape: struct definitions, raw queries, validation and side effects, and the thin methods the frontend actually calls. That’s more ceremony than a personal tool strictly needs. A single file with a dozen functions would have shipped the same features.

I split it anyway, because the split is the point. If a rule like “company name is required” or “auto-update the last-touched date when status changes” lives apart from the query code, I can look at the file that owns that rule without also reading SQL. That’s not a nice-to-have for a team of one — it’s the difference between code I can still reason about after not touching it for three weeks, and code I have to re-read top to bottom every time I come back.

Go rewards that kind of explicitness in a way that made the discipline easy to keep. There’s no framework magic hiding where a request actually goes. The path from “the frontend called this” to “this row changed” is a straight line you can read.

Wails, and the bet that came with it

The frontend still ships as React and TypeScript — that part of the stack I already knew. What changed is what it runs inside. Wails takes that same React app and binds it directly to Go methods: no REST layer, no endpoints to design, no fetch calls. The frontend calls a Go function like it’s a local async function.

That choice came with a real cost. Wails was pre-1.0 when I picked it — not the “latest and boring” option. I pinned to a specific release instead of tracking the newest one, and accepted that a breaking API change was a live possibility for the whole build. What it bought back was worth the bet: one native binary, about 7.6 MB, no bundled browser runtime sitting underneath it. No Electron tax.

SQLite came with a matching bet — a pure-Go driver instead of the more common C-backed one, so the entire application, backend and frontend and database engine together, compiles into one file that just runs. No C toolchain to install alongside it, no gap between the dev environment and the thing I hand to myself as an installer.

Whether it was worth it

I use this app every day. That’s the actual test a personal tool has to pass, and it’s a harder one than “did it ship.” Months from now, when I want to add a feature or fix something that’s bothering me, I want to open the file that owns that behavior and change exactly that — not untangle a script that only made sense the day I wrote it.

Building it like production code didn’t slow the project down. It’s the reason I still want to keep working on it.


Career OS is a fully offline desktop app for tracking a job search and freelance leads, built solo in Go and React. The full build is at /work/career-os.