I build software, and I build the tools that build the software.
I'm Kevin Aspesi — a software engineer working across iOS/Swift, TypeScript agents on LangGraph, and the agentic-dev tooling that surrounds Claude Code. I ship things, and when the thing I need to ship them doesn't exist yet, I build that too.
What I do
ios
Most of my product work is native iOS. On The Grid is a SwiftUI app in the App Store for discovering and saving places worth going — save a spot straight from an Instagram or TikTok link, and meet people nearby who saved the same places. It's a proprietary product, so I'll leave the internals off the page; it's the project that taught me most of what I know about the gap between a demo and something people open on a Tuesday.
agents
The other half is agents. Otto is a LangGraph.js agent I built for a car-buying service that walks a shopper from their first message to a qualified sales lead — extracting details as they talk, pricing real deals, and handing the lead to a CRM. Building it turned out to be less about prompting and more about conversation state: a graph that pulls what it can from each message, routes on intent, and only asks for what's still missing.
tooling
Around both of those sits a layer of my own tooling — worktree-per-agent setup scripts, a self-review pipeline where two independent reviewers and a judge argue about a diff before I read it, and enough shell glue to make all of it one command. Roughly a third of my week goes into tools nobody asked for. It's the highest-leverage third.
What I actually think
Test infrastructure should be boring
Clever test frameworks rot faster than the code they test. I'd rather have flat fixtures, assertions on real values, and one slow end-to-end suite that fails honestly than a beautiful abstraction nobody can debug at 11pm. A test that would still pass if you deleted the feature is not a test; it's a comment with a run time.
An agent must cite its evidence
The single biggest quality jump in my agent work came from refusing to accept a claim without a file path and a line number. Make the model quote the code it's talking about and most confident hallucinations die on contact — because it has to go look, and looking is where the mistake surfaces. An answer I can't check is a liability no matter how good it reads.
Ship it, then make it good
Polish before contact with a user is a guess. I'd rather put a rough version in front of ten people and learn which half of my assumptions were wrong than spend three more weeks perfecting the half that gets deleted. The corollary, which I hold just as hard: once it's shipped and being used, you owe it real maintenance. Shipping fast is not permission to leave a mess.
Write the failure modes down
Every post on this site includes what didn't work. Not out of modesty — the failure modes are the transferable part. Anyone can reproduce the happy path from a README; what saves you a weekend is knowing which of the four obvious approaches quietly falls apart at scale, and why.
How I work
Outside the terminal
offline
I cook a lot, mostly things that take longer than they need to — a proper ragù on a Sunday, bread that I've finally stopped over-proofing. I run slowly and consistently and have no interest in getting faster. I'm working through a stack of secondhand paperbacks that grows faster than I read it, and I still play far too much of one particular strategy game whose name I will defend in person but not in writing.
If we've met at a meetup and I cornered you about why every good restaurant rec is trapped in a screenshot, or why your CI is slow: sorry, and also, I was right.
Elsewhere
- writing ~/aspesi.dev/writing — posts, with the failure modes left in
- projects ~/aspesi.dev/projects — what I've shipped, ranked
- github github.com/kaspesi — source for most of the above
- email kevin@aspesi.dev — I answer