Code quality signals
Updated 2026-05-12What this is
Four code-quality signals PreFlight scans for:
console.log (and friends) in production code paths. Console statements left in code:
- bloat the bundle (minifiers don't always strip them);
- leak diagnostic data to user DevTools (sometimes including secrets);
- confuse users who happen to open DevTools.
The probe excludes test files and explicit debug paths.
Oversized files. A single source file over ~1500 lines is a yellow signal; over ~5000 lines is a red one. Oversized files correlate with the developer (or AI tool) having lost track of what's in the file, which correlates with security gaps slipping past review.
Unhandled promise rejections. A promise without a .catch() or await inside try {}:
fetch('/api/foo')
.then((r) => r.json())
.then(processData);
// network error or processData throws? Unhandled rejection.
Unhandled rejections cause silent data loss in browsers and process crashes in Node 15+.
async without try:
async function handle(req) {
const data = await fetch('/api/foo').then((r) => r.json());
return data.value;
}
If anything throws, the function rejects. The caller may or may not handle it. Often: not.
Why it matters
None of these are security vulnerabilities individually. They are signals of code paths where the developer was not thinking about failure modes, which correlates strongly with security issues clustering nearby. An empty catch (covered in AI Code Smells) plus an unhandled rejection plus a missing input validation usually appear together.
The console.log finding is slightly different. Leaving console.log(userToken) in production code path is a direct credential leak: every user with DevTools open sees the token printed in their console.
What the failure looks like
PreFlight scans for:
console.log,console.debug,console.info,console.warn,console.error,console.tracein production-source files (not insrc/test/, not in scripts excluded by the project's preflight-config).- Source files past the four-band size ladder. The bands map to PreFlight's severity scale and the HIGH band gates the dogfood scan, so the tool catches its own monoliths the same way it teaches users to catch theirs.
- 1500 LOC (
FILE_SIZE_WARN_LINES) -> low ("watch this; one more responsibility and split") - 2000 LOC (
FILE_SIZE_MED_LINES) -> medium ("architectural smell; plan a split") - 3000 LOC (
FILE_SIZE_HIGH_LINES) -> high ("split required; this gates the dogfood scan") - 5000 LOC (
FILE_SIZE_CRIT_LINES) -> critical ("emergency split; the file is hiding security logic in noise")
- 1500 LOC (
- Promise chains without trailing
.catch(). asyncfunctions whose body lacks anytryblock when the body containsawaitcalls.
What the fix looks like
Console statements: route through a logger that respects an env-driven log level, or strip at build time.
import { log } from '@/lib/logger';
log.debug('charging', { amount }); // dev only
Or in Vite:
// vite.config.js
define: {
'console.log': process.env.NODE_ENV === 'production' ? '(()=>{})' : 'console.log',
}
Oversized files: split by responsibility. A 3000-line App.jsx typically contains a router, two or three views, a handful of hooks, and several formatters. Each becomes its own file with a clear export.
Unhandled promises:
fetch('/api/foo')
.then((r) => r.json())
.then(processData)
.catch((e) => log.error('foo fetch failed', e));
Or:
try {
const data = await (await fetch('/api/foo')).json();
processData(data);
} catch (e) {
log.error('foo fetch failed', e);
}
Async without try: decide what should happen on throw and write that.
async function handle(req) {
try {
const data = await (await fetch('/api/foo')).json();
return data.value;
} catch (e) {
log.error('handle failed', { req, error: e });
throw new HandlerError({ cause: e });
}
}
Related
- AI code smells covers empty catch blocks and
anytypes, which co-locate with these signals. - Hardcoded secrets in source covers the case where
console.logaccidentally prints a credential.
Sources
CWE-489 covers active debug code (console.log being the most common form). MDN's async/await error-handling docs cover the unhandled-promise pattern.
RELATED PROBES
- · Code Quality
- · AI Code Smells