Same model, Claude Opus 5.5. Some people say it gets a week's work done in half a day. Others say it gets two out of every three questions wrong.
I recently used it to build a mobile app called Ruzai (如在). When you're away from your computer, it lets you use your phone to view the AI assistant's conversations on that computer, send it messages, and approve the critical actions it wants to run.
Product design, UI, code, GitHub Actions pipelines that build Android APKs and push iOS builds to TestFlight, a self-hosted OTA update server, App Store listing assets: nearly all of it was done by Opus 5.5 inside Claude Code. Along the way we switched tech stacks once, evaluated two cross-platform frameworks, and I rejected the UI three times.
Looking back, model capability was almost never the bottleneck. With the same model, whether the results are good comes down to the person using it. This post breaks that down into a few concrete gaps, based on what actually happened during the project.
The tech stack, up front
| Area | What we ended up with | Tried / abandoned along the way |
|---|---|---|
| Mobile framework | React Native 0.88 + Expo SDK 58 | uni-app x (abandoned), Lynx 4.0 (abandoned after a prototype) |
| Routing and compilation | Expo Router, React Compiler, TypeScript 6 | TypeScript 7 (incompatible with typescript-eslint) |
| Gestures and animation | Reanimated 4.7, Gesture Handler 3.3 | |
| Builds | GitHub Actions: Android APK, iOS upload to TestFlight | |
| OTA updates | expo-updates protocol, hosted on my own server | |
| HarmonyOS | Native ArkTS version, builds cleanly in CI | |
| Development and debugging | Expo Go plus a real device, with the AI driving the phone directly over adb |
Every "abandoned" and "switched to" in that table was a disagreement between me and the AI. Let's go through them one by one.
Gap 1: Say "it's ugly" and the AI can only guess, three rounds in a row
When the first version of the UI came out, I said: it's ugly.
Round one, Opus 5.5 changed the color scheme. It found three established palettes online and built them into the prototype for me to choose from:
| Option | Source | Primary color |
|---|---|---|
| A Warm Cream | Claude's official palette | Terracotta orange #c96442, off-white background #faf9f5
|
| B Teal | Radix Sage gray + Jade green | #29a383 |
| C Indigo | Radix Slate gray + Indigo blue | #3e63dd |
All three palettes were perfectly sensible. I still thought it looked ugly.
Round two, it stopped just swapping colors and rebuilt the whole thing in the style of the 2026 Gemini App redesign: pure white or pure black backgrounds, no borders anywhere, an oversized greeting, and a floating pill-shaped input box.
In this round it also tracked down a cause I never would have guessed: the screenshots themselves looked cheap. The mockups were captured with headless Chrome on Windows, where Chinese text renders in Microsoft YaHei by default. After switching to Source Han Sans (Noto Sans SC), the look instantly got close to MiSans and PingFang on a phone. Part of what I'd been calling ugly in the first two rounds was really the screenshot font.
Round three, I figured the problem might be the icon colors, so I had it drop gradients entirely. It produced six solid-color icon options (black background with a bold sans, black background with a serif, a vermilion seal stamp...), and we settled on a white "Z" on solid black.
Only after three rounds did I realize I'd been handing out verdicts the whole time, never direction. If my first sentence had been "conversation-first like ChatGPT, black and white, no gradients," the first version would have been close to final.
AI has no aesthetic preferences. What it gives you is the average of a huge number of designs. If you can't articulate what you want, toss it a screenshot of an app you like. That beats ten rounds of "make it look more premium."
Gap 2: Whatever the spec says, it does to the letter
This project had two computers doing the work: one handled the server and the mobile app, the other handled the Windows client. The AIs on each side didn't share memory. They coordinated through spec documents in the code repository: the protocol, the APIs, what needed to change this time. One side wrote and committed the doc, the other pulled it down, followed it, and marked the status when done.
This setup worked really well. When the desktop client got a new icon and was hooked up to the mobile relay, this side wrote the handoff doc first, and the other side just followed it and got it done.
But it has a side effect: any decision written into the doc, the AI will carry out without compromise.
At one point, the spec switched the mobile tech stack to uni-app x. Opus 5.5 followed it and wrote an entire uni-app codebase, committed it, fixed the issues, and did a beautiful job. Only after it was done did I realize this locked the product into a single platform vendor, DCloud: push notifications, builds, and OTA updates would all depend on them. So I tore it down and started over with React Native + Expo.
The AI won't ask on its own, "Will you regret this choice three years from now?" Irreversible decisions like tech stack selection have to be made by a human.
Gap 3: Have it write code first, or have it explain why first
Later I wanted to try ByteDance's Lynx: one codebase for Android and iOS, and possibly HarmonyOS down the road. Opus 5.5 quickly put together a prototype that compiled, connected to the server, and rendered conversations.
But the UI had gone ugly again, nothing like the finalized design.
This time I didn't let it jump straight into fixes. Instead I said: no code yet. First let's work out why, then come up with a plan, and only then write code.
The reasons it gave were very specific:
| Area | React Native version | Lynx prototype | Why |
|---|---|---|---|
| Icons | SVG line icons | ☰ + ↑ text symbols | Lynx can't render SVG out of the box |
| Sidebar | Slides in from the left, 84% width, with an overlay | Replaced by a full-page list | No ready-made drawer or modal components |
| Bottom sheet | Picking a computer, confirmation details with screenshots | None | Same as above |
| Overall | Tuned piece by piece against the prototype | Thrown together in an hour to prove feasibility | Lynx can't use React Native components; only pure logic can be reused |
On top of that, testing turned up regressions: in Lynx 4.1.0, taps inside a scroll-view didn't fire, and in ReactLynx 0.128 all taps stopped working entirely, so we had to roll back to 4.0.3 and 0.126.
Once the reasons were laid out, the decision was easy: stick with React Native.
If I'd just said "make the UI look good," it would probably have spent hours re-tuning everything on Lynx, only for us to switch back in the end. The faster AI writes code, the further it runs in the wrong direction.
Gap 4: The more power you give it, the clearer the boundaries need to be
For on-device testing, I had the AI drive an Android phone directly over adb: tapping buttons, typing text, taking screenshots to check results. That's how a lot of issues surfaced that the desktop prototype simply couldn't reveal:
| Symptom | Cause | Fix |
|---|---|---|
| Keyboard covers the input box | With edge-to-edge enabled, the layout didn't make room for the keyboard | Switched KeyboardAvoidingView to padding |
| Taps do nothing after the screen dims | The first tap gets used to wake the screen | Send KEYCODE_WAKEUP before testing |
| Sidebar swipe gesture ends the moment it starts | The native ScrollView stole the touch | Switched to Gesture Handler's ScrollView, plus a 36dp-wide transparent hit zone along the left edge |
| Long conversations stop halfway when opened | During batched layout, the code's own scroll-to-bottom also fired scroll events, which got misread as "the user scrolled up" | Only finger drags can change the "at bottom" state |
| Text typed via adb never appears | The WeChat keyboard intercepted the input | Tap a candidate word to commit it |
But things did go wrong once: it hit the back button, landed on the phone's home screen, and its next few taps went straight into WeChat and Alipay.
No harm done, but it was enough of a wake-up call: an AI that can only chat can only talk; an AI hooked up to tools can verify its own work, and can also cause trouble on its own. The same model performs at a completely different level depending on whether it has tools. How much access to grant, which device to test on, and which apps are off-limits all need to be spelled out up front.
Gap 5: Where you review the work determines what you see
The screenshot font issue earlier was really a review problem: if you look at results in the wrong environment, even good work gets dismissed.
Builds are the same. Android packages built by GitHub Actions were signed with a different debug certificate every time, so installing a new version meant uninstalling the old one first. Committing a fixed debug.keystore to the repo finally solved it. You'd never catch that without installing twice on a real phone.
App Store listing assets are an even bigger pile of details:
| Asset | Required dimensions |
|---|---|
| Title artwork | 5244 × 2950 or 3840 × 1646 |
| Search result artwork | 5244 × 2950, 3840 × 2560, or 1920 × 1280 |
| iPhone 6.3-inch screenshots | 1206 × 2622 or 2622 × 1206 |
The AI got all these dimensions right on the first try, but the sample data in the screenshots only got swapped for neutral content after I looked them over and asked.
Then there was the store description. It wrote it based on our real internal use cases, with every feature covered, but describing it that literally would very likely have gotten stuck in review. The AI didn't know that; I did. We ended up reframing it as "use your phone to remotely control and view the AI conversations on your computer," with the subtitle "Remote-control and view the AI on your computer."
AI can get things right, but only someone who's been through it knows which things, done right, are actually risky.
Gap 6: Do you know what to ask?
Late in the project, I asked: "For small UI tweaks from now on, can we just push an update instead of rebuilding every time?"
That one question bought me a full OTA update system: using the expo-updates protocol, with update bundles hosted on my own server. runtimeVersion uses the fingerprint policy, so JS-only releases update automatically when the phone opens the app, and only changes to native code require a new build.
If I hadn't asked, it wouldn't have done it on its own. Not because it couldn't, but because it didn't know this mattered to me.
Likewise, the HarmonyOS version only started because I asked, "Can you build a native HarmonyOS version?" It wrote one in ArkTS that builds cleanly on GitHub Actions, and along the way worked through a few pitfalls you only find by actually doing it: Huawei's ohpm registry frequently returns 503 to GitHub's runners, so retries are needed; imageknife 3.2.8 and above requires API 18, so we had to stay on 3.2.7.
AI goes exactly as far as your questions take it. People who know how to ask naturally get more out of it.
Gap 7: Are you really using "the same model"?
One last fact that often gets overlooked: when two people say "I'm using Opus 5.5," they may not be using the same thing at all.
| Difference | What it actually means |
|---|---|
| Client | Web chat, Claude Code, all sorts of wrapper apps, each with a completely different system prompt |
| Tools | Whether it can read files, run a terminal, drive a browser or a phone |
| Reasoning effort | Thinking levels, fast mode; the same model varies a lot between high and low settings |
| Context | 1M context or standard length; whether old conversations get compacted, and what gets lost when they do |
| Route | Official API, a cloud provider, or various third-party relays; the version and limits behind them aren't necessarily the same |
I built Ruzai with Claude Code: it can read and write the entire repository, run commands, call adb, and use GitHub. Comparing that to a one-question-one-answer session in a web chat was never a fair fight.
Final thoughts
Putting the seven gaps side by side:
| Feels "dumb" | Feels "smart" | |
|---|---|---|
| Aesthetics | Just says "it's ugly" | Gives reference screenshots, says clearly what not to do |
| Spec docs | Once it's written, it's settled | Makes irreversible decisions yourself |
| Order | Jumps straight into code | Reasons and plan first |
| Tool access | None, or wide open | Gives tools, and draws boundaries |
| Review | Looks at screenshots on a computer | Checks on a real device, with real fonts and real data |
| Questions | Waits for it to think of things | Knows what to ask |
| Setup | Only looks at the model name | Looks at client, tools, effort level, route |
Across the whole project, Opus 5.5 wrote nearly all the code, but every turning point was a human decision: dropping uni-app x, passing on Lynx, removing gradients, rewriting the store copy, adding OTA updates.
So next time you feel like the AI has "gotten dumber," don't rush to switch models. Take another look at the job description you gave it, and ask whether that was the part that wasn't very smart.
For the more technical side of things, like retrieval, context curation, reasoning budgets, and verification, I wrote about those in Before Switching Models, Make Your AI Smarter. The two pair well together.
If you're also building your own product with Claude Code or another AI, I'd love to chat: linktr.ee/weiyii
This article was prepared for DEV with AI assistance.
Top comments (0)