Construction project management — a Next.js dashboard, an Expo field app for crews, Stripe and ACH payments, and a Python OCR service that reads supplier invoices.
Native apps
Shipped iOS work, not React Native wrapped in a shell — SwiftUI, widgets, watchOS, StoreKit, and the App Store review process end to end.
Apple Pencil handwriting turned into text and live sessions transcribed and summarized; AI answers grounded in the official Rules of Golf, with an offline database for when the signal drops.
Internal tools
The unglamorous software that decides whether a week takes four hours or forty. Usually document-heavy, usually replacing a spreadsheet and a lot of re-keying.
Deerman Sales
Purchase orders arrive as PDFs, get parsed into line items, matched against manufacturer price files, and go back out as vendor confirmations — with a human in the loop where it counts.
How it goes
1 / 7
Platforms
BuilderHelp
A construction platform that had to work in the office and on a jobsite with one bar of signal.
The problem
A builder's software has to serve people whose jobs have almost nothing in common. The office needs invoicing, change orders, subcontractor payments, RFIs, submittals, and budget tracking — dense, detail-heavy screens. The homeowner needs a calm view of progress and a way to approve things without learning construction vocabulary. The field crew needs to photograph a receipt with cold hands and get back to work.
What we built
The web app is the system of record — projects, invoicing, the owner portal, and staff admin, built on Next.js with MongoDB behind it. It owns the API that everything else talks to.
The hard part
Payments were the least forgiving part of the build. Subcontractor payments run through Stripe with both cards and ACH via Financial Connections, and the system carries a second provider as an alternative path — because a payments integration you can't swap is a business risk, not just a technical one.
Handwriting on an iPad, turned into searchable text — and a sermon, transcribed while you take notes on it.
The problem
People who study seriously write by hand. It's slower, and that's the point — it forces you to compress an idea before committing it. But handwritten notes are a dead end: you can't search them, you can't pull a thread across three years of notebooks, and you can't line them up against what the speaker actually said.
What we built
Notes are captured with PencilKit and recognized with Vision's handwriting OCR, so the stroke data stays authoritative and the text is a derived, searchable index of it. You never lose what you actually wrote.
The hard part
The app is used in rooms with bad wifi by people who don't want to make an account before they can write something down. So the offline path isn't a fallback, it's the default: SwiftData caches everything locally, and Supabase auth starts anonymous and upgrades later if the person wants sync.
Purchase orders arrive as PDFs. Staff re-keyed every line by hand, then checked each price against a book that changes quarterly.
The problem
The manual process wasn't just slow, it was silently lossy. A part number mistyped by one character, or a price checked against last quarter's file, produced an order that looked completely normal and went out the door. The mistake surfaced when an invoice didn't match — weeks later, when unwinding it meant a phone call to a customer and a credit.
What we built
A PO is uploaded and run through an AI PDF parser that returns structured line items. Those get normalized, then matched against the manufacturer's price database — both the general catalog and the active price list, which are separate sources with different key fields.
The hard part
Every vendor's PDF is different, and the parser's field naming is non-deterministic — the real part code turns up under a different key depending on the document. Beyond that, the failure modes multiply: the code leads the description instead of sitting in its own column; the code is buried mid-description; OCR turns zeros into the letter O; the part is legitimately all-numeric and looks like a stock number; quantity and price columns are swapped.
A golf rules assistant that cites the rule it's answering from, and still answers when the course has no signal.
The problem
The situation this app exists for is a disagreement. Two players standing over a ball in an awkward place, each half-remembering a rule, neither willing to concede to the other's memory. Nobody in that standoff is persuaded by a confident paragraph from a chatbot. They're persuaded by a rule number they can look up, because it's the only thing in the exchange that isn't somebody's opinion.
What we built
The Rules of Golf is unusually well suited to retrieval, because it already has the structure you would have wanted to impose on it. Numbered rules, sub-rules, lettered clauses, each one a self-contained procedure with a stable identifier. Most retrieval pipelines throw that away and split the source into fixed-size chunks, which slices a procedure in half, strands the heading in whichever chunk happened to catch it, and leaves the model to reattach a number to a passage after the fact.
The hard part
Golf courses have famously bad reception. They're large, wooded, often rural, and frequently built into exactly the terrain that blocks a signal. The place where a rules question occurs is, more often than not, a place with no bars. Treating the network path as the real product and the offline path as a courtesy would have inverted the actual usage.
Everyone secretly vetoes what they won't tolerate — and the database, not the app, is what keeps those vetoes secret.
The problem
A poll in a group chat asks what people want, and what comes back is a spread of first choices nobody can turn into a decision. Four options, four votes, and now you're relitigating it. Worse, the format rewards whoever is loudest and quickest — the first reply anchors everyone else, and the person with the strongest opinion about Thai food wins by default.
What we built
With friends, one person hosts a named round and shares a code or an iMessage link. Everyone joins from their own phone and options get added collaboratively in a lobby, which matters more than it sounds — half the value is discovering that somebody already had a suggestion nobody would have volunteered. Then the round moves to the veto step, everyone marks in secret, and the host reveals.
The hard part
Individual vetoes are the sensitive data in this system. If one leaks, the app isn't degraded, it's finished — the whole premise is that you can mark something honestly without it becoming a conversation. So the enforcement lives in the database, where a client bug can't reach it.
A palate log that has to survive a dark, loud bar — and private club rooms where the verdicts get argued out.
The problem
The context matters more than it seems from a desk. A bar is dark, it's loud, you're standing, one hand is holding a glass, and the person across from you is mid-sentence. You have a few seconds of socially acceptable phone time before you become the person on their phone.
What we built
Capture opens straight into the thing you need: what it is and how it landed. Name the pour, mark the reaction, done — that's a complete entry, and the app treats it as complete rather than nagging about empty fields. Everything else lives on the same screen, further down, optional, reachable without switching modes or opening a second sheet. Nothing blocks on a lookup, either; if a bottle isn't recognized, the name you typed is the name, and it can be reconciled later.
The hard part
Speakeasy rooms are closed groups: a set of people, a shared log of what's being poured, and a place to pin the verdicts worth arguing about. No public feed, no discovery, no followers. That sounds like a smaller version of a social app, and it isn't — it's a different object with a different set of problems.
One site serving a family buying a cup and a clinician evaluating a medical device — two audiences who want opposite things.
The problem
The first visitor is a person with dysphagia, or more often the family member helping them. They arrive worried, they aren't shopping in any leisurely sense, and what they need is to understand quickly what the cup does, whether it fits their situation, and how to get one. Clarity, and a short path to a purchase.
What we built
The commerce path is short and unambiguous — what the cup does, who it's for, how it works, and a way to buy it. Shopify owns the cart and checkout. The product content, the photography, and the explanation live in Sanity, so the storefront reads as one site even though the transaction happens on somebody else's infrastructure.
The hard part
There's a persistent pull toward owning the entire experience: custom cart, custom checkout, everything on your own domain, pixel control end to end. It's technically straightforward and it was the wrong call here.