The Build Was Never the Problem
Field notes from six days, a rebuilt garage, and golf data that kept lying to me.
I fell into the golf simulator world about seven years ago the way most people do: a hitting mat and a net, then a projector aimed at a screen that was never quite square. When we moved a couple of years ago, I finally got to do it properly: ceiling raised, garage conditioned, custom turf, a sixteen-by-nine screen maxed to within an inch of what the framing allowed. The family negotiation took less time than you would think, because the room doubles as a movie theater. The kids have logged more hours in there than I have, and lately they are starting to swing clubs with me. We are not car people; nobody misses the parking.
If you have never used one, a golf simulator is essentially a video game you play with real clubs. You hit a real ball into the screen, a launch monitor reads the strike, and physics does the rest. After the rebuild, I started playing on the Simulator Golf Tour: same courses, real people, real standings. It woke up a competitive streak that solo practice never touched.
It also created a problem. In a garage, you can hit five times the balls you would ever hit at a range, which means you can groove a bad habit five times as fast. A whole chorus in golf says simulator gains are suspect anyway: you hit down differently off a mat, the misses do not punish you, and none of it survives grass. On the real course, sensors on my clubs have made me measurably better; the garage had nothing like that. GSPro, the software that runs my screen, exports every shot to a CSV, and up until this summer I had never done anything with it. I played by feel, and I remembered my good days.
I wanted to know two things. Am I getting better, and will better in the garage survive contact with grass? (This stops being a golf story shortly.)
A market of one
The first week of August we were at the beach with the family. Vacation, for me, has always come with a side project, and this year my side project was an app. I spent about three hours a day on it, mostly late into the night after everyone else was asleep. My wife, the first person to read this piece, said the word obsessive out loud. Fair.
The customer problem was mine, which means I built for a market of one. That is the first thing product people are taught not to do, and I did it on purpose. Normally this is the moment for the sizing ritual: TAM, SAM, SOM, how big the market is, how much you could plausibly serve, which beachhead you go win first. I have built that deck many times, and I still believe in it. This time the market was me, the beachhead was my garage, and the honest label was a passion project with a hypothesis attached. That simulator golf can effectively improve your golf game.
I am a data geek; I know plenty of golfers and plenty of operators who think in numbers the way I do, and I wanted a live read on what building with AI agents is actually like right now. I started my career in software engineering; C was my first language, and I moved to the product side early and mostly stayed there. In early 2025, I built a few small things on Lovable, a tool that turns a prompt into a working web app. It was easy in a way that told me less than it seemed, because the tool did most of the deciding. This time I wanted the frontier models, the deciding on me, a single week, and a data-heavy project instead of a demo. The repo’s own record says it plainly; one of my first prompts reads, “it’s also just a way for me to learn how to build an app with Claude.”
The simulator tools I had are essentially a bank statement for your swing: every transaction listed, no advice. I wanted the other thing. Given my goal, what should I practice, and is it working?
What the data was hiding
The build was supposed to be a quick proof of concept: parse the exports, show my scoring, prove the pipeline. The exports had other plans. By the end of the week, the app was ingesting five different file formats, one of which is a saved web page, because that was the only place a number I needed lived.
A sample of what was in there, each with a commit dated to the hour I ran into it.
The first data issue that was flagged involved the tournament export, which has a column that indicates the club you hit. Unfortunately, every row says DRV. Driver. Every approach, every wedge, every putt, filed as driver. The column exists, it is populated, it passes every type and null check, and it means nothing. The same file hides a second lie: the export is cumulative, each new file repeating everything the previous one had, so the 717 shots I actually hit arrive listed as 1,719 rows.
The second issue was that the distance column changes units row by row, with the unit buried in the value instead of declared for the column: a putt logged as 27.18 ft, an approach logged as 22.91 yds, same field. It is text pretending to be a measurement. Average it and you get a number that renders beautifully and means nothing. My favorite: ninety-two rows turned out to be gimmes, the putt so short your friends wave it off, cloned into the data as measured shots, each copying the full flight telemetry of the stroke before it. One of them logs a conceded putt as a 131-yard tap-in. The actual concession was 1.4 feet. And three of my first seven rounds carried the wrong date entirely, which taught me a rule I did not expect to need: a calendar date is not a round. Two rounds can share an evening, and one round can cross midnight.
Then the one that got past everything. Face to path, the number that says whether the clubface was open or closed to the swing, is about the most diagnostic thing a launch monitor reports, and one export stores it with the sign flipped. The app inherited the flip, and for most of a week, a face eight degrees closed displayed as eight degrees open. Nobody catches that by eye; a wrong sign just reads as a shot shape. What should have caught it was the verification suite the agents had built: 1,617 comparisons, all green, for five and a half days. It stayed green because the face-to-path checks took their answer key from the same flipped column they were checking, and a check like that cannot fail. Worse: it manufactured a discovery, a subtle inconsistency between two columns, 228 of 231 rows in support. The build log’s retraction: “The figure ‘228 of 231’ is exactly the kind of number this project trusts: measured, precise, with the sample size attached. It was an artifact throughout.” Every type and bound was correct. The defect lived in what the numbers meant, and no schema expresses meaning.
The parsers accepted every one of these records without complaint. A human caught them, looking at a number and knowing it could not be true. For example, a human would know that a driver on every putt is suspicious. Or in a business context, the learned instinct from seeing years of operating data is that two valid-looking records cannot both describe the same event. Underneath the facade of confidence is the unglamorous point. Every company I have worked with, from seed stage to private-equity-owned, runs on disparate systems that disagree in exactly these ways, and every one would rather talk about the AI than the plumbing under it.
An agent can point at data it doesn't understand and be confidently wrong while still being completely convincing; mine would have averaged fake putts without one complaint. The data work turned out to be most of the project. That lesson travels.
The weeds
I got lost in there too. This part is on me.
Six days, 112 commits. The schema changed eight times; six were new tables for new data arriving, and the two that reshaped existing tables were both me misunderstanding my own golf. The best one: the data model assumed a round meant a tournament round, so when I played a casual short-format round on a Tuesday night, the app had nowhere to put it. I had built a definition of golf that excluded the golf I actually play. Two migrations also shipped broken; both announced themselves and were fixed inside an hour, and the hour matters later. I built an entire page at 10:41 on a Tuesday night and deleted it Saturday afternoon, 139 lines, gone without ceremony. I declared two fields required and made them optional nine minutes later, with its own little migration as the receipt.
And then the code reviews. The loop I ran: one company’s model writes the code, a rival’s model tries to tear it apart, alternate. Claude Code, running Claude Opus 5, built nearly everything; OpenAI’s Codex, running GPT-5.6 Sol at its highest setting, tore it apart. It works. It also worked too well. Three consecutive passes on one module found 13 defects, then 12 more, then 10 more, and for a week I told that story as the reviewer that never runs dry. The log says something more interesting: the module nearly doubled across those three reviews, and every added line was repair code answering the previous round’s findings. The well never ran dry because I kept refilling it. Every finding sounded plausible; most were edge cases I will never hit in my garage. By then the verification scripts had grown to nearly half the size of the application they verify, and the face-to-path story says what can live inside a check.
I have spent a decade in sprint planning meetings on the other side of this exact conversation: an engineer enumerating edge cases, correctly, while the room tries to ship. First as the product manager in the room, eventually as the person answering for what the room decided. This week I was both people at once, and I ran the meeting the way I always have; three review passes became two, then one. Part of the spiral was my own prompt, which said to tear the module apart and never said what failure mattered in a one-user prototype. The agents don't know what stage you are at unless you carry that judgment with you; the default posture reads a six-day-old hobby app the way it would read a bank. The most valuable review I ran all week was an adversarial pass on the product plan, before any code existed. Plan first, then build. Same as it ever was, except the loop now runs in hours instead of quarters.
Where the hard part went
Every failure that cost me anything sat a layer above the last. The data lied. Then the check I built to catch the data manufactured its own finding. Then two independent checks agreed exactly, and agreed because they had inherited the same mistake. The hard part never left. I had been looking for it where it used to be.
While writing this piece, I checked the log. I would have told you, flatly and in good faith, that I started building on Wednesday. The first commit is Monday, August 3, at 2:44 p.m. I lost two days somewhere, the way a golfer forgets three bad approach shots and keeps the perfect one putt. A second layer: an earlier version of this sentence said 3:44, because that is the clock the log happens to store, New York’s, and I live in Texas. The correction needed correcting. My memory of the most documented week of my year is wrong, and I can prove it, because for once there was an instrument running. The data lies, and memory lies worse. That, more than any industry argument, is why I wanted the thing to exist.

So, the claim, plainly. Almost nothing that went wrong in six days went wrong at the build step, and the record makes me keep the almost: those two broken migrations, both loud, both fixed within the hour. A build error announces itself. The expensive failures were the quiet ones, and every quiet failure was upstream: what the data meant, which defects mattered, what stage the product was at, what I actually wanted it to do. Those lived for days, and the worst one survived until the day before publication. The agents own more and more of the stretch between decision and working software, turning any assumption of mine into working software faster than I could find out whether the assumption was true. By Saturday night there were about 11,600 lines of application TypeScript and another 5,400 of verification scripts. I wrote very little of either, though the repository does not track per-line authorship.
None of this is abstract for me. The product teams I work alongside are figuring out how to build this way in real time, and boards and funds keep telling management teams to go build with AI; fair enough, I say it too. Up close, on a low-stakes week, the building was nearly effortless, and judgment was the whole job. The load did not shrink; it moved. I have seen the same pattern at a seed-stage startup and inside private equity portfolio companies; only the stakes and the units change. There is a longer argument for later pieces about who gets to work this way now that a working prototype costs a hobby budget; I run a version of that play in my day job. The garage is just where I get to publish the mistakes.
Nine days later
I drafted this piece on August 9, and it ended with the app as it stood the day before: it ran on my machine and nowhere else, held 46 rounds and 28 practice sessions, and still couldn't tell me whether I was getting better. The plan was a five-session pilot on myself. Nine days later, on the 18th, I ran the fact-check: agents from both companies, pointed at the repo, told to verify every number in the draft and to distrust each other. The interval had news, and so did the check.
The app can now try to answer the question. I kept building after the beach. A longitudinal layer shipped on the 15th and 16th: it watches every club-and-metric series, and when something moves it nominates one thing to work on, with the baseline registered in advance so the evidence that raised the flag cannot also grade the result. Its first product behavior was refusal. As I write this, it reports no series nominated, most not yet testable, and declines to say more. The refusal is the product working. My instruction to the agents sits verbatim in the record: “I need this app to be as reluctant to tell me I’m getting worse as it is to tell me I’m getting better,” with “not enough data to say” a first-class result rather than a fallback.

The interval also fixed a product mistake I should have caught sooner. The first version tracked what the launch monitor measures: club path, face-to-path, attack angle. Those settle fast; a handful of swings gives you a reading, while the score I actually care about takes months and is confounded by everything. Managing to the fast input metric is usually the right instinct, because a number that arrives a quarter late arrives too late to act on. The mistake was subtler. Nothing had established that these particular fast numbers moved anything I was buying. Mechanics take weeks to train; nobody steers a club mid-swing. Dispersion, distance, and repeatability arrive on the same shot and sit a rung closer to a score: closer to where I aimed, at the distance I planned, again and again. Every swing number now answers to those three. The launch monitor measures swings. I am not buying a swing; I am buying a better score.
The interval’s other product decision changed how I will work. The five-session pilot on myself is canceled. At a two-sided marketplace I helped build, we said every quarter that we needed a real demo environment; one side was easy to show, but we could never stage the other convincingly, and every sprint meant to fix it lost out to something a customer was waiting on. I understood why then and I understand now: a startup builds what survives the runway. I no longer think the demo environment is what you cut, because I just lived what it buys.
My own history could not test my own product. One mid-handicap golfer’s data cannot say whether the guidance holds for a scratch player, a high handicapper, or somebody quietly getting worse. So I invented them: twelve synthetic golfers, each with an authored truth, written as byte-faithful export files and run through the same parsers as my real data. One improves on schedule. One holds flat as a control. Two exist only to catch the app manufacturing a trend out of a change in what got practiced rather than how well. Some screenshots in this piece are those invented golfers, labeled, because my real data cannot honestly produce a known answer yet.
And what ships today is deterministic on purpose: thresholds and refusals I can explain; the AI layer comes later, to see what a rule cannot, landing on data I have already caught lying and fixed. The other order gets you confident nonsense at scale.


Then the accounting, which is where this piece was always going to end, though not how I planned.
The whole point is that finding out got cheap, so here is what finding out cost. Six days, let’s call it twenty hours of mine. The agents’ own tally of the model spend, at list API rates, came to $1,111.64, and I nearly printed it. During the fact-check I had the second agent recompute it without reusing the first one’s tooling. It returned $1,111.63880625. Agreement to eight decimal places, and the agreement was the tell. Both agents were counting transcript records; the transcript repeats a response’s full bill on every record it writes, and the biller counts responses. Counted properly, it is $513.43, and that is still a subtotal because the reviewing agent’s tokens have no published rate I could verify, and I am not going to invent one. What finally caught it was one sentence in the instructions: name a way both methods could be wrong in the same direction. The second opinion was no help. The second opinion agreed. Earlier that week, the same tooling passed five of its own consistency checks on a count that was 84 percent wrong. A check that cannot fail is decoration.
What the week actually cost me is two subscriptions I already pay for, $100 a month each. Roughly 97 percent of the tokens behind the corrected figure were the agents re-reading their own accumulated notes, and the gap between the list-rate number and what I pay is somebody’s business-model bet, the oldest one there is: get people hooked, then slowly turn the screws. That one deserves its own essay, and I now have unusually well-audited receipts for it.
The prototype bought me a cheap place to be wrong. Every lie in this story surfaced while the stakes were still a hobby app, before real money or anyone’s roadmap was riding on the answers.

The bigger question stays open, and I am not going to pretend otherwise: whether improvement in the garage translates to grass. The chorus might be right. I will find out on the course, slowly, because that is the only place the answer lives.
More of these are coming, roughly monthly: decisions I was accountable for, with the receipts attached. If you put agents into real work this year, I want to hear about the last time two of your systems agreed exactly and were both wrong, and what decision was waiting on the answer. And if you are a golfer who thinks in numbers, tell me what you wish your data could tell you, and what you would do differently if it did.
Joey Rodriguez is a product leader who has run product across six industries and up and down the capital stack, from venture-backed startups through buyout, with depth in pricing, product-led growth, and practical AI. He currently works with private-equity-backed companies as an operating partner and fractional chief product officer, with capital alongside.



