Forge
Critical issues

Find critical issues before your users do

Critical issues are the ones that lose data, money or trust: writes that half-complete, errors nobody sees, deploys that can't be rolled back and features that look finished but aren't. They hide in the paths you rarely test. Here's where to look before launch, and after every big change.

Last reviewed by the Forge team

What goes wrong

What it costs when this is missed

Critical

Lost or corrupted data

A crash halfway through a multi-step write leaves orders without payments, or accounts without owners.

High

Failures nobody sees

Swallowed errors and missing monitoring mean users find your bugs first, and most of them just leave.

High

Deploys that break

An env variable nobody documented, or a migration that can't be undone, turns a release into an outage.

Medium

Half-built features in production

A TODO on the payment path, or a stub behind a live button, looks fine in a demo and fails for real users.

Common problems

What to look for in your code

Each one: why it hurts, and how to spot it in your own project.

01Multi-step writes without a transaction

If the second write fails, the first one stays. Your data now says something that isn't true.

How to spot it: Find places that write to two or more tables (or call an external service, then write). Check they're in one transaction, or can be safely retried.

Risky
await db.order.create({ data: order });
await db.stock.update({ where: { id }, data: { count: { decrement: 1 } } });
Better
await db.$transaction([
  db.order.create({ data: order }),
  db.stock.update({ where: { id }, data: { count: { decrement: 1 } } }),
]);

02Network calls without timeouts

A slow payment or AI provider makes your requests hang, then your server runs out of connections.

How to spot it: Search for fetch and SDK calls to other services, and check each one has a timeout.

Risky
const res = await fetch(PROVIDER_URL, { method: "POST", body });
Better
const res = await fetch(PROVIDER_URL, {
  method: "POST",
  body,
  signal: AbortSignal.timeout(10_000),
});

03Errors swallowed or never reported

An empty catch block, or a log line nobody reads, hides the failure until a user complains.

How to spot it: Search for catch blocks that do nothing, and check that errors reach an error tracker or alert someone looks at.

Risky
try {
  await sendReceipt(order);
} catch {}
Better
try {
  await sendReceipt(order);
} catch (err) {
  logger.error({ err, orderId: order.id }, "receipt failed");
  await queue.retry("receipt", order.id);
}

04Migrations you can't trust

Schema changes made by hand, or destructive migrations without a backup, can't be repeated on a new environment or undone.

How to spot it: Check every schema change is a versioned migration in the repo, and that destructive ones (drop, rename) have a plan.

05Config nobody documented

If the code reads an env variable that isn't in the example file, the next deploy (or the next developer) will miss it.

How to spot it: Compare every process.env read in the code with your .env.example, and check required values fail loudly at startup.

06No tested backups

A backup you've never restored is a hope, not a backup.

How to spot it: Find out where backups go, how often, and when someone last restored one.

07Unbounded queries and jobs

A list endpoint without a limit works with 100 rows and falls over at 100,000.

How to spot it: Look for queries without a limit, and background jobs without a cap on how much they process at once.

08Unfinished work on important paths

TODOs, FIXMEs and stub functions are fine in drafts, and dangerous where users can reach them.

How to spot it: Search for TODO, FIXME and 'not implemented' along sign-up, payment and data-writing paths.

Quick self-check

How does your project do?

Tick each one that's true today. Anything left unticked is worth a closer look.

Tick what's true for your project. 9 checks.

How Forge checks this

A second opinion on your code, from the Issues scan.

  • Checks data integrity, reliability, deployment and config, observability and unfinished work, one topic at a time.
  • Gives three top actions: the things to do first, each pointing at the findings it resolves.
  • Turns any finding into a plan your coding agent can carry out.
  • Re-audits after your fixes, to confirm what's actually fixed.

Questions

What counts as a critical issue?

Anything that can lose or expose data, take money wrongly or take the product down, and anything users would hit without you noticing.

We have tests. Isn't that enough?

Tests cover the paths someone thought of. A review finds the paths no test exists for: the timeout nobody set, the write that isn't atomic, the variable nobody documented.

When should we check?

Before launch, before big releases, and after migrations, major refactors or a burst of AI-assisted changes.

Related guides

All guides

Sources