Launch offer: everything is 100% off. Download any skill free, no sign-up. Get the skills
TechFixSkillsBroke After Deploy
Broke After Deploy

Broke After Deploy: find what will break in production, before or after you ship

Run it before you deploy and it lists what will break on your host, with the file, the line and the fix. Paste a production error and it finds the cause in your code, then checks what will break next.

Free during launch. Works with Claude Code, Claude.ai, Cursor and Codex.

AgentsClaude Code, Claude.ai, Cursor, Codex
Read-onlyfixes only with your OK
Offlineno network calls, no account
NeedsPython 3.8+ (standard library)

When to use it

Use it in the hour before a launch, or the hour after one went wrong. It covers JavaScript and TypeScript apps in depth (Next.js, Vite, React, Node, Express and others) and Python web apps (Django, FastAPI, Flask) at a basic level.

What you type:

You haveWhat you typeWhat runs
Nothing broken yet"check this before I deploy"Scan (scripts/scan.py)
An error, a build log, or "it broke"Paste the error and say "my site broke after deploying"Diagnose (scripts/diagnose.py --project), then Scan

If you say it broke but don't paste the error, Claude asks for it in one line (browser console, build log or the host's logs) and runs the scan meanwhile. You can also type /broke-after-deploy.

How it works

  1. The script reads your project. scan.py detects the framework and the host from config files (vercel.json, render.yaml, railway.json, fly.toml and others), then checks the code for 41 known reasons an app works locally and breaks in production. In Diagnose mode, diagnose.py first matches your error against the known error messages, then checks your code for the likely causes, so a cause it can point at in your code ranks first. Both scripts are read-only and use only Python's standard library.
  2. You get a first pass within about a minute. The verdict line word for word, the host it detected, the top 3 "will break" items (what, file:line, one-line fix) and a "Not covered" line if the scan was incomplete. It is labelled "First pass, confirming now".
  3. Claude confirms every finding. It opens each "will break" line, drops wrong ones and says so. Then it walks references/review-checklist.md over the files that run in production, starting from the rows for your host. The checklist catches what patterns miss, such as a localhost URL built from constants or a helper in another file.
  4. The verdict is strict. An item is "will break" only when the repo itself proves it fails on your host. Anything that depends on settings it can't see (your dashboard variables, for example) is "check". Each cause lists safe look-alikes, like a localhost URL inside a development-only branch, and Claude rules those out before reporting. A scan that was incomplete is never reported as clean.
  5. Fixes come as diffs. One diff per "will break" item, applied only after you say yes. Fixes outside the code (dashboard variables, OAuth callback URLs, migrations against production, health check paths) come as exact steps. The production build command is offered last, as an update.

What it checks

41 causes in total. The code is checked for most of them; for the rest it tells you where to look.

Config and secrets

Hardcoded local values

Files and the filesystem

Dependencies and build

Database

Network, auth and runtime

Python (basic)

What it can't see

Worked examples

1. Diagnose: the Vercel build fails with "Module not found"

A Next.js dashboard on Vercel. It runs fine with npm run dev on a Mac. The Vercel build fails. The developer pastes the build log.

app/page.tsx (the file on disk is app/components/Header.tsx)
import Header from './components/header';
import { getStats } from '@/lib/stats';
lib/stats.ts
const API_BASE = 'http://localhost:3000';

export async function getStats() {
  const res = await fetch(`${API_BASE}/api/stats`, { cache: 'no-store' });
Developer
my site broke after deploying to vercel, build log: "Failed to compile. ./app/page.tsx Module not found: Can't resolve './components/header'"
Script output (trimmed): diagnose.py --project
**Most likely cause: An import's capitalization doesn't match the file name. Found in your code at app/page.tsx:1.**

Project: next · host: vercel (vercel.json or .vercel/ found) · 6 source files scanned

## Most likely: An import's capitalization doesn't match the file name
- **Matched:** Module not found: Can't resolve './components/header'; "module not found"; "can't resolve"
#### 1. Import './components/header' doesn't match the file name 'app/components/Header.tsx'
- **Evidence:** `app/page.tsx:1` `import Header from './components/header';`
- **Impact:** Mac and Windows treat Logo.tsx and logo.tsx as the same file. Linux build servers don't, so an import that works on your laptop is 'not found' in the build.
- **Fix:** Change the import to './components/Header' (capitalization must match exactly on Linux).
- Cause #11: An import's capitalization doesn't match the file name

## Also possible (#2): The production build fails, but the dev server hid it
…
## Also possible (#3): Your code imports a file that git ignores, so it never gets deployed
…
- Checked your code: nothing found for this cause.
Script output (trimmed): scan.py, run next for "what breaks next"
**3 things will break in production, plus 1 to check.**

Project: next · host: vercel (vercel.json or .vercel/ found) · 6 source files scanned · 0.0s

## Will break in production
### 1. Import './components/header' doesn't match the file name 'app/components/Header.tsx'
…
### 2. Hardcoded http://localhost:3000
- **Evidence:** `lib/stats.ts:1` `const API_BASE = 'http://localhost:3000';`
- **Impact:** localhost means 'this machine'. In production it points at the server itself or at each visitor's own computer, so the call fails.
- **Fix:** Use an environment variable (for example NEXT_PUBLIC_API_URL) or a relative path like /api/... instead.
- Cause #6: Your code calls localhost

### 3. Hardcoded http://localhost:3000/api/stats
- **Evidence:** `lib/stats.ts:4` `const res = await fetch(`${API_BASE}/api/stats`, { cache: 'no-store' });`
…
## Check these
### 4. Node version isn't pinned, so the host uses its default
…
Fix, applied only after you say yes
  app/page.tsx
- import Header from './components/header';
+ import Header from './components/Header';

  lib/stats.ts
- const API_BASE = 'http://localhost:3000';
+ // Set STATS_API_URL in Vercel for Production and Preview.
+ const API_BASE = process.env.STATS_API_URL;

2. Scan: a FastAPI app on Render, before the first deploy

A small notes API in FastAPI with SQLAlchemy and SQLite, deployed to Render with a render.yaml blueprint. Python gets basic checks, and the report says so.

render.yaml
    buildCommand: pip install -r requirements.txt
    startCommand: uvicorn app.main:app --host 127.0.0.1 --port 8000
    healthCheckPath: /health
app/db.py
DATABASE_URL = "sqlite:///./notes.db"

engine = create_engine(DATABASE_URL, connect_args={"check_same_thread": False})
app/main.py
app.add_middleware(
    CORSMiddleware,
    allow_origins=["http://localhost:5173"],
Developer
first time deploying this to render, will it work in production?
Script output (trimmed)
**3 things will break in production. Not a complete scan (Python checks are basic), so there may be more.**

Project: fastapi · host: render (render.yaml found) · 4 source files scanned · 0.0s

**Not covered:**
- Python support is basic: requirements, Python version, localhost and database URLs, local paths, SQLite, file writes, Django settings, gunicorn/uvicorn binding and migrations are checked. Other Python problems (type errors, import cycles, Celery/worker setup, framework-specific config) are not.

## Will break in production
### 1. Your app uses a SQLite database file. render wipes the server's disk on every deploy or restart (no disk in render.yaml).
- **Evidence:** `app/db.py:4` `DATABASE_URL = "sqlite:///./notes.db"`
…
- Cause #13: You use a SQLite file as your database on serverless hosting

### 2. CORS only allows http://localhost:5173, so your production site's requests are blocked
- **Evidence:** `app/main.py:13` `allow_origins=["http://localhost:5173"],`
…
- Cause #26: Your API only allows requests from localhost

### 3. The server listens on 127.0.0.1, so the host's proxy can't reach it
- **Evidence:** `render.yaml:6` `uvicorn app.main:app --host 127.0.0.1 --port 8000`
- **Impact:** Hosts like Render, Railway, Fly and Heroku tell your app which port to use through the PORT variable and reach it from outside the container. An app hardcoded to port 3000 or bound to 127.0.0.1 is unreachable.
- **Fix:** Use --host 0.0.0.0 --port $PORT.
- Cause #7: Your server listens on a fixed port or only on localhost
…
Fix, applied only after you say yes
  render.yaml
-     startCommand: uvicorn app.main:app --host 127.0.0.1 --port 8000
+     startCommand: uvicorn app.main:app --host 0.0.0.0 --port $PORT
      envVars:
        - key: PYTHON_VERSION
          value: 3.11.9
+       - key: DATABASE_URL
+         fromDatabase:
+           name: notes-db
+           property: connectionString
+       - key: FRONTEND_ORIGIN
+         value: https://notes.example.com
+ databases:
+   - name: notes-db

  app/db.py
+ import os
- DATABASE_URL = "sqlite:///./notes.db"
+ DATABASE_URL = os.environ["DATABASE_URL"]

  app/main.py
+ import os
-     allow_origins=["http://localhost:5173"],
+     allow_origins=os.environ.get("FRONTEND_ORIGIN", "http://localhost:5173").split(","),

  requirements.txt
+ psycopg2-binary==2.9.9

3. Scan: an Express API on Railway, with a safe localhost it should not flag

An invoice API in Express. localhost appears twice in the config, but only in the branch that runs outside production. That is a known look-alike, not a bug.

src/config.js
const isProd = process.env.NODE_ENV === 'production';

export function publicBaseUrl() {
  if (isProd) {
    return process.env.PUBLIC_URL;
  }
  return 'http://localhost:4000';
}
railway.json and src/server.js
    "healthcheckPath": "/healthz"

app.get('/health', (req, res) => res.json({ ok: true }));
const port = process.env.PORT || 4000;
Developer
check this before I deploy to railway
Script output (trimmed)
**1 thing will break in production, plus 3 to check.**

Project: express · host: railway (railway config found) · 4 source files scanned · 0.0s

## Will break in production
### 1. The railway health check calls /healthz, but no route in the app serves it, so every deploy fails its health check
- **Evidence:** `railway.json:5` `"healthcheckPath": "/healthz"`
- **Impact:** Render, Fly, Railway and App Platform only send traffic to a new deploy after its health check URL answers 200. If that path doesn't exist in the app (a typo, /healthz vs /health), every deploy fails its health check and is never promoted, even though the app itself starts fine.
- **Fix:** Point the health check at a route that exists (or add a /healthz route that returns 200).
- Cause #39: The host's health check calls a path your app doesn't serve

## Check these
### 2. PUBLIC_URL is used but not defined in any env file here
- **Evidence:** `src/config.js:5` `return process.env.PUBLIC_URL;`
…
- Cause #1: An environment variable your code uses isn't set in production
- And 2 more variable(s) like it: FRONTEND_ORIGIN, DATABASE_URL
…
Fix, applied only after you say yes
  railway.json
-     "healthcheckPath": "/healthz"
+     "healthcheckPath": "/health"

Results

TestPlanted bugsWith the skillPlain Claude
Blind test, JavaScript and TypeScript projects (Oct 13)33All 33 caught, 0 false alarms94% caught, 11 false alarms
Harder test (Oct 14) with Python, Bun, Deno, Docker and monorepo projects2619 caught as will-break, 0 false alarms23 caught, 0 false alarms
Read the second row honestly. On the harder test, plain Claude caught more. Python, Bun and Deno support is basic, so treat the skill as a checklist there, not a guarantee.

Install Broke After Deploy

Download broke-after-deploy.zip Free during launch. Checksums

You need Python 3.8 or newer where your agent runs its commands, because the checks are small Python scripts (standard library only, nothing to pip install). macOS and Linux usually have python3 already; on Windows install it from python.org.
Claude Code
  1. Download broke-after-deploy.zip (button above).
  2. Unzip it into your personal skills folder so it works in every project:
    unzip ~/Downloads/broke-after-deploy.zip -d ~/.claude/skills/
    Or into one project only (commit it to share it with your team):
    unzip ~/Downloads/broke-after-deploy.zip -d .claude/skills/
  3. Check it landed:
    ls ~/.claude/skills/broke-after-deploy/SKILL.md
  4. Use it. Claude Code picks up new skills without a restart (if ~/.claude/skills did not exist when your session started, run /reload-skills). Ask in plain words, for example “check this before I deploy” or paste the error from your host's logs, or type /broke-after-deploy.
Claude.ai (web and desktop app)
  1. Open Settings > Capabilities and make sure Code execution and file creation is on. Skills need it. (Available on Pro, Max, Team and Enterprise plans; on Enterprise an Owner enables Skills first.)
  2. In the Skills section, click Upload skill and choose broke-after-deploy.zip. Do not unzip it.
  3. Check that it appears in your Skills list and switch it on.
  4. Start a chat, attach your project as a zip (or paste the error or alert) and ask, for example “check this before I deploy”. In Claude.ai the scripts run in Claude’s sandbox on the files you upload, not on your computer.
Cursor
  1. Unzip into Cursor’s skills folder for all projects:
    unzip ~/Downloads/broke-after-deploy.zip -d ~/.cursor/skills/
    or into a project: .cursor/skills/. Cursor also reads ~/.claude/skills/, so a Claude Code install works in Cursor too.
  2. In Agent chat, type / and pick broke-after-deploy, or just ask: “check this before I deploy”.
OpenAI Codex
  1. Unzip into your user skills folder:
    unzip ~/Downloads/broke-after-deploy.zip -d ~/.agents/skills/
    or into .agents/skills/ at the root of a repo.
  2. Mention it with $broke-after-deploy (or pick it from /skills), or just ask: “check this before I deploy”.
Other agents, updates and removal
  • Any agent that reads SKILL.md files: unzip into its skills folder. Each skill is a folder with SKILL.md, scripts/ and references/.
  • Update: download the new zip and unzip it over the old one with unzip -o.
  • Remove: delete broke-after-deploy/ from the skills folder.
  • Check the download: shasum -a 256 broke-after-deploy.zip and compare with SHA256SUMS.txt.

FAQ

Does it change my code?

No, not without asking. The scripts are read-only. Fixes are shown as diffs, one per item, and applied only after you say yes. It adds no files unless you ask it to keep a record of runs, in which case it appends one line per run to .techfix/ledger.md in your project.

Does my code leave my machine?

The scripts run locally, use only Python's standard library and make no network calls (they run git locally to see which files are tracked). They never print a full secret: keys and passwords in evidence lines are shortened. Your agent reads the files it reviews, the same as any other request you make to it. In Claude.ai, the scripts run in Claude's sandbox on the files you upload.

Which stacks does it support?

JavaScript and TypeScript in depth: Next.js, Vite, React, Node, Express and others, on Vercel, Netlify, Cloudflare, Lambda, Firebase, Amplify, Cloud Run, App Engine, Azure, Render, Railway, Fly, Heroku, DigitalOcean, Docker and Deno Deploy. Python web apps (Django, FastAPI, Flask) get basic checks, and the report never calls a Python project fully checked. Bun and Deno support is basic too. Go, Ruby, PHP, Java and other stacks are not analysed: the scan lists them and says it was incomplete.

How long does it take?

The scripts take under half a second on these examples (0.10 to 0.33 seconds per run, measured for both scan and diagnose). Claude's review adds time: the first pass is posted within about a minute, then the confirmed list follows after Claude reads the files.

How is this different from just asking my agent?

Your agent knows these mistakes in general. The skill gives it a fixed list of 41 causes, a script that checks every file for them in seconds, rules for which causes break on which host, and a list of safe look-alikes to rule out before calling something a bug. On the JavaScript and TypeScript blind test, plain Claude raised 11 false alarms; with the skill, none. On Python, Bun and Deno projects the advantage is smaller (see Results).

What does it cost?

It is free during launch. The normal price is $9.