{"@attributes":{"version":"2.0"},"channel":{"title":"Ramiz Raja \u2014 Writing","link":"https:\/\/codebyramiz.com\/blog\/","description":"Articles on building, testing and shipping Android and iOS apps.","language":"en-us","lastBuildDate":"Sat, 14 Nov 2026 00:00:00 GMT","item":[{"title":"The Removed Member Whose Socket Kept Listening","link":"https:\/\/codebyramiz.com\/blog\/sse-vs-websockets\/","guid":"https:\/\/codebyramiz.com\/blog\/sse-vs-websockets\/","pubDate":"Sat, 14 Nov 2026 00:00:00 GMT","description":"SSE vs WebSockets, decided by deployment rather than protocol features: a Next.js board that streams SSE locally, uses a hibernating Durable Object WebSocket on Cloudflare when configured, falls back to polling, and closes a removed member's socket with code 4001.","author":"hello@codebyramiz.com (Ramiz Raja)","category":["Web","Architecture","Cloudflare","TypeScript"]},{"title":"Twelve Apps, Nine Kits, and a Theme Function Still Called ResizePingTheme","link":"https:\/\/codebyramiz.com\/blog\/white-label-app\/","guid":"https:\/\/codebyramiz.com\/blog\/white-label-app\/","pubDate":"Thu, 12 Nov 2026 00:00:00 GMT","description":"A first-hand look at white label app development: twelve Ping apps, nine shared kit repos, most apps pinning them as git submodules, an ads-kit release that eight app repos pinned within two days, and the drift two forked apps still carry.","author":"hello@codebyramiz.com (Ramiz Raja)","category":["Architecture","Android","iOS","Mobile Engineering"]},{"title":"Trunk-Based Development With Coding Agents: What My Merge History Shows","link":"https:\/\/codebyramiz.com\/blog\/trunk-based-development-mobile\/","guid":"https:\/\/codebyramiz.com\/blog\/trunk-based-development-mobile\/","pubDate":"Tue, 10 Nov 2026 00:00:00 GMT","description":"A first-hand look at trunk based development with coding agents: one 37-commit branch that failed CI three times before merging, the nine PRs after it with a three-minute median, and merge times for 89 PRs across four other repos.","author":"hello@codebyramiz.com (Ramiz Raja)","category":["CI\/CD","Git","Mobile Engineering","AI Agents"]},{"title":"My Notes Said Firebase Test Lab Ran on Every Gate. It Never Went Green.","link":"https:\/\/codebyramiz.com\/blog\/firebase-test-lab\/","guid":"https:\/\/codebyramiz.com\/blog\/firebase-test-lab\/","pubDate":"Thu, 05 Nov 2026 00:00:00 GMT","description":"A first-hand Firebase Test Lab setup: the five-device matrix I picked to fit the free quota, the stale device IDs the catalog check caught, the Robo script for onboarding, and the five CI runs that all failed while my notes said it was running.","author":"hello@codebyramiz.com (Ramiz Raja)","category":["Testing","Android","Device Testing","Firebase"]},{"title":"Hiring an Android Developer? Read the Git Log Before the CV","link":"https:\/\/codebyramiz.com\/blog\/hire-android-developer\/","guid":"https:\/\/codebyramiz.com\/blog\/hire-android-developer\/","pubDate":"Tue, 03 Nov 2026 00:00:00 GMT","description":"A Play Store link and a Kotlin test don't show what decides whether an Android hire works out: where their work lives, whether they know your app's Google Play deadlines, how a build reaches testers, and who holds the upload key.","author":"hello@codebyramiz.com (Ramiz Raja)","category":["Hiring","Android","App Rescue","Google Play"]},{"title":"Android and iOS, Two Device Layers: agent-device Against My Own Stack","link":"https:\/\/codebyramiz.com\/blog\/mobile-mcp-agent-device\/","guid":"https:\/\/codebyramiz.com\/blog\/mobile-mcp-agent-device\/","pubDate":"Tue, 03 Nov 2026 00:00:00 GMT","description":"I ran Callstack's agent-device and my own device stack (adb and android layout on Android, AXe on iOS) through the same tasks and logged every call. agent-device's refs, settle diffs and compact snapshots won on what the agent reads. On iOS its covered-element check refused taps that worked. A wait command passed before anything was saved, on both platforms.","author":"hello@codebyramiz.com (Ramiz Raja)","category":["AI Agents","Mobile Testing","Android","iOS","MCP"]},{"title":"How to Hire an iOS Developer: What to Check Before You Pay","link":"https:\/\/codebyramiz.com\/blog\/hire-ios-developer\/","guid":"https:\/\/codebyramiz.com\/blog\/hire-ios-developer\/","pubDate":"Sun, 01 Nov 2026 00:00:00 GMT","description":"An App Store link proves an app exists, not who built it. Here is what I would check before paying an iOS developer, including me: the listing, the release pipeline, App Review experience and account ownership.","author":"hello@codebyramiz.com (Ramiz Raja)","category":["Hiring","iOS","App Rescue","Mobile Development"]},{"title":"Claude Code Plan Mode, and the Second Reader It Doesn't Have","link":"https:\/\/codebyramiz.com\/blog\/claude-code-plan-mode\/","guid":"https:\/\/codebyramiz.com\/blog\/claude-code-plan-mode\/","pubDate":"Sat, 31 Oct 2026 00:00:00 GMT","description":"Plan mode lets Claude read and propose without editing until you approve, and it saves the plan to a file. Here is how it works per the docs, how rarely it shows up in 7,866 of my transcripts, and the gate I put in front of plans another agent will execute.","author":"hello@codebyramiz.com (Ramiz Raja)","category":["AI-Assisted","Claude Code","Planning","Workflow"]},{"title":"OpenCode vs Claude Code: Which to Pick, With 50 Logged Tasks","link":"https:\/\/codebyramiz.com\/blog\/opencode-vs-claude-code\/","guid":"https:\/\/codebyramiz.com\/blog\/opencode-vs-claude-code\/","pubDate":"Fri, 30 Oct 2026 00:00:00 GMT","description":"OpenCode takes any provider's models; Claude Code is built around one vendor's. Here is a feature table from the official docs, who should pick which, and what 50 OpenCode tasks and 20 Claude Code tasks looked like, failures included.","author":"hello@codebyramiz.com (Ramiz Raja)","category":["AI-Assisted","OpenCode","Claude Code","Workflow"]},{"title":"What It Costs to Maintain an App: Nine Years of Forced Updates on One Android App","link":"https:\/\/codebyramiz.com\/blog\/app-maintenance-cost\/","guid":"https:\/\/codebyramiz.com\/blog\/app-maintenance-cost\/","pubDate":"Thu, 29 Oct 2026 00:00:00 GMT","description":"Most answers to app maintenance cost are a percentage of the build budget. Here is the actual record instead: eleven target API raises, six Billing Library majors and a deleted test suite, from the git history of an Android app I built in 2017 and came back to in 2026.","author":"hello@codebyramiz.com (Ramiz Raja)","category":["Android","Google Play","App Maintenance"]},{"title":"Flutter vs React Native: How I Choose, and When I Go Native","link":"https:\/\/codebyramiz.com\/blog\/flutter-vs-react-native\/","guid":"https:\/\/codebyramiz.com\/blog\/flutter-vs-react-native\/","pubDate":"Wed, 28 Oct 2026 00:00:00 GMT","description":"I have a production Flutter app on both stores, and React Native experience that is brownfield, SDK and sample work. Here is how the two render according to their docs, what my Flutter app needed from native code, and when I pick native instead.","author":"hello@codebyramiz.com (Ramiz Raja)","category":["Flutter","React Native","Cross-Platform","Architecture"]},{"title":"Claude Code in Android Studio: The Editor Is the Easy Part","link":"https:\/\/codebyramiz.com\/blog\/claude-code-android-studio\/","guid":"https:\/\/codebyramiz.com\/blog\/claude-code-android-studio\/","pubDate":"Tue, 27 Oct 2026 00:00:00 GMT","description":"The Claude Code plugin for Android Studio shows diffs in the IDE and lets the agent read lint, but the phone is reached from the terminal. Counts from my agents' transcripts: thousands of leased adb commands, almost no use of Google's android CLI, a screenshot path that kept returning black frames, and a guard hook whose biggest rule mostly blocked commands that only mentioned a script.","author":"hello@codebyramiz.com (Ramiz Raja)","category":["AI Agents","Claude Code","Android","Testing"]},{"title":"Claude Code Compact: What Survives, With Counts From My Sessions","link":"https:\/\/codebyramiz.com\/blog\/claude-code-compact\/","guid":"https:\/\/codebyramiz.com\/blog\/claude-code-compact\/","pubDate":"Mon, 26 Oct 2026 00:00:00 GMT","description":"Claude Code compact replaces your conversation with a summary. The docs list what survives and what does not. I counted compactions across 4,121 of my own session transcripts, and here is the one rule I follow because of it.","author":"hello@codebyramiz.com (Ramiz Raja)","category":["AI-Assisted","Claude Code","Context","Workflow"]},{"title":"Claude Code Hooks: Three I Run Every Day","link":"https:\/\/codebyramiz.com\/blog\/claude-code-hooks\/","guid":"https:\/\/codebyramiz.com\/blog\/claude-code-hooks\/","pubDate":"Mon, 26 Oct 2026 00:00:00 GMT","description":"Three Claude Code sessions each launched about twelve background commands and crowded a 12-core Mac, and no session could see the others. A CLAUDE.md rule cannot fix that; a hook can. Three hooks I run on every session: a budget guard that blocks background fan-out before it starts, a Stop hook that refuses to end a turn on an unverified still-running claim, and a cleanup pass that reaps what a session leaves behind. Which event each one hangs on, what the contract allows, and the two places mine are still wrong.","author":"hello@codebyramiz.com (Ramiz Raja)","category":["AI-Assisted","Claude Code","Workflow","Automation"]},{"title":"Claude Code Pricing: What Each Plan Includes and What It Costs","link":"https:\/\/codebyramiz.com\/blog\/claude-code-pricing\/","guid":"https:\/\/codebyramiz.com\/blog\/claude-code-pricing\/","pubDate":"Mon, 26 Oct 2026 00:00:00 GMT","description":"Claude Code pricing depends on how you sign in: a subscription plan with usage limits, or pay-per-token through the API. Here is what Anthropic's pricing page and cost docs said on 2026-10-26, quoted and linked, with no numbers from memory.","author":"hello@codebyramiz.com (Ramiz Raja)","category":["AI-Assisted","Claude Code","Pricing","Workflow"]},{"title":"Claude Code Statusline: A Setup That Shows Your Limits","link":"https:\/\/codebyramiz.com\/blog\/claude-code-statusline\/","guid":"https:\/\/codebyramiz.com\/blog\/claude-code-statusline\/","pubDate":"Mon, 26 Oct 2026 00:00:00 GMT","description":"A Claude Code statusline is a shell command that reads JSON on stdin and prints one line under the prompt. Here is the setting, the fields the docs list, the settings.json scopes, and my own script that shows context used and five-hour and seven-day usage with reset times.","author":"hello@codebyramiz.com (Ramiz Raja)","category":["AI-Assisted","Claude Code","Settings","Workflow"]},{"title":"Claude Code Subagents: When One Beats Doing It Inline","link":"https:\/\/codebyramiz.com\/blog\/claude-code-subagents\/","guid":"https:\/\/codebyramiz.com\/blog\/claude-code-subagents\/","pubDate":"Mon, 26 Oct 2026 00:00:00 GMT","description":"A subagent is its own context window with its own prompt, tools and permissions. The docs say to delegate verbose or self-contained work that can return a summary, and to stay inline when it needs back-and-forth. Here is the shape, the decision table, and the bounded brief I hand over.","author":"hello@codebyramiz.com (Ramiz Raja)","category":["AI-Assisted","Claude Code","Subagents","Workflow"]},{"title":"Claude Code vs Cursor: What I Measured Running Both as Coding Workers","link":"https:\/\/codebyramiz.com\/blog\/claude-code-vs-cursor\/","guid":"https:\/\/codebyramiz.com\/blog\/claude-code-vs-cursor\/","pubDate":"Mon, 26 Oct 2026 00:00:00 GMT","description":"I have used the Cursor CLI and Claude Code as bounded-task workers, and I keep a results log of the tasks I dispatch. Here are the counts from that log (9 Cursor composer rows, 20 Claude Sonnet rows), the failures, one older three-tool benchmark that did not include Claude Code, and the Cursor and Claude claims that come from their own docs.","author":"hello@codebyramiz.com (Ramiz Raja)","category":["AI-Assisted","Claude Code","Cursor","Workflow"]},{"title":"Claude Code YOLO Mode: What It Removes and What I Run Instead","link":"https:\/\/codebyramiz.com\/blog\/claude-code-yolo-mode\/","guid":"https:\/\/codebyramiz.com\/blog\/claude-code-yolo-mode\/","pubDate":"Mon, 26 Oct 2026 00:00:00 GMT","description":"YOLO mode, the bypassPermissions mode behind --dangerously-skip-permissions. Here is what the docs say it removes, why I run auto mode with a long allowlist instead, and the one hook guard I added for a failure I actually saw.","author":"hello@codebyramiz.com (Ramiz Raja)","category":["AI-Assisted","Claude Code","Permissions","Workflow"]},{"title":"CLAUDE.md File: What to Put In It and What to Leave Out","link":"https:\/\/codebyramiz.com\/blog\/claude-md-file\/","guid":"https:\/\/codebyramiz.com\/blog\/claude-md-file\/","pubDate":"Mon, 26 Oct 2026 00:00:00 GMT","description":"A CLAUDE.md file is loaded into every session, so a long one costs context and, per the docs, reduces adherence. I cut mine from 3,639 words to 512 with a pointer index, then let it regrow to 9,555. Here is what belongs in it, what doesn't, and the numbers from my own git history.","author":"hello@codebyramiz.com (Ramiz Raja)","category":["AI-Assisted","Claude Code","CLAUDE.md","Workflow"]},{"title":"Codex Exec in a Script: The Flags, the Exit Codes and a Timeout","link":"https:\/\/codebyramiz.com\/blog\/codex-exec-script\/","guid":"https:\/\/codebyramiz.com\/blog\/codex-exec-script\/","pubDate":"Mon, 26 Oct 2026 00:00:00 GMT","description":"Codex exec runs Codex without the interactive screen, so you can call it from a script. The docs cover sandbox, stdin and the output file. They do not describe a timeout, so I added one, plus an exit-code rule: no verdict is never a pass.","author":"hello@codebyramiz.com (Ramiz Raja)","category":["AI-Assisted","Codex","Automation","Workflow"]},{"title":"Using Codex as the Read-Only Reviewer for Claude Code","link":"https:\/\/codebyramiz.com\/blog\/codex-vs-claude-code\/","guid":"https:\/\/codebyramiz.com\/blog\/codex-vs-claude-code\/","pubDate":"Mon, 26 Oct 2026 00:00:00 GMT","description":"I run Claude Code as the agent that plans, edits and dispatches, and Codex as the independent reviewer that returns a binary VERDICT on the changes I send it. Here is where each one sits in my workflow, one real failure the gate caught, and what I did not compare.","author":"hello@codebyramiz.com (Ramiz Raja)","category":["AI-Assisted","Claude Code","Codex","Workflow"]},{"title":"Flaky Tests or No Result? Seven Bad Runs, Sorted","link":"https:\/\/codebyramiz.com\/blog\/flaky-mobile-ui-tests\/","guid":"https:\/\/codebyramiz.com\/blog\/flaky-mobile-ui-tests\/","pubDate":"Mon, 26 Oct 2026 00:00:00 GMT","description":"A run that never produced a test result is not a flaky test and not a failing one. Here are seven red or incomplete runs from my Android logs, how I classified each, the one failure I could reproduce on purpose, and the ones I never explained.","author":"hello@codebyramiz.com (Ramiz Raja)","category":["AI-Assisted","Testing","Android","Workflow"]},{"title":"Gemini CLI vs Claude Code: What I Can and Cannot Say From My Logs","link":"https:\/\/codebyramiz.com\/blog\/gemini-cli-vs-claude-code\/","guid":"https:\/\/codebyramiz.com\/blog\/gemini-cli-vs-claude-code\/","pubDate":"Mon, 26 Oct 2026 00:00:00 GMT","description":"If you want Gemini CLI vs Claude Code, my logs cannot give it to you: I have no Gemini CLI runs, only Gemini models run through Antigravity (22 rows) next to 20 Claude Code rows. Here are the counts, the failures, what Gemini CLI's own README says, and what I did not measure.","author":"hello@codebyramiz.com (Ramiz Raja)","category":["AI-Assisted","Claude Code","Gemini","Workflow"]},{"title":"SwiftUI vs UIKit: Counts From Eight of My iOS Apps","link":"https:\/\/codebyramiz.com\/blog\/swiftui-vs-uikit\/","guid":"https:\/\/codebyramiz.com\/blog\/swiftui-vs-uikit\/","pubDate":"Mon, 26 Oct 2026 00:00:00 GMT","description":"Every one of my eight iOS apps imports both frameworks. Here are the file counts for SwiftUI and UIKit, the UIKit types that keep appearing, Apple's quoted description of the bridging types, and what the counts cannot tell you.","author":"hello@codebyramiz.com (Ramiz Raja)","category":["iOS","SwiftUI","UIKit","Architecture"]},{"title":"Xcode MCP: Setting It Up for Claude Code, and What I Saw","link":"https:\/\/codebyramiz.com\/blog\/xcode-mcp\/","guid":"https:\/\/codebyramiz.com\/blog\/xcode-mcp\/","pubDate":"Mon, 26 Oct 2026 00:00:00 GMT","description":"Xcode ships an MCP server that an agent started outside Xcode can use to build and act on your project. Here is Apple's setup, quoted, and four things I saw when I wired it to Claude Code on Xcode 27.0 on 2026-09-29.","author":"hello@codebyramiz.com (Ramiz Raja)","category":["AI-Assisted","Claude Code","iOS","Workflow"]},{"title":"Claude Code Skills: How to Write Ones Your Agent Actually Uses","link":"https:\/\/codebyramiz.com\/blog\/claude-code-skills\/","guid":"https:\/\/codebyramiz.com\/blog\/claude-code-skills\/","pubDate":"Sun, 25 Oct 2026 00:00:00 GMT","description":"You write a detailed SKILL.md, and your agent still ignores it and improvises from scratch. I run 130 skills across three Claude Code profiles, and whether one gets used comes down to its description, a lean procedure with detail in side files, and one shared folder instead of copies. Here is what decides whether an agent loads and follows a skill.","author":"hello@codebyramiz.com (Ramiz Raja)","category":["AI-Assisted","Claude Code","Workflow","Automation"]},{"title":"Google Play Target API Level Requirement: A Checklist for an App Nobody Has Touched","link":"https:\/\/codebyramiz.com\/blog\/google-play-target-api-level-checklist\/","guid":"https:\/\/codebyramiz.com\/blog\/google-play-target-api-level-checklist\/","pubDate":"Thu, 22 Oct 2026 00:00:00 GMT","description":"Play Console warns that your app must target a newer API level, and the app has not been built in a year. Here is the requirement as Google states it and the order I raise targetSdk in.","author":"hello@codebyramiz.com (Ramiz Raja)","category":["Android","Release","Google Play"]},{"title":"Don't Trust an Agent That Says the Tests Passed","link":"https:\/\/codebyramiz.com\/blog\/agent-says-tests-passed\/","guid":"https:\/\/codebyramiz.com\/blog\/agent-says-tests-passed\/","pubDate":"Wed, 21 Oct 2026 00:00:00 GMT","description":"A change came back with 768 passing tests reported. An independent reviewer, which did not rerun them, found two High bugs those tests did not cover. Another reviewer found no blocking issues in static review and still failed a module, because it could not run the tests itself. Here are the three checks I put between an AI coding agent and the word done: a hook that checks status claims, a binary verdict from a reviewer that did not write the code, and exit codes that separate a failure from a run that never produced a result.","author":"hello@codebyramiz.com (Ramiz Raja)","category":["AI-Assisted","Testing","Workflow","Android","iOS"]},{"title":"When Your Coding Agent Hits a Rate Limit: A Fallback Ladder That Keeps Working","link":"https:\/\/codebyramiz.com\/blog\/coding-agent-rate-limit-ladder\/","guid":"https:\/\/codebyramiz.com\/blog\/coding-agent-rate-limit-ladder\/","pubDate":"Wed, 21 Oct 2026 00:00:00 GMT","description":"Your coding agent just ran out of quota. Waiting kills the afternoon, and pasting the prompt into whatever tool is open wastes your best model on small work. Here is the ordered fallback ladder I run instead: free models first, paid rows one step at a time, automatic retries only when nothing was written, and a journal that decides the order.","author":"hello@codebyramiz.com (Ramiz Raja)","category":["AI-Assisted","AI Agents","Workflow"]},{"title":"Twelve Cores, Seven Devices, One Disk","link":"https:\/\/codebyramiz.com\/blog\/running-multiple-claude-code-agents\/","guid":"https:\/\/codebyramiz.com\/blog\/running-multiple-claude-code-agents\/","pubDate":"Wed, 21 Oct 2026 00:00:00 GMT","description":"Every guide to running multiple Claude Code agents in parallel says the same thing: give each one its own git worktree. That keeps the files apart. It doesn't keep the machine apart. On my setup, several sessions share a 12-core Mac, a Windows build PC and seven test phones, and what broke was never a merge conflict. It was CPU oversubscription, two test runs fighting over one port, phones left awake around the clock, a disk that went from 56 GB free to 1 GB overnight, and a test run that sat hung for seven hours. The guards that fixed each one, with the real config.","author":"hello@codebyramiz.com (Ramiz Raja)","category":["AI-Assisted","Multi-Agent","Workflow","Android","iOS"]},{"title":"What Keeps Agent-Written Code Clean When Nobody Reads It","link":"https:\/\/codebyramiz.com\/blog\/clean-architecture-ai-agents\/","guid":"https:\/\/codebyramiz.com\/blog\/clean-architecture-ai-agents\/","pubDate":"Tue, 20 Oct 2026 00:00:00 GMT","description":"AI agents write most of the code in my apps and I don't read it line by line. The clean architecture still has to hold, because the next feature, the next SDK swap and the next agent all depend on it. What I found: a rule written into a prompt decays, a boundary test doesn't, and a reviewer that isn't the writer catches layer mistakes before they become code. Real tests, a real reviewer verdict, and one boundary that did break.","author":"hello@codebyramiz.com (Ramiz Raja)","category":["AI-Assisted","Clean Architecture","Android","iOS"]},{"title":"Live Users, Zero Ad Requests","link":"https:\/\/codebyramiz.com\/blog\/admob-zero-ad-requests\/","guid":"https:\/\/codebyramiz.com\/blog\/admob-zero-ad-requests\/","pubDate":"Tue, 13 Oct 2026 00:00:00 GMT","description":"AdMob reported zero ad requests for an app with people actively using it. Consent and app-ads.txt could not explain it: the build that reached the store had been compiled with ads off, because one git-ignored file was missing from the build machine. That case, the same fault on iOS, and the different failure that hides behind a 0% match rate.","author":"hello@codebyramiz.com (Ramiz Raja)","category":["AdMob","Release Engineering","Android","iOS"]},{"title":"Why Kobiton Became Part of My Everyday Agentic Development","link":"https:\/\/codebyramiz.com\/blog\/kobiton-real-device-cloud\/","guid":"https:\/\/codebyramiz.com\/blog\/kobiton-real-device-cloud\/","pubDate":"Wed, 07 Oct 2026 00:00:00 GMT","description":"An AI agent can write and test mobile code quickly, but proving it on a real iPhone or Android phone still needs hardware. This is how Kobiton's real-device cloud and MCP server became the place my agent does that work, with iOS and Android examples, the timings I measured, and what to check before you start.","author":"hello@codebyramiz.com (Ramiz Raja)","category":["iOS","Android","Test Automation","AI Agents"]},{"title":"Your App Should Still Be Yours When the Developer Leaves","link":"https:\/\/codebyramiz.com\/blog\/your-app-should-still-be-yours\/","guid":"https:\/\/codebyramiz.com\/blog\/your-app-should-still-be-yours\/","pubDate":"Thu, 24 Sep 2026 00:00:00 GMT","description":"If the person who builds your app stopped answering tomorrow, could someone else ship a fix next week? Two real handovers show where that question actually gets decided: eight months of work that had never reached the shared repository, arriving as a single commit, and an agency-built app whose App Store upload key sat in the repository. Then the checklist of what your business has to own, and what a developer should only ever be granted access to.","author":"hello@codebyramiz.com (Ramiz Raja)","category":["App Handover","App Rescue","Mobile Development"]},{"title":"The Agent Taps the Phone. Nothing Touches the Mac.","link":"https:\/\/codebyramiz.com\/blog\/agent-drives-a-real-iphone\/","guid":"https:\/\/codebyramiz.com\/blog\/agent-drives-a-real-iphone\/","pubDate":"Wed, 09 Sep 2026 00:00:00 GMT","description":"An agent will implement your iOS feature, write the test, and run it \u2014 then stop at the only question that matters: does this work on a phone? Two walls stand between you and letting it drive the device. The first is iOS itself: on Android a cable and a debugging switch inject a tap into any handset, while nothing on a physical iPhone accepts one without a signed on-device agent and a live tunnel. The second is self-inflicted \u2014 the assumption that the agent must work a pointer like a person, which makes the automation unusable even when it works. Here is the receipt from a real session, the three honest tiers of physical-device driving, the exact commands to reproduce the whole loop yourself, and the two bugs a simulator could never have caught.","author":"hello@codebyramiz.com (Ramiz Raja)","category":["iOS","Test Automation","AI Agents"]},{"title":"Call, Text, and Share Without Asking for a Single Permission","link":"https:\/\/codebyramiz.com\/blog\/contact-actions-without-permissions\/","guid":"https:\/\/codebyramiz.com\/blog\/contact-actions-without-permissions\/","pubDate":"Tue, 25 Aug 2026 00:00:00 GMT","description":"A row of quick actions under a phone number \u2014 call it, text it, save it, share it \u2014 looks like a convenience and builds like a permissions grab. The naive version asks the OS to place calls, send texts, and write to the address book: a runtime prompt in front of a button the user tapped because they were ready to act, a store-policy tripwire on Android, and a privacy disclosure on both platforms it never needed. None of it is required, because none of those actions is something the app has to do itself. Here's the permission-free version on Android and iOS, and the per-platform permission audits that prove it.","author":"hello@codebyramiz.com (Ramiz Raja)","category":["Android","iOS","Permissions"]},{"title":"A Correct Fix That Quietly Broke the Feature Next to It","link":"https:\/\/codebyramiz.com\/blog\/regression-testing-when-fixes-break-things\/","guid":"https:\/\/codebyramiz.com\/blog\/regression-testing-when-fixes-break-things\/","pubDate":"Sat, 15 Aug 2026 00:00:00 GMT","description":"A one-line change did exactly what it was meant to: it made a half-finished unit of work report itself as a failure instead of a fake success. It shipped in the afternoon it was written. Before the day was out it had to be fixed again \u2014 because that honest little fix had quietly taught the app to abandon entire batches of work, without an error, a crash, or a single line in any dashboard. That's a regression: a bug your own progress created, invisible from the inside. Here's the one that got caught, and the test that now catches it for good.","author":"hello@codebyramiz.com (Ramiz Raja)","category":["Android","Testing","Regressions"]},{"title":"Your iOS and Android Apps Are Supposed to Be the Same Product. One of Them Isn't.","link":"https:\/\/codebyramiz.com\/blog\/android-ios-feature-parity\/","guid":"https:\/\/codebyramiz.com\/blog\/android-ios-feature-parity\/","pubDate":"Fri, 14 Aug 2026 00:00:00 GMT","description":"Most companies with two mobile apps have one real product and one approximation of it. The gap never appears on a dashboard, because parity is nobody's number \u2014 it grows one reasonable 'we'll port that later' at a time, and the users on the weaker platform don't file tickets about features they've never seen. Taking a production Android app to iOS, screen by screen, taught me what closing that gap actually takes: an adversarial inventory of every surface, proof that filters mean the same thing and not just look the same, and the discipline to write down which differences are deliberate. Here's the method, with the failures it caught.","author":"hello@codebyramiz.com (Ramiz Raja)","category":["Android","iOS","Feature Parity"]},{"title":"Your Crash-Free Rate Is 99.5% and Users Still Say the App Is Broken","link":"https:\/\/codebyramiz.com\/blog\/crash-free-and-still-broken\/","guid":"https:\/\/codebyramiz.com\/blog\/crash-free-and-still-broken\/","pubDate":"Wed, 12 Aug 2026 00:00:00 GMT","description":"Your dashboard says 99.5% crash-free and your reviews say the app doesn't work. Both are true, because crashes are the failure class users actually forgive \u2014 they're loud, they're recoverable, and the app tells you about them. The expensive ones are silent: a send queue stuck in a state nothing reports, a concurrent write that leaves two people believing different things, a query that quietly stops matching real data. Nobody files a ticket for those. They just leave. Here's what those failures look like from the inside, and what to instrument so the app tells you instead of your reviews.","author":"hello@codebyramiz.com (Ramiz Raja)","category":["Android","iOS","Stability"]},{"title":"Your App Hasn't Shipped in a Year. The Problem Usually Isn't the Code.","link":"https:\/\/codebyramiz.com\/blog\/your-app-hasnt-shipped-in-a-year\/","guid":"https:\/\/codebyramiz.com\/blog\/your-app-hasnt-shipped-in-a-year\/","pubDate":"Wed, 05 Aug 2026 00:00:00 GMT","description":"Your app hasn't shipped an update in a year, and everyone has quietly agreed that's fine because nothing is on fire. It isn't fine: a frozen app is on a clock set by Apple and Google, not by you. In four rescues the blocker was never that the code couldn't be changed \u2014 it was a pipeline nobody could run, a build only one person could produce, a store gate nobody had decoded, or a platform floor that rose while the team stood still. Here's what's actually decaying, and the order I'd unblock it in.","author":"hello@codebyramiz.com (Ramiz Raja)","category":["Android","iOS","Release"]},{"title":"You Cannot Test Android on Every Phone. Here's What to Prove.","link":"https:\/\/codebyramiz.com\/blog\/your-app-does-not-need-every-phone\/","guid":"https:\/\/codebyramiz.com\/blog\/your-app-does-not-need-every-phone\/","pubDate":"Wed, 29 Jul 2026 00:00:00 GMT","description":"You cannot test an Android app on every phone. The device matrix is unbounded, and it changes without you shipping anything \u2014 a third-party update can break a flow that was proven working last month. So what do you actually have to prove? A four-device bench with a written ledger of what is and isn't covered, field telemetry filtered hard enough to trust, fallbacks that fail loudly instead of silently, and OEM work that expands only when a recorded failure calls for it.","author":"hello@codebyramiz.com (Ramiz Raja)","category":"Android"},{"title":"App Store Rejection for Requiring WhatsApp: Fixing Guideline 4.2.3(i)","link":"https:\/\/codebyramiz.com\/blog\/surviving-app-store-guideline-4-2-3i\/","guid":"https:\/\/codebyramiz.com\/blog\/surviving-app-store-guideline-4-2-3i\/","pubDate":"Wed, 22 Jul 2026 00:00:00 GMT","description":"Apple rejected my app under Guideline 4.2.3(i) \u2014 Minimum Functionality \u2014 for requiring WhatsApp to be installed. Building a complete standalone send path wasn't enough: the next build was rejected on the same guideline. What actually cleared it was the empty first-launch screen, and an appeal made of screenshots instead of argument. What the guideline is really testing, and the five things I'd check before you resubmit.","author":"hello@codebyramiz.com (Ramiz Raja)","category":"iOS"},{"title":"How I Review AI Changes in a Production Mobile App","link":"https:\/\/codebyramiz.com\/blog\/two-agents-one-repo\/","guid":"https:\/\/codebyramiz.com\/blog\/two-agents-one-repo\/","pubDate":"Mon, 13 Jul 2026 00:00:00 GMT","description":"The dangerous thing about an AI coding agent isn't that it writes bad code \u2014 it's that it will tell you the work is done when it isn't. A skipped test reported as passing. A readiness summary that says PASS over a build that actually died. The method that's held up for me on a production mobile codebase: split the planner from the coder so nothing approves its own work, and keep the state that matters in files instead of the chat. The loop, the artifacts, and the four questions to ask anyone putting AI on your app.","author":"hello@codebyramiz.com (Ramiz Raja)","category":["AI-Assisted","Multi-Agent","Workflow"]},{"title":"How to Know an Android App Audit Actually Found Nothing","link":"https:\/\/codebyramiz.com\/blog\/the-audit-that-found-nothing\/","guid":"https:\/\/codebyramiz.com\/blog\/the-audit-that-found-nothing\/","pubDate":"Fri, 10 Jul 2026 00:00:00 GMT","description":"A full audit of an Android codebase came back with one line: no verified bugs found. That's either the best sentence in engineering or the most worthless one \u2014 and the difference is entirely in the process. Here's what had to happen first before I'd believe it.","author":"hello@codebyramiz.com (Ramiz Raja)","category":["Android","Testing","AI-Assisted"]},{"title":"Before You Rewrite an Inherited App, Stabilize It","link":"https:\/\/codebyramiz.com\/blog\/stabilize-before-you-rewrite\/","guid":"https:\/\/codebyramiz.com\/blog\/stabilize-before-you-rewrite\/","pubDate":"Wed, 08 Jul 2026 00:00:00 GMT","description":"You inherited an unstable mobile app and someone has already said 'just rewrite it.' A rewrite trades a known set of bugs for an unknown one and deletes years of edge-case handling that exists nowhere else. Here's the five-step playbook I run instead \u2014 profile before touching anything, kill the worst pain in place, add boundaries so fixes hold, repair the release pipeline, and only then decide whether a rewrite has actually been earned.","author":"hello@codebyramiz.com (Ramiz Raja)","category":["Android","iOS","Flutter"]}]}}