This isn’t a controlled benchmark, and I’m not claiming that one AI will always write better code than another.
It’s a comparison of what happened when I used Claude and ChatGPT during a difficult, multi-session architectural change in a real application.
There was good work.
There were warning signs I let slide.
And then there was the ugly part, where a project that had been moving along beautifully landed in the hands of a new session that acted as though it had never seen a keyboard before.
It started with 10,000 pages
As part of the extreme testing for PageSnip, we generated libraries containing 5,000 and 10,000 pages.
The plan is eventually to test libraries containing 50,000 pages.
The goal isn’t that I expect the average PageSnip user to create 50,000 pages in a single library. The goal is to make good on the promise that PageSnip doesn’t impose arbitrary limits on collections, libraries, or pages. The practical limit should be the user’s storage and computer, not a number I picked because it was convenient for the original design.
The 10,000-page tests showed us that the existing navigation architecture wasn’t going to be acceptable at that scale.
The system held the page and navigation metadata in memory. Theoretically, React and JavaScript should’ve been able to handle 50,000 records without much trouble.
Theoretically.
In the real application, it was sluggish. Navigation operations were taking too long. Some actions became noticeably slower as the library grew. The program was doing too much work too often.
The original design wasn’t necessarily bad.
It was designed around what we thought an average user would do, and it would’ve been perfectly reasonable for an average-sized collection of pages.
But once I decided that “unlimited” needed to mean something, the assumptions behind that architecture had to change.
I handed the project to Claude
I’d been hearing reports that Claude was producing better code than ChatGPT, so this seemed like a good opportunity to find out for myself.
I gave Claude the current PageSnip repository, the results from the large-library testing, and the project documentation. I asked it to do a deep analysis of the code and the performance problems before recommending a direction.
The initial work was good.
Claude didn’t come back and claim that the entire application had been designed badly. It recognized that the original architecture had been reasonable for the scale it was meant to support.
The problem was that the requirement had changed.
We weren’t trying to make PageSnip comfortable with a few hundred or a few thousand pages anymore. We wanted an architecture that could remain usable when the amount of data became much larger.
Claude and I agreed on two major changes.
First, the navigation metadata needed to move into SQLite.
Second, the React navigation system needed to stop behaving as though it owned and manipulated the entire collection of records at once. It needed to become a viewport into the data.
The database would hold the complete navigation authority. React would display and work with the portion of that information needed for the current view.
Those were good architectural decisions.
I reviewed them. I approved them. We moved forward carefully.
The good
We didn’t try to replace the entire navigation architecture in one giant leap.
We worked through it over a series of builds. Each build moved another part of the system toward SQLite and reduced the application’s dependence on the old in-memory behavior.
During that process, Claude also found other performance problems.
There were state changes causing the navigation tree to rebuild when nothing important had actually changed. Operations that should’ve been able to say, “Everything is current, there’s nothing to do,” were rebuilding the tree anyway.
That unnecessary work added up, especially with 10,000 pages.
We worked through those problems patiently. Claude did a good job analyzing the code and making the changes. The application became faster and more stable with each round.
One operation that had been taking roughly 2,600 milliseconds dropped to around 350 milliseconds.
That’s not a subtle improvement.
The difference in the application was obvious. Navigation felt dramatically faster. The architectural change was working.
This is important because I don’t want this to turn into an article saying Claude can’t code.
Claude produced good code.
It made good architectural recommendations.
It found real problems in the existing system and helped solve them.
That was the good.
The first warning signs
My normal development process is conservative.
I never do file edits with AI suggested code. They are the quickest path to broken code with no decent route to recovery.
I have the AI generate full file replacements for files that changed as well as any new files that were added.
I have the AI return a zip file in the shape of the repo on my machine so I can drop it into place and compile quickly.
That makes the work easier to review and reduces the chance that unrelated files will be accidentally altered.
The repository also contains startup and continuation documents.
Those files are a critical part of the way I do session-based coding. Every zip file should contain enough current information for a new AI session to understand the project, see what has been done, know what remains, and continue without requiring me to personally reconstruct the entire history.
The AI is supposed to update those documents every time it returns a build.
Claude wasn’t doing that consistently.
It might update the change log and one other file, but it wasn’t keeping the full set of required documents current.
I noticed it.
I reminded it.
Then I let it slide.
The code was good. The performance was improving. The architectural work was going in the right direction.
I allowed the visible success of the code to distract me from the fact that the process wasn’t being followed.
That was my mistake.
Then Claude needed a nap
When I use Claude I don’t generally ask the AI to send back a completely rebuilt repository after every change like I do with ChatGPT. I have it return a partial repository containing only the files it changed, along with the required project documents.
That makes Claude last longer before it hits the wall about usage and nag for more money or a plan upgrade.
During the work on PageSnip, Claude interrupted the process several times to tell me that I’d used the available capacity for that period and would need to wait before continuing.
I don’t like that model.
From the user’s side, it feels like being encouraged to buy a larger plan or purchase additional capacity by the pound.
Whatever the business reason is, the practical result is the same: development stops.
You may be in the middle of a complicated architectural change. You may have finally reached the point where the AI has a deep understanding of the system. You may have several connected changes that still need to be completed.
None of that matters when the account reaches its limit.
Claude takes a nap.
I waited through those interruptions because the work was good enough to justify it.
But it meant I had to start thinking about the AI’s remaining capacity as another project constraint.
Could we finish the next build before it stopped?
Should I start another change or produce a handoff now?
Would the interruption last a few hours or a couple of days?
That’s not something I normally have to think about with ChatGPT.
In my normal ChatGPT workflow, I use the most advanced reasoning model available to me at the high setting. I often have multiple browser tabs open, each running a separate development session.
I don’t normally run out of resources or have to stop working because the AI has decided the day is over.
That doesn’t automatically make ChatGPT a better programmer.
It does make it a more predictable development environment for the way I work.
The bad
Eventually, Claude displayed a message saying it was compressing the conversation so we could continue talking.
I’ve seen enough long AI sessions to know what that means.
The thread is getting too long.
Important context is at risk.
The session may continue to sound confident while its understanding of the project becomes less reliable.
By the time the message appeared, the session was already beginning to go sideways.
I ended up having to stop it, return to the thread, and work to get a usable handoff prompt for a new Claude session.
This is exactly the situation my startup and continuation documents were designed to handle.
A new session should’ve been able to receive the latest repository, read the current documents, examine the code, and continue.
But the documents hadn’t been maintained properly.
The first Claude session still knew the project because it had lived through the changes.
The second one didn’t.
The ugly
I handed the latest repository to a new Claude session.
I told it to read the startup documents, inspect the code, understand the architectural changes, and continue the work.
The difference between the two sessions was astonishing.
The first Claude session had been thoughtful and thorough. It made good logistical choices. It understood the architecture. It reasoned through problems before changing the code.
The replacement session behaved like a junior programmer who had never seen a keyboard before.
It made rookie mistakes.
It ignored explicit rules.
It didn’t seem to absorb the project structure.
Instead of giving me logical reasons for its decisions, it offered excuses.
I’d tell it that something was wrong, and rather than tracing the problem through the current architecture, it would produce a plausible-sounding explanation that didn’t match the evidence.
This wasn’t one bad answer.
It continued through several rounds.
That’s when the missing documentation came back to bite me.
The handoff system itself wasn’t the problem. The problem was that I’d allowed the previous session to stop maintaining it.
A handoff can’t transfer information that was never written down.
I tried increasing Claude’s effort
I had originally intended to use another Anthropic option that had briefly been available at a discounted rate, but that offer had expired. I normally keep Claude on Sonnet because it works well for consultations, website work, and smaller projects.
After the replacement session performed so poorly, I left it on Sonnet but increased the effort setting.
The last few rounds of code were better.
That confirmed that the model was still capable of doing good work when it applied more effort.
But then Claude reported that I’d used about 90 percent of the available capacity.
I knew what was coming.
Another nap.
This one might last much longer.
Probably days.
At that point, I stopped trying to rescue the session.
I had Claude prepare a complete repository rather than another partial update. I also had it write an analysis explaining the architectural changes, the work that had been completed, the current problems, and what still needed to happen.
Then I took the project back to ChatGPT.
Returning to ChatGPT
I gave ChatGPT the full repository, the architectural explanation, and a detailed startup prompt.
But I didn’t simply tell it to read the files and trust that it had done so.
I told it to open the startup documents completely.
I told it to inspect the actual codebase.
I told it not to rely on previous memory, old repositories, or anything it thought it knew about PageSnip.
The new repository was the source of truth.
Before it was allowed to change anything, it had to come back and demonstrate that it had done the work.
It had to identify the current architecture, explain the SQLite changes, describe the remaining problems, show that it understood the project rules, and present a plan for moving forward.
It did.
We were back on track.
Development resumed the way it should’ve been proceeding all along.
ChatGPT breaks rules too
None of this means ChatGPT is perfect.
It isn’t.
ChatGPT will take shortcuts if I let it. It may skim a document instead of reading it carefully. It may decide it understands a file from the name or the first few sections. It may rely on memory from an earlier build.
The difference is that working with ChatGPT taught me not to trust a simple statement that says, “I’ve read the files.”
I have to make it prove it.
That’s now part of my startup process.
The AI has to report back with specific details that could only have come from examining the current repository.
It has to tell me what it read, what it found, what it believes the current architecture is, which files it expects to change, and why.
That checkpoint has caught more than one AI trying to move forward on assumptions.
The mistake wasn’t using Claude
I don’t regret using Claude for the architecture work.
The recommendations were good.
The code was good.
The performance improvements were real.
I think ChatGPT could’ve done the same work, but that doesn’t take anything away from what Claude produced.
My mistake was allowing good code to convince me that the rest of the process wasn’t as important.
I’d heard that Claude was better at coding. The work looked good, so I relaxed my guard.
I allowed the session to return builds without maintaining all the required documents.
I accepted visible progress while the project’s portable memory fell behind.
Then the session ended, and the next Claude didn’t have the information the first one had been carrying in its conversation.
That’s when the weakness became visible.
It isn’t really Claude versus ChatGPT
I don’t think the ultimate lesson is that everyone should use ChatGPT instead of Claude.
I also don’t believe there’s one AI that’s always best for every kind of work.
Claude can be excellent at coding.
ChatGPT can be excellent at coding.
I’ve used other AIs that are good for particular tasks. I’ve also used Copilot, which in my experience has been the least capable and most argumentative of the group. PageSnip even has cleanup rules for some AI Helper output because Copilot will sometimes blatantly ignore the requested format while apologizing for doing it.
But even that isn’t the central issue.
The rules of successfully working with AI remain the same regardless of which model is behind the screen.
You need a good structure.
You need a current source of truth.
You need clear boundaries.
You need documents that survive the session.
You need a handoff that contains the project’s actual state.
And you need to keep your finger on the pulse of the work instead of assuming that good-looking output means everything is under control.
The handoff is part of the product
The biggest lesson for me is that a session-based development system is only as good as its handoff.
The startup documents aren’t an optional summary.
The change log isn’t paperwork.
The architecture notes aren’t something to update when there’s time.
They’re part of the deliverable.
When I receive a build, I’m not just receiving code. I’m receiving the project in a state that should be understandable to the next qualified session.
If the code works but the project can’t be safely continued, the build isn’t complete.
Good code made me careless
That may be the most useful way to sum up the entire experience.
Claude didn’t lose the project because it wrote bad code.
It wrote good code.
The quality of that work made me feel safe enough to let it break the rules.
That’s why the failure was so instructive.
Bad output gets your attention immediately.
Good output can hide a weakening process.
Whether I’m using Claude, ChatGPT, or whatever comes next, the model doesn’t get to replace the method.
The repository has to remain the source of truth.
The AI has to read it.
The AI has to prove that it read it.
And no matter how impressive the work becomes, I can’t let good code talk me into abandoning the rules that make the project survivable.
Related guide
The broader workflow lessons from this experience are covered in the companion guide, The Model Is Not the Method.