DEV Community

Roronoa
Roronoa

Posted on

Replay the Bug Before You Read the Model Code: A Junior's First-Hour Harness for a Mobile AI Repo

The ticket said: "Transcription stalls after screen lock, then the queue never drains." No device model, no OS version, no network state, no permission state — just one sentence and a screenshot of a spinner. The junior engineer who picked it up spent the morning reading model integration code, because the only machine with a working inference endpoint belonged to a senior developer's laptop. That is a repository problem, not a junior problem: when the endpoint is a secret, the first hour of every ticket becomes archaeology.

This article is the harness I would hand a new engineer on day one. It leaves three artifacts in the repo, lets them replay a reported failure outside the app, and rehearses the rollback in the same hour. I have not measured any of the numbers below on a specific device — the code and the card are the proposal, and your recordings are the evidence.

What the first hour should leave behind

Before you read a single line of model code, produce these three things and commit them together:

  1. An endpoint switch — one build-time value with an on-device default, so a laptop with no key still builds and runs.
  2. An environment card — a small JSON file that records the device, OS, framework versions, network, permissions, power state, and app state at the moment of the failure.
  3. A replay script — one command that sends the same request to the same endpoint and saves the timing breakdown next to the response.

The point is not process for its own sake. The point is that a failure report becomes something a second person can reproduce without borrowing anyone's credentials.

Step 1 — Make the endpoint a build-time value, not a credential

Start by giving the app exactly three targets. Any more than three and the junior cannot tell which one a build used; any fewer and you cannot separate "staging is slow" from "the feature is slow."

// config/inference.ts
export type InferenceTarget = "on_device" | "staging" | "prod";

const raw = process.env.EXPO_PUBLIC_INFERENCE_TARGET ?? "on_device";

if (!["on_device", "staging", "prod"].includes(raw)) {
  throw new Error(`Unknown EXPO_PUBLIC_INFERENCE_TARGET: ${raw}`);
}

export const target = raw as InferenceTarget;

export const baseUrl =
  target === "staging"
    ? process.env.EXPO_PUBLIC_STAGING_BASE_URL
    : target === "prod"
      ? process.env.EXPO_PUBLIC_PROD_BASE_URL
      : undefined;

export const isOnDevice = target === "on_device";
Enter fullscreen mode Exit fullscreen mode

Two details matter more than they look. First, the default is on_device, so a fresh clone with an empty .env still launches and still exercises the offline path. Second, baseUrl is undefined for on-device builds, which forces the call site to handle that case instead of quietly sending traffic somewhere unexpected.

# .env.local  (never commit this file)
EXPO_PUBLIC_INFERENCE_TARGET=staging
EXPO_PUBLIC_STAGING_BASE_URL=https://your-staging-host.example
Enter fullscreen mode Exit fullscreen mode

Then add a startup assertion that logs the resolved target and the framework version once, at cold start. If a tester reports a bug, you can ask them for that one line instead of asking which branch they built.

Step 2 — Write the environment card

A report is only reproducible if it carries the conditions. Keep the card flat, human-readable, and small enough that someone will actually fill it in.

{
  "captured_at_utc": "2026-11-05T09:14:00Z",
  "device": "<model name>",
  "os_version": "<os build>",
  "app_build": "<build number and target>",
  "framework": "<framework and version>",
  "inference_target": "staging",
  "network": "wifi | cellular | none",
  "battery_percent": 0,
  "low_power_mode": false,
  "mic_permission": "granted | denied | not_determined",
  "app_state_at_failure": "foreground | background | relaunched",
  "reproduction_steps": ["..."]
}
Enter fullscreen mode Exit fullscreen mode

The values above are placeholders, not a recorded run — fill them from a real capture before you attach the card to a ticket. app_state_at_failure is the field juniors skip and seniors need, because "after screen lock" is a background transition, not a network condition.

Step 3 — Replay the request outside the app

Once the endpoint is a value, replaying is a shell script. That matters: it separates "the phone is doing something strange" from "the endpoint is doing something strange" in about a minute.

#!/usr/bin/env bash
# tools/replay.sh — replay one inference request and record what happened
set -euo pipefail
: "${INFERENCE_BASE_URL:?set INFERENCE_BASE_URL to staging or prod}"
: "${AUDIO_FILE:?set AUDIO_FILE to a short, non-user clip}"

STAMP="$(date -u +%Y%m%dT%H%M%SZ)"
OUT="artifacts/replay-${STAMP}"
mkdir -p "$OUT"

curl -sS -X POST "${INFERENCE_BASE_URL}/v1/transcribe" \
  -H "Content-Type: audio/wav" \
  --data-binary "@${AUDIO_FILE}" \
  -o "$OUT/response.json" \
  -w 'ttfb=%{time_starttransfer}s total=%{time_total}s http=%{http_code}\n' \
  | tee "$OUT/timings.txt"

echo "wrote $OUT"
Enter fullscreen mode Exit fullscreen mode

Run it twice: once on Wi-Fi, once with the phone on cellular and the laptop tethered to the same connection. You are looking for the shape of the difference, not a scoreboard — if time-to-first-byte moves by hundreds of milliseconds between runs, the endpoint or the network is your suspect, and the app's queue is probably innocent.

If the replay succeeds while the app stalls, you have narrowed the bug to the client: permission handling, background suspension, or retry behaviour. If the replay also stalls, stop reading app code.

Step 4 — Rehearse the rollback in the same hour

The third artifact is the one nobody writes until an incident forces them to. Before the day ends, flip EXPO_PUBLIC_INFERENCE_TARGET back to on_device, rebuild, and confirm three behaviours:

  • The feature still produces a result offline, or shows a specific "on-device only" message instead of a spinner.
  • A request that was in flight when the target changed does not retry forever; it either completes or fails once, visibly.
  • Revoking the microphone permission mid-session produces the same fallback, and granting it again recovers without an app restart.

Write down which of those three you observed. "Recovered", "restarted", and "silently disappeared" are different outcomes, and only the first one is a pass.

Where a free shared endpoint fits

Most juniors cannot reproduce a hosted failure because they have no paid key. A free tier fixes the access problem, not the correctness problem. MonkeyCode's free model access and free server option are one way to get a staging target up on day one, and the operator states a free allowance in the millions of tokens — check the current terms page yourself before you depend on a figure or a limit.

Disclosure: This article was prepared as part of MonkeyCode's product outreach.

Use it where it buys reproducibility, and nowhere else:

Situation Target Why
Reproducing a user's reported stall staging (shared free endpoint) one known endpoint, no paid key, any teammate can rerun it
Debugging airplane-mode behaviour on_device the network has to be genuinely absent
Latency or battery numbers for a release decision hardware you control shared capacity cannot attribute variance to your code
Real user audio or transcripts never a shared free tier consent, region, and retention are unknown to you

Limitations and who should skip this

This harness gives you reproducibility, not performance truth. A shared free endpoint has neighbours, and neighbours move; do not publish latency or energy numbers measured against it. If your product handles regulated audio, health data, or anything under a retention agreement, the free tier is out of scope regardless of convenience.

Skip the whole approach if your team already has a dedicated staging environment with recorded fixtures — you have solved the access problem the script addresses. Also skip it if there is no on-device path at all, because then the rollback rehearsal has nothing to roll back to.

What to put in your first PR

Ship the switch, the card, and the script together, plus one line in the README telling the next person the exact command to run. Then attach a real replay artifact to the original ticket, even if the bug is not fixed yet — a reproduced stall with timings is worth more than a guess in a comment.

If you run this, tell me your device, OS, and whether the fallback recovered, restarted, or silently disappeared. That comparison is the part I cannot write for you.

Top comments (0)