Model Retirement Survival Guide: A "Model Swap" SOP for Solo Devs
October brought two Copilot retirement waves, GPT-5.5 leaving Codex, and 17 OpenAI API model identifiers shutting off — model swaps are now operations, not upgrades. This field guide delivers a complete swap SOP: three abstraction layers, a 30-minute minimum eval baseline, a four-step shadow-to-full rollout, 10-minute weekly notice monitoring, the true cost formula, three real failure modes, and a one-page copy-paste checklist.

On the morning of October 2, GitHub Copilot retired four models in a single day. If you opened VS Code that day, a few familiar names were simply gone from the model picker — we covered the news itself in "GitHub Copilot Retires Four Models in One Day: AI Coding Enters the 'Model Swap' Era". But the news only told half the story: after a retirement notice goes out, the real work begins — every place in your project where a model name is hardcoded has to be swapped, tested, and rolled out behind a gradual migration before the deadline.
And October is just the beginning. GPT-5.5 retires from Codex on October 14, six more Copilot models go on October 19, and the OpenAI API shuts off another batch of 17 model identifiers on October 23. In other words, a typical solo project may be forced through two or three model swaps this month alone. This is not a tech upgrade — it is operations. Like renewing a server or rotating a certificate, model retirement has become a fixed, recurring event in the lifecycle of every AI application.
This guide doesn't re-tell the news. It answers one question: when a retirement notice lands on your desk, do you have a "model swap" SOP? — covering abstraction layers, eval baselines, gradual rollouts, announcement monitoring, cost accounting, and a one-page checklist, all copy-paste ready. The core argument is one sentence: a project without a swap SOP turns every model retirement into a minor production incident; a project with one treats a swap as just another release.
1. The Retirement Wave, Mapped: Models Are Dying Faster Than You Ship
Here's the timeline. Every item below is a real retirement event from the last two weeks — announced or already executed — each traceable to an official changelog:
- Oct 2 (executed) — GitHub Copilot wave one: Gemini 3.5 Flash, Gemini 3.6 Flash, Kimi K2.7 Code, and Claude Opus 4.7 were removed from Copilot Chat, inline edits, Ask/Agent modes, and code completions, with official replacement mappings (Gemini 3.8 Flash, Kimi K3, Claude Opus 5.5).
- Oct 14 — GPT-5.5 retires from Codex: OpenAI announced GPT-5.5's retirement from ChatGPT, ChatGPT Work, and Codex (ChatGPT sign-in), pointing users toward GPT-5.6 Sol or GPT-6 Astra. Note the critical detail in this one — expanded below.
- Oct 19 — GitHub Copilot wave two (announced Sept 18): Gemini 3.7 Flash, GPT-5.5, GPT-5.4, GPT-5.4 mini, GPT-5 mini, and Grok 4.5 retire, succeeded by Gemini 3.8 Flash, GPT-5.6 Sol, GPT-5.6 Luna, and Grok 4.6.
- Oct 22 — Google Veo 3.1 preview video models end: if your AI video feature runs on the preview models, that line expires this month too.
- Oct 23 — OpenAI API shutoff wave: 17 model identifiers stop serving, including gpt-4-0613, gpt-4-turbo, gpt-4o-2024-05-13, gpt-4.1-nano, gpt-3.5-turbo-0125, gpt-image-1, o1, o1-pro, o3-mini, and o4-mini (deprecated since April 22, 2026; October 23 is the shutoff date).
- Nov 30 — Anthropic retires a Claude Sonnet 4.5 snapshot: claude-sonnet-4-5-20250929 retires, replaced by claude-sonnet-5-5. Anthropic's policy — at least 60 days' notice for publicly released models — is the most developer-friendly of the three vendors.
- Jan 6, 2027 — OpenAI self-serve fine-tuning wind-down: if you have fine-tunes on old base models, the window for new jobs is closing. Migrating a fine-tune is harder than migrating a base model — schedule it separately.
Three conclusions fall out of this timeline, and each one shapes your SOP design:
Conclusion one: "product-surface retirement" and "API shutoff" are two different tracks — don't conflate them. The GPT-5.5 notice is the perfect teaching case: it retires from ChatGPT and Codex (ChatGPT sign-in), but the OpenAI API is unaffected — gpt-5.5 called with an API key keeps working. Conversely, the October 23 batch is a pure API shutoff that ChatGPT users will never feel. The most common solo-dev mistake is either panicking at the words "GPT-5.5 retired" or assuming "API unaffected" means you're safe. Your first move is always the same: figure out which channel your calls go through, then decide whether to move at all.
Conclusion two: median model lifespan is shrinking fast. GPT-5.5 launched on April 23, 2026 and retires October 14 — a flagship model with a lifespan under six months. Grok 4.5 is more extreme: on the October 19 retirement list barely two months after launch. We used to plan model choices by the year; now plan by the quarter. Your architecture must assume any model can disappear within six months.
Conclusion three: retirement is turning from "announcements" into "a pipeline." GitHub's two waves are 17 days apart; OpenAI scheduled four waves within a year. Anthropic even pre-assigns every model a tentative "no earlier than" retirement date. The "wait for the notice, then scramble" model is already obsolete — you need standing monitoring and a swap procedure that's ready to run at any time. That's what the following sections build.
2. Three Layers of Swap Abstraction: Get Model Names Out of Your Code
The conclusion first: a concrete model name should appear exactly once in your codebase — in a config file. If a global search for "gpt-5" or "claude-sonnet" today returns hits scattered across a dozen files, every retirement turns into a game of hide-and-seek, and each missed spot is a production incident.
The three abstraction layers each solve one class of "can't swap" problem:
Layer one: provider abstraction — "whose API you call" is configuration, not code. Every model call goes through one complete(messages) interface, backed by adapters for OpenAI-compatible, Anthropic, Gemini, and others. Switching vendors means changing one environment variable, not rewriting business logic. There's an underrated bonus: when a vendor has an outage or a price hike, your escape hatch already exists.
Layer two: model abstraction — "how smart a model you need" is a logical role, not a concrete ID. Don't write model="gpt-5.6-sol" in code; write role="smart" / role="fast" / role="cheap", translated to concrete model IDs by a mapping table. When a retirement hits, you edit one mapping table. During gradual rollout, you can route 5% of the "smart" role's traffic to the new model while "fast" stays put — precision down to the task type.
Layer three: prompt abstraction — versioned prompt templates, decoupled from models. Don't scatter prompts as string literals in business code; centralize them in a prompts/ directory versioned like task-name.v1.md. The most common rework in a swap is "the old prompt behaves differently on the new model" — with versioning, you keep v1 (old model) and v2 (tuned for the new model) side by side, route by model during the gradual phase, and roll back at zero cost.
Someone will ask: isn't three layers of abstraction over-engineering? For a 2023 team, maybe. For a 2026 solo dev, no. The reason is simple: in the age of AI-written code, the "writing cost" of an abstraction layer approaches zero — the boilerplate is written by AI and you just review it — while what the layer saves you is your own late-night hours during a swap. Abstractions written by AI and maintained with AI only get cheaper over time. The old constraint (hand-writing boilerplate is exhausting) no longer exists.
Here's a minimal, ready-to-use implementation (TypeScript, runs on Node 18+ with zero dependencies):
// llm.ts —— swap layer one: provider abstraction
export interface ChatMessage { role: "system" | "user" | "assistant"; content: string; }
export interface LLMResult { text: string; inputTokens: number; outputTokens: number; latencyMs: number; }
export interface LLMProvider {
name: string;
complete(messages: ChatMessage[], opts?: { maxTokens?: number }): Promise<LLMResult>;
}
// OpenAI-compatible interface: one implementation serves OpenAI / DeepSeek / Kimi / most Chinese models
export class OpenAICompatible implements LLMProvider {
name = "openai-compatible";
constructor(private baseUrl: string, private apiKey: string, private model: string) {}
async complete(messages: ChatMessage[], opts: { maxTokens?: number } = {}): Promise<LLMResult> {
const t0 = Date.now();
const res = await fetch(`${this.baseUrl}/chat/completions`, {
method: "POST",
headers: { "Authorization": `Bearer ${this.apiKey}`, "Content-Type": "application/json" },
body: JSON.stringify({ model: this.model, messages, max_tokens: opts.maxTokens ?? 1024 }),
});
if (!res.ok) throw new Error(`LLM ${res.status}: ${await res.text()}`);
const data = await res.json();
return {
text: data.choices[0].message.content,
inputTokens: data.usage?.prompt_tokens ?? 0,
outputTokens: data.usage?.completion_tokens ?? 0,
latencyMs: Date.now() - t0,
};
}
}
// Layer two: logical role → concrete model ID map (on retirement day, edit only this)
const MODEL_MAP: Record<string, string> = {
"smart": process.env.MODEL_SMART ?? "gpt-5.6-sol",
"fast": process.env.MODEL_FAST ?? "gpt-5.6-luna",
"cheap": process.env.MODEL_CHEAP ?? "gpt-5.4-mini",
};
export function createProvider(role: keyof typeof MODEL_MAP = "smart"): LLMProvider {
return new OpenAICompatible(
process.env.LLM_BASE_URL ?? "https://api.openai.com/v1",
process.env.LLM_API_KEY ?? "",
MODEL_MAP[role],
);
}
Note the deliberate design: business code only ever calls createProvider("smart"); model IDs live solely in MODEL_MAP and environment variables. In an extreme case like Copilot's October 19 wave — six models gone in a day — your change surface is a few environment variables plus one eval run (next section), not a repo-wide grep.
3. No Baseline, No Swap: A Minimum Viable Eval in 30 Minutes
This is the easiest step to skip and the most fatal one. Swapping without a baseline means A/B testing on your users. Our site's Evals guide covers the full evaluation discipline — that's "everyday quality engineering." What follows is its emergency subset: the "minimum viable baseline" — buildable in 30 minutes, serving swaps specifically, answering one question: "is the new model at least as good as the old one on my real tasks?"
A minimum baseline needs exactly three things:
1. A golden set: 20–50 real production cases. No synthetic data — pull from your production logs: real user inputs from the last two weeks covering your core task types. Each case records only three things: id, input, and expected "features" (not verbatim gold answers). Express features as two rules: must_include (keywords the output must contain) and must_not_include (anti-patterns that must never appear, like bare URLs or "as an AI" boilerplate). Twenty is the floor — below that, random noise drowns out real differences.
2. Four mandatory metrics. Every baseline run records, beyond pass rate: p50/p95 latency, input+output token counts, and per-case cost. A swap decision is never "pass rate looks the same" — it's all four metrics together. A new model with 2% higher pass rate at double the price may be a loss for a solo team (Section 6 does that math).
3. A one-command eval run. A baseline you can't run with one command is a baseline you won't run at 2 AM. This script (a single Node file, zero dependencies) is the whole thing:
// eval.mjs —— usage: LLM_BASE_URL=... LLM_API_KEY=... LLM_MODEL=... node eval.mjs golden.jsonl
import { readFileSync } from "node:fs";
const cases = readFileSync(process.argv[2], "utf8").trim().split("\n").map(JSON.parse);
let pass = 0, tokens = 0, latency = 0;
for (const c of cases) {
const t0 = Date.now();
const res = await fetch(`${process.env.LLM_BASE_URL}/chat/completions`, {
method: "POST",
headers: { "Authorization": `Bearer ${process.env.LLM_API_KEY}`, "Content-Type": "application/json" },
body: JSON.stringify({
model: process.env.LLM_MODEL,
messages: [{ role: "user", content: c.input }],
max_tokens: 1024,
}),
});
const data = await res.json();
const text = data.choices[0]?.message?.content ?? "";
latency += Date.now() - t0;
tokens += data.usage?.total_tokens ?? 0;
const ok = (c.must_include ?? []).every(k => text.includes(k))
&& !(c.must_not_include ?? []).some(k => text.includes(k));
if (ok) pass++;
else console.log(`FAIL [${c.id}]: ${text.slice(0, 120)}...`);
}
console.log(`pass rate ${pass}/${cases.length} (${(pass / cases.length * 100).toFixed(1)}%)`);
console.log(`avg latency ${(latency / cases.length).toFixed(0)}ms · total tokens ${tokens}`);
A golden.jsonl looks like this, one case per line:
{"id": "summarize-1", "input": "Summarize this changelog in one sentence: v2.3 fixed garbled CSV exports…", "must_include": ["fixed"], "must_not_include": ["http"]}
{"id": "classify-2", "input": "User says 'this is garbage, I want a refund' — classify as praise/complaint/refund", "must_include": ["refund"], "must_not_include": ["praise"]}
The usage in a swap is fixed: run the old model first, record the three baseline numbers (pass rate / latency / per-task cost); run the new model on the same set; compare. The decision line is fixed too: a pass-rate drop of more than 5 percentage points is an instant reject; if pass rate holds or rises, move on to cost and latency (Section 6). Run this baseline monthly in normal times (10 minutes); at swap time it's your only objective evidence. Remember: "the new model feels fine" is the most expensive hallucination — only baseline numbers count.
4. The Four-Step Gradual Rollout: Shadow → 5% → 50% → Full
Passing the baseline doesn't mean you can go full traffic. Baselines use historical cases; production traffic always contains inputs you've never seen. Gradual rollout handles the "unknown unknowns" — four steps, each with explicit continue/rollback conditions:
Step one: shadow traffic (1–3 days). Send 100% of production requests to the new model in parallel, but only log its results — never return them to users. This is the zero-risk step: it replays the baseline comparison against the real traffic distribution. Implementation is a wrapper function:
// shadow.ts —— shadow traffic: users always get the old model's answer; the new model only gets scored
import type { ChatMessage, LLMProvider, LLMResult } from "./llm.js";
export async function completeWithShadow(
primary: LLMProvider, shadow: LLMProvider, messages: ChatMessage[],
): Promise<LLMResult> {
const main = await primary.complete(messages);
// Shadow request: non-blocking, never throws, logs only
shadow.complete(messages).then(
(s) => console.log(JSON.stringify({
shadow: true, latencyMs: s.latencyMs, tokens: s.inputTokens + s.outputTokens,
primaryHead: main.text.slice(0, 200), shadowHead: s.text.slice(0, 200),
})),
(e) => console.log(JSON.stringify({ shadow: true, error: String(e).slice(0, 200) })),
);
return main;
}
During the shadow period, review the logs daily, watching three things: the new model's error rate, average latency vs. the old model, and output length distribution (suddenly longer usually means verbosity; suddenly shorter usually means corner-cutting).
Step two: 5% of real traffic (3–7 days). Split by user-ID hash; 5% of users genuinely go through the new model. From here the new model's output affects real users, so monitoring gets serious: error rate, p95 latency, per-task cost, user feedback (complaints/negative ratings). At 5%, even total failure has a contained blast radius.
Step three: 50% of traffic (at least one full business cycle). For a solo team, a "full business cycle" usually means 7 days — covering weekday and weekend traffic patterns. The focus shifts from "nothing breaks" to "quality comparison": sample 50 real outputs from each model, score them blind (without knowing which model produced which), and confirm there's no systematic quality regression.
Step four: full rollout — but keep the old model's config for two weeks. Full rollout isn't the finish line; the rollback path is. Keep the old MODEL_SMART env var, the old v1 prompt templates, and the old vendor keys for 14 days, labeled "rollback use, deletable after [date]." If things go wrong, rollback = one env var change + restart, done in under a minute.
Rollback triggers (any single one fires an immediate rollback — no meeting needed):
- Error rate (5xx / timeouts / empty responses) rises more than 2 percentage points above baseline;
- p95 latency increases more than 30% above baseline;
- Average per-task cost rises more than 50% with no matching quality gain;
- Golden-set pass rate drops more than 5 percentage points;
- User complaints double within 48 hours.
Write these five lines into your SOP doc and stick them on your monitor. Their biggest value isn't technical — it's psychological: at 3 AM, when the alert fires, you don't "wait and see," you execute. A solo team has no on-call buddy to sanity-check your judgment, so the judgment has to be made in advance.
5. Retirement-Notice Monitoring: A 10-Minute Weekly SOP
Half of a swap SOP actually happens before the announcement. The most damaging thing about a retirement notice isn't its content — it's that you never saw it. The 17 models in OpenAI's October 23 API shutoff were marked deprecated back on April 22 — a full six-month window — yet every cycle, teams discover their fine-tunes are dead on shutoff day.
A solo team doesn't need a media-monitoring stack. It needs a fixed 10-minute weekly ritual:
The monitoring checklist (4 pages + 1 command):
- OpenAI deprecations page (platform.openai.com/docs/deprecations): the master table of API model retirements, with shutoff dates down to the day; fine-tune wind-downs live here too;
- Anthropic model deprecations page (docs.anthropic.com/en/docs/about-claude/model-deprecations): at least 60 days' notice for public models, plus a tentative "no earlier than" retirement date per model for advance planning;
- GitHub Changelog (github.blog/changelog): Copilot model retirements debut here — both October waves were changelog-first;
- The Gemini API deprecations page in Google's AI developer docs: the retirement schedule for the Gemini family and Veo video models;
- One grep across your own repo:
grep -rn "gpt-\|claude-\|gemini-\|grok-" --include="*.ts" --include="*.py" --include="*.yaml" --include="*.env*" .— verify the model names actually in production match the ones you think are in production.
The Monday-morning 10-minute flow: 5 minutes skimming the 4 pages, looking only for models in your stack with dates in the next 90 days; 2 minutes running the grep; 3 minutes putting any model expiring within 90 days on your calendar (reminders at 30 days and 7 days out). If you want more automation, the open-source community has tools like model-eol — "Dependabot for models" — turning vendor retirement schedules into a machine-readable feed that runs in CI, exactly like Dependabot nudging you about stale dependencies.
One more subtlety: treat "retirement notices" and "price-cut / new-model announcements" as separate workflows. A new model launch (say, GPT-5.6 Sol) doesn't mean the old model retires tomorrow — there's usually a 30–90 day overlap window. That window is your golden swap period: the new model is available, the old one still serves, and the four gradual steps run at their most comfortable pace. If you wait for the retirement notice to start, you've already burned half that window.
6. Re-Counting the Cost: The True Price Formula of a Swap
Let's talk money. If your swap accounting only compares "new model price vs. old model price," it will be wrong — the three missing terms (re-testing, rework, dual-running) usually cost more than the price delta itself. Here's the full formula:
Total swap cost = Δunit-price × monthly tokens × 12 + re-test cost + rework cost + dual-run cost
- Δunit-price = (new blended unit price − old blended unit price). "Blended" means weighted by your actual input/output ratio — don't compare headline input prices if your workload is output-heavy; the output price is what dominates;
- Re-test cost = golden cases × cost per run × rounds (typically 3–5) + your labor hours × your hourly rate. Don't zero out the labor: reading FAIL logs, tuning prompts, and confirming false positives all take time;
- Rework cost = prompt-rewrite hours × hourly rate + regression bug fixes. Rule of thumb: number of prompt templates × 0.5–2 hours each × your rate. Price a solo dev's hour at your freelance market rate, not at zero — your time is the project's most expensive resource;
- Dual-run cost = extra tokens during the shadow phase. Shadow at 100% traffic effectively doubles that month's model bill — budget it upfront.
A worked example (numbers are purely illustrative, to demonstrate the formula): your support bot does 8M input + 2M output tokens a month; old blended price $3/M tokens, new $5/M — Δ = $2. At 10M monthly tokens that's $20/month extra, $240/year. Re-testing: 40 cases × 4 rounds costs almost nothing in tokens, but 4 hours of your time reading results and tuning prompts at $50/hr = $200. Rework: 6 prompt templates × ~1 hour each = $300. Dual-run: 2 weeks of shadow ≈ half a month doubled ≈ (10M × $5/M) × 0.5 = $25. Total: ≈ $240 + $200 + $300 + $25 = $765 — and the price delta is only a third of it.
The point of the example: for a small team, swaps are expensive mainly in human time, not tokens. "The new model is 20% cheaper" is not a reason to swap (the token savings may not cover your re-test time); "the old model is being retired" is — that's forced spend, and the goal is minimizing total cost, not chasing unit price.
One more counterintuitive conclusion: compare "cost per task," not "cost per token." A new model 50% pricier per token but lifting first-try success from 70% to 95% (fewer retries, less human fallback) can lower per-task cost. If your golden set records "average tokens per task, retries included," you can compute this directly. Many "the new model is more expensive" complaints use the wrong dimension.
7. Three Real-World Failure Modes: Baselines Passed, Production Still Broke
Even with all six sections above in place, three failure modes slip past baselines. Each is dangerous because its root cause isn't model quality — it's in your engineering assumptions:
Failure one: silent degradation. Symptom: you changed nothing, yet output quality quietly shifted. Two root causes — vendors aliasing an old model name to new weights (same name you call, different weights underneath), or "automatic migration" policies that move your traffic to the new model ahead of the deadline for smoothness. Neither trips your rollback lines, because your monitors watch error rates, and silent degradation has no errors — only a changed "flavor." Countermeasure: re-run the golden set monthly (the Section 3 one-liner) and diff pass rates plus output-length distributions; for core tasks, add an output-embedding similarity check — semantic drift is visible in the numbers.
Failure two: prompt fragility exposed. Symptom: the new model benchmarks higher but performs worse on your tasks. The uncomfortable root cause: your prompt was always wrong — the old model just covered for you. The classic case: your prompt says "return JSON" but not "return ONLY JSON, no explanation"; the old model "happened" to return bare JSON, the new one follows instructions strictly, dumps its reasoning too, and your downstream parser explodes. The old model's "leniency" masked sloppy prompting; the swap ripped the cover off. Countermeasure: treat prompts as "inputs to re-validate" during a swap, not "assets that don't move"; add "no explanatory prose" to your golden set's must_not_include to force rigorous prompting.
Failure three: long-tail capability loss. Symptom: aggregate metrics all improve, but one niche feature quietly breaks. Root cause: public benchmarks (SWE-bench, MMLU) measure general capability, while you may depend on a long-tail one — Thai-language support scripts, parsing an obscure data format, recall at a specific position in ultra-long contexts. The new model can regress on exactly those points while your 40 golden cases happen not to cover them. Countermeasure: reserve 20% of the golden set for "long-tail cases" — tasks that fire a few times a month but matter enormously when they do; and in the 50%-traffic step's blind human review, deliberately oversample long-tail tasks.
All three share one trait: they are things the old model was silently doing that engineering should have owned — aliasing fallbacks, prompt leniency, long-tail memory. Sometimes a swap's real value isn't the new model at all — it's forcing those hidden dependencies into explicit engineering.
8. The One-Page Swap SOP: Copy and Use
The previous seven sections, compressed into one checklist. The moment a retirement notice arrives, print it (or copy it into your task manager) and check items off:
Model Swap SOP —— trigger: any upstream model retirement/shutoff notice
┌─ T+0 (notice day, 30 min)
│ [ ] Confirm retirement scope: product-surface retirement or API shutoff? (the GPT-5.5 lesson: two tracks)
│ [ ] Confirm which channel your calls use: API key / ChatGPT sign-in / Copilot / third-party gateway
│ [ ] Grep the repo for model names; list every reference (configs, prompts, docs, runbooks)
│ [ ] Create three calendar reminders: T-30, T-7, T-1 (relative to shutoff day T)
│ [ ] Assess impact: not affected → file the notice, done; affected → continue
├─ T-30 (30 days before shutoff)
│ [ ] Verify the three abstraction layers: do model names appear only in config / MODEL_MAP?
│ [ ] Run the golden set on the OLD model; record the three baseline numbers: pass rate / p95 latency / per-task cost
│ [ ] Pick the replacement; run the golden set on the NEW model; compare (pass-rate drop > 5pp = instant reject)
│ [ ] Compute total swap cost (Section 6 formula); confirm budget
│ [ ] Version prompt templates: v2 tuned for the new model, v1 kept for rollback
├─ T-7 (gradual rollout begins)
│ [ ] Ship shadow traffic (Section 4 script); run 1–3 days; review the three log metrics
│ [ ] Cut 5% of real traffic; hash-split by user ID; watch the five rollback lines
│ [ ] Cut 50% of traffic; cover one full business cycle (usually 7 days)
│ [ ] Blind human review of 50 real outputs; oversample long-tail tasks
├─ T-1 (1 day before shutoff)
│ [ ] Full cutover; keep old model config / old prompt v1 / old keys for 14 days (labeled "rollback use")
│ [ ] Update model-name references in runbooks and docs
│ [ ] Announce to team (if any) / users: flag any perceptible behavior change in advance
└─ T+7 / T+30 (review)
[ ] T+7: compare post-cutover metrics vs. baseline; confirm no silent degradation (re-run golden set)
[ ] T+30: delete leftover old-model config; archive this swap's record (model pair, dates, baseline numbers, gotchas)
[ ] Write this round's lessons into prompt-template comments — one fewer pitfall next swap
One last uncomfortable truth: this SOP's biggest enemy isn't complexity — it's the "we probably don't need it this time" instinct. In October's retirement wave, the hardest-hit were always the projects that "deferred it until shutoff day" — at which point you're no longer running a calm migration but fighting a midnight fire. Model retirement is operations now. Operations deserve an SOP. File this checklist away, and the next time a notice arrives, you'll go from "panic" to "execute" in 30 minutes.
Comments (0)
Related articles

Your half-dead side project is worth more than you think. This field guide covers when to sell, three valuation methods for micro-projects, a 30-day value-boost checklist, a four-channel marketplace comparison, how to write a listing buyers trust, escrow and handover mechanics, and dignified plan Bs when it doesn't sell.

For solo teams, a native app is not a starting point but a growth tax — only worth paying in front of validated demand. This guide argues why the AI era favors web-first, lists the five exceptions that truly require native, and gives you a three-dimensional scoring matrix, a 2026 PWA capability map, four data triggers for going native, three migration paths, a weekly maintenance-tax comparison, and a 10-question decision template for a conclusion in 30 minutes.

You can ship code but break into a sweat at 'want to chat?' Indie-developer sales isn't a persuasion performance — it's a structured way to help users decide: discovery, diagnosis, demo, objection handling, and closing as five repeatable moves, with copy-paste call scripts, a follow-up cadence table, and a lightweight CRM board. Introverts can sell too.