In March 2025 I built a travel app. You picked a destination, talked to it, and got an itinerary you could save as a PDF. Work started March 16 and stopped April 29. A second project did the part nobody sees, collecting the travel content behind the chat, and kept going until June 11. It took roughly three months to reach the point where the product delivered the value I wanted.
A year later I built it again. The 2026 version started on April 4. By April 14 it did most of what it does today. Ten days after I started.
Same author. Same kind of product. Both still on my laptop, both with a full record of every change I ever made.
So I measured them instead of remembering them. Software keeps its own logbook. Every time I saved a batch of work, it wrote down the date, and counting those days is the closest thing a project has to a stopwatch. What follows comes from those logs, the ticket lists, and the files. Where my memory and the record disagree, I went with the record.
In July, I measured the same year from the tooling side in What A Difference A Year Makes. This is the companion at the product level.
TL;DR
Version one was two projects and a folder I filled by hand. Four destination planners (New York, Japan, Walt Disney World, Iceland), each set up one at a time. The travel content was PDFs, notes, and data files I gathered myself.
Version two finds its own content. 78 starter cities across 59 countries, four kinds of public event listing plus web search, and a nightly job that refills any city running low. It keeps itself fed.
The work compressed. Version one took 14 working days on the app and 19 more on the content project. Version two took 11, April 4 to 14.
The safety work got cheap. The 2025 app switched off the language’s own error checking in 43 places. The 2026 one does it in 5. Tests went from 53 to 151.
The discipline is still uneven. The checks that run automatically on every change do not run those 151 tests. Every change went straight to the main branch. The written description of the app has been out of date since day one.
My argument: the model improved, but cheap engineering discipline matters more. Even with a softer curve, within three years harnesses erase the practical distinction between software engineering and vibe coding by making tests, security, maintenance, and system fit part of generation itself.
What version one was
The 2025 app took 166 saved changes across 14 working days, March 16 to April 29.
Four destinations shipped. Each one was a separate assistant I set up by hand, with its own settings file, its own opening line, its own word list and background images. Adding a fifth meant doing all of it again. The work per destination did not come down, so five destinations would have cost five times what one did.
The travel knowledge behind the chat came out of the second project: 72 saved changes over 19 days, still going six weeks after the app had stopped. What it produced landed in a folder on my laptop holding 23 data files, 15 notes, 11 PDFs, and one text file. Downloaded etiquette booklets. Festival guides. Train-travel PDFs. And a file named, literally:
Expand this json file with another 50 new locations.mdThat is the whole method in one filename. The list of places grew because I sat down and asked a model to extend it, then fed the longer list back in. Nothing ran unless I ran it, so the content was only ever as fresh as the last evening I spent on it.
What version two does on its own
The 2026 version started on April 4 with an empty project. Eleven days and 259 saved changes later, it did most of what it does today.
The largest product-level change is the content pipeline. Version one had a folder I filled. Version two goes out and gets its own material. Four kinds of public event listings feed it: public calendars, event sites, a ticketing company’s open catalog, and the details venues publish on their own pages. A web-search service adds more, one city at a time. Version one had no live search at all. I tried that same search service during the 2025 build and the results were poor. It has improved since, which is why the 2026 version leans on it.
The starting point is a list of 78 cities across 59 countries I built by hand, each with three to five local sources I picked myself: tourism boards, Time Out, local culture sites. That is a starter list. I did not count how many events are live in the app today.
The list is only the starting point.
Every source is asked at once, with a fifteen-second cutoff each. One dead source cannot hold up the rest, and a failure gets written down instead of quietly vanishing. So a broken feed shows up as a broken feed. A city does not just mysteriously go quiet.
The same concert reported by a listing site and by a search result collapses into one entry. A reader sees the show once instead of three times.
A job runs every night at 06:00 UTC and goes back out for any city that has dropped below twenty listings. That is the difference between a travel app that works the day it ships and one that still works a month later.
If the hosting service cuts that job off partway through, the job hands back its turn instead of blocking everything behind it. A second check takes over an abandoned run after fifteen minutes with no progress. Nightly work that gets stuck at 3am sorts itself out before anyone is awake to notice.
A free first pass throws out obvious junk before anything reaches the step that costs money. Bad pages cost nothing to throw away.
A second nightly job rebuilds every city page. New listings appear publicly without me shipping a new version of the app, so fresh content no longer waits on me.
None of that is exotic. It is the unglamorous plumbing that used to go undone.
Side by side
2025: the app plus the content project 2026: one app Separate projects 2 1 Active development days 14 + 19 11 (April 4 to 14) Saved changes 166 + 72 259 Lines of code 28,219 + 24,614 34,317 Where the travel content came from PDFs, notes, and data files I gathered by hand Public listings and web search, refilled every night Signing in Email, first name, invite code Google account behind an invite list How models were used One assistant per destination, set up by hand One model for every job Automated tests 53 + 57 151 Tickets filed 0 + 7 (all on one day at the end) 27, all closed Changes reviewed before going live 1 0
My own estimate for version one, from a retrospective I wrote in March 2025, was roughly 76 hours over the first six days. The day counts above come straight out of the log.
What got safer, and what did not
Version one had real problems, the kind that come from moving fast with nothing checking you.
Two copies of the same 57-line sign-in file sat in the project, identical down to the byte. I had started rearranging the files partway through and left the job unfinished, so both were still there. Editing one would have quietly missed the other, and that kind of bug shows up later, in the part you were sure you had fixed.
In 43 places the code switched off the language’s own error checking. That checking is what catches a mistake before the app runs. Switch it off and the mistake surfaces in front of a user instead. This was deliberate, too: the project’s written rules told me to do it, with a worked example.
Version two narrows most of those gaps. Five places switch off error checking across 34,000 lines, all of them in two files. The app checks its own inputs in 22 places before spending money on them, so a source that changes its format gets caught and thrown out before it fills the app with garbage. Nineteen files open with a short note saying why the code exists and how you would test it. Seventeen spell out the test case, so whoever touches the file next (me, months later) does not have to guess what it was meant to do.
Two of the April tickets were security holes. One let a crafted search term reach data it should not have. The other let a shared-trip link accept whatever was sent to it. Both were found and fixed the same day, in the middle of the build. A security hole gets more expensive the longer it sits unnoticed. These sat for a few hours.
Version two has its own gaps. The checks that run automatically on every change will format the code and build the app. They will not run the tests. So 151 tests exist and not one of them stands between a bad change and the live site. They help only when I remember to run them, and the whole point of an automatic check is not having to remember. No change ever waited on review by anyone, including me. The app’s written description still describes the empty project it started from on April 4, so a person reading it learns nothing about what the app became.
The tickets and the notes came earlier than I remembered. I have said I brought them in “in the latter part” of the build. Tickets existed from day one. The first twelve were filed on April 4, the same day as the first saved change, as a list of things I wanted. Then they sat, and were closed in a batch five months later rather than one at a time as each feature landed.
The stretch where the tracker was doing live work is tickets 13 through 24, filed and closed on April 6 and 7: two security findings, six cleanup and safety fixes, a data-quality fix, a speed fix, a dead-code removal, and one feature. File, fix, close, same day or the next. That is the middle of the burst, not the end of the project.
The design notes tell a cleaner story. Twenty-four dated documents, every one written between April 4 and April 12, alongside the build. Which framework to move to, how to shape an itinerary, how to search one city well. Writing the reasoning down while the decision is still fresh is what separates a project you can pick back up from one you have to work out again from scratch.
What I think the difference is
Almost every 2026 saved change carries a line naming the AI model that helped write it: 253 of 279. The build ran Claude Code through claude-mpm, a tool I wrote that runs a set of AI agents the way a manager runs a small team. It is a harness, the software that sits between a person and a model, turning a request into steps, running them, and checking what comes back. It did not exist when version one was built. Version one’s saved changes carry the older form of that line, with no model named: the coding tool on its own, nothing coordinating above it. I recall using a second AI coding tool alongside it that year, though nothing in either project confirms it.
The model improved over the year. Version one used one company’s assistant service, one assistant per destination, because that was the tool available. Version two sends eight or more different jobs through a single small fast model: reading an itinerary, pulling events out of a page, writing a plan, translating a foreign-language listing, judging whether a listing is worth keeping. It gets usable results from all of them. One model, one place to watch the bill.
The bigger share of the gap is that the work around the work got cheap. In 2025, writing a safety check, a note with an expected test case, a browser test, and a ticket for every unit of work would have cost more time than the work itself. So I skipped nearly all of it, and what I got was four planners sitting on a pile of content I fed by hand. In 2026 that same wrapper is close to free, because the thing writing the code also writes the check, the test, and the ticket. What I got, in a third of the days, keeps its own listings current, unsticks its own nightly job, and screens its own inputs before spending money on them.
The self-refilling city list is the clearest example. “Check every city each night, find the ones running low, and go get more” is a simple idea. It is also boring, fiddly plumbing with awkward edge cases, and in 2025 it would have been the thing I meant to do and did not. It exists now because handing it off was cheaper than putting it off.
Two things limit all of this. I got better at this over the year, and with two projects and a log I cannot separate my improvement from the tooling’s, so some unknown share of the gap is me. Both projects were also solo, unreviewed, and low stakes. Nothing here says what happens with a team, a compliance boundary, or a customer contract.
What this points at
Version one needed me to know things. What an assistant setting did. Why a stored login needs a date it stops working. What half-finished file rearranging does to a project over the following month. Version two needed less of that from me, because the parts that required it were the parts I handed off.

The model wrote the code in both years, more or less. What changed is everything around the code. The harness broke the work into steps, filed the ticket, wrote the test, caught the security hole the same afternoon, and left a note explaining why the code was there. I brought the idea, a travel app that keeps its own listings current, and the judgment about whether what came back was any good. The system handled the steps in between.
Two projects cannot prove a three-year forecast. Here is mine anyway.
The curve will soften. Legacy systems and organizational limits will slow it, especially where regulation raises the cost of being wrong. Even after that discount, I think the practical distinction between software engineering and vibe coding disappears within three years.
Today, vibe coding means prompting for a result without much discipline around how the result is produced. Software engineering is the work surrounding the code: architecture, tests, security, maintenance, and fitting a new piece into everything already running. The harness is pulling that work into the generation loop. It will not make every decision right. It will make reliable engineering the default instead of a cleanup phase after the prototype.
That changes the economics of code. As software gets cheaper to make, code by itself is worth less. The scarce part is how well it fits the larger system and how much real-world value the system creates. I made that bet with Trusty, putting the harness and its supporting tools into the open rather than treating the code as the moat.
I expect more software to move into the open-source ecosystem for the same reason. The tools will also get better at searching what is already there, understanding whether it fits, and integrating it before writing another copy. Cheap generation makes rebuilding easy. A good harness will make reuse easier still.
The second project is live at tripbot.tours. One caveat: I am paying for the scraping and hosting myself. If enough people use it to overwhelm that budget, I will have to shut it down or sell it. For now, it is there to try.
Two projects, one author, a year apart. The second is bigger, cleaner, and more capable, and it took a third of the days.
Bob Matsuoka is CTO of Duetto, a hospitality profit and revenue-management platform, and writes about AI-augmented engineering practice. Previously, Bob has been CTO of Tripadvisor, Citymaps, and Runtime Technologies.
Related reading:
What A Difference A Year Makes. The same year measured at the tooling level, comparing two of my own developer projects
AI Power Ranking. Tool comparisons and benchmarks for AI practitioners
LinkedIn Newsletter. Strategic AI insights for CTOs and engineering leaders





