Mesrai
Back to blog
// essayTechnical Deep Dive

The Breaking API Change Mesrai Caught Pre-Merge

Real PR catch: endpoint rename with live mobile callers on old path. Transitional alias + Deprecation header fix.

Mesrai TeamAugust 15, 20268 min read

A PR renaming an avatar endpoint. The author updated the web client in the same PR. Mesrai cross-referenced the old route against `client/ios/` and `client/android/` and found three live app versions still calling the original. Breaking change caught pre-merge.

The vulnerable diff

tsapi/users-photo.ts
// api/users-photo.ts (the rename PR)
- app.get("/users/:id/avatar", requireAuth, getAvatar);
+ app.get("/users/:id/photo",  requireAuth, getAvatar);

// client/web/UserProfile.tsx (also updated in PR — fine)
- fetch(`/api/users/${id}/avatar`)
+ fetch(`/api/users/${id}/photo`)

// client/ios/UserViewModel.swift (untouched — still calls old route)
let url = "/api/users/\(id)/avatar"     // → 404 in production after merge

What is wrong

REST endpoints are part of the public contract of the service, even for first-party clients. Mobile clients carry an install base that updates over weeks or months — a route rename without an alias breaks every old client at deploy time. The fix is a transitional alias: keep the old route working for at least one full mobile-release cycle (typically 30-90 days), then deprecate, then remove. The deprecation window is a rollout boundary the team agrees on; the code change is a one-line alias or 301 redirect.

The attack

Production timeline without alias:

textexploit
t+0 — Deploy. Web works. iOS 4.12 (40% of users) starts 404ing.
t+0:15 — Pager. Rollback.
t+1d — Rebuild PR with alias.
t+8d — Ship mobile update.
t+90d — Telemetry confirms < 1% on old route. Safe to remove alias.

Alias-first is the only path that doesn't burn engineering goodwill on every rename.

Mesrai's review comment

textreview-comment.txt
mesraipilot · Bot · reviewed 90 sec ago

[mesrai] [code-review] [Logic] [Breaking-Change] [critical]

Renaming `/users/:id/avatar` → `/users/:id/photo`. Code-search shows
old path still referenced in:

  - client/ios/UserViewModel.swift:42
  - client/android/data/User.kt:88
  - client/mobile-web/Profile.tsx:12

Mobile builds in the wild can't update overnight. Add a transitional
alias for at least 90 days, then remove in a follow-up PR:

  app.get("/users/:id/photo",  requireAuth, getAvatar);
  app.get("/users/:id/avatar", requireAuth, getAvatar);  // deprecated alias

Optionally include a Deprecation header so client telemetry can find
remaining callers:

  res.set("Deprecation", "version=2026-09-01");
  res.set("Link", '</users/:id/photo>; rel="successor-version"');

Add a follow-up issue to remove the alias once telemetry shows it's
unused (90 days, typically).

The fix

tsapi/users-photo.ts (fixed)
// api/users-photo.ts — fixed
const photoHandler = async (req, res) => { /* ... */ };
app.get("/users/:id/photo",  requireAuth, photoHandler);
app.get("/users/:id/avatar", requireAuth, (req, res, next) => {
  res.set("Deprecation", "version=2026-09-01");
  res.set("Link", `</users/${req.params.id}/photo>; rel="successor-version"`);
  return photoHandler(req, res, next);
});

The alias serves the same handler, with a Deprecation header so client telemetry can identify legacy callers. After 90 days of low traffic, the alias is removed in a follow-up. Mobile rollout cadence drives the removal date — never the merge date of the rename.

Why human review missed it

Renaming endpoints feels like a refactor, but it's a contract change. Reviewers who own only the web client miss the mobile/native callers. Mesrai catches the pattern by cross-referencing the removed route against every client codebase in the monorepo.

Related rules + further reading

Mesrai rule pack: logic/breaking-route-rename — flags route removal/rename with active callers in any sibling client codebase.

RFC 8594: The Deprecation HTTP Response Header.

Apollo + Stripe deprecation playbooks both formalize the alias-then-remove pattern.

Takeaway

Aliases are mandatory on route renames. 90-day window. Telemetry-driven removal. Mesrai catches every rename without an alias.

// try

See it on your next PR.

Free for individuals. Install in two minutes. Mesrai reviews every commit.