Browse 158 rules, 25 knowledge articles, and 25 prompt templates across security, performance, architecture, and quality.
158 rules
Bare console.log statements with no context make production debugging impossible. Use scoped loggers with errorId, userId, and structured metadata.
Same logic duplicated in 3+ places creates consistency bugs and maintenance burden. Extract to the appropriate layer after the third occurrence.
Using useEffect + useState for data fetching creates waterfalls, loading spinners, and unnecessary API routes. Use async Server Components instead.
Pages that block until all data loads show nothing until everything is ready. Wrap slow components in Suspense to stream content progressively.
Serial await statements for independent data fetches create request waterfalls. Use Promise.all to parallelize and cut load times by 2-5x.
Adding 'use client' to large components or pages ships unnecessary JavaScript to the browser. Push interactivity to the smallest leaf components.
Business logic belongs in service classes, not in route handlers, server actions, or components. Use the ServiceResult<T> pattern. [CWE-1086]
Database access belongs in repository classes, not in services or route handlers. Repositories abstract the data source behind a typed interface. [CWE-1057]
Split large repository interfaces into focused, role-specific contracts so consumers only depend on the methods they use
Use factory classes (ServiceFactory, RepositoryFactory) for dependency wiring instead of direct instantiation or imports in consuming code.
Domain entities should be pure TypeScript classes/interfaces with no framework dependencies like Supabase, React, or Next.js
Build in dependency order: Domain, Interface, Repository, Service, Controller, Presentation. Top-down builds couple UI to data.
Sanitize and escape all user-provided input before rendering in HTML, executing in SQL, or passing to system commands. Unsanitized input is the entry point for XSS, SQL injection, and command injection attacks — the three most exploited vulnerability classes in web applications.
Always log errors with structured context: user ID, request ID, input data, stack trace. An error message like "Cannot read property of undefined" with no context is impossible to debug — you don't know which user hit it, what they were doing, or how to reproduce it.
Wrap UI sections in React Error Boundaries to catch rendering errors gracefully. Without error boundaries, a single component crash (a null reference, a failed API parse) takes down the entire page — showing users a white screen with no way to recover or navigate away.
Audit and minimize third-party scripts (analytics, chat widgets, ad trackers). Each third-party script adds DNS lookups, TLS handshakes, and JavaScript execution that blocks the main thread — a single chat widget can add 500ms+ to page load and degrade Core Web Vitals scores.
Serve images in modern formats (WebP/AVIF), at appropriate dimensions, and with width/height attributes. Unoptimized images are typically the largest assets on a page — a single uncompressed hero image can be larger than all your JavaScript combined, destroying load times on mobile.
Lazy-load images, components, and data that are below the initial viewport fold. Loading everything upfront makes the user wait for content they haven't scrolled to yet — increasing Time to Interactive and burning mobile data on content the user may never see.
Never merge user-controlled objects into application objects using Object.assign, spread, or deep-merge without validation. Prototype pollution lets an attacker inject __proto__ properties that modify the behavior of every object in your application — enabling denial of service, authentication bypass, or remote code execution.
Set security headers on all HTTP responses: Content-Security-Policy, X-Content-Type-Options, X-Frame-Options, Strict-Transport-Security. Missing headers leave your app vulnerable to XSS, clickjacking, MIME sniffing, and protocol downgrade attacks.
Never use eval(), new Function(), or vm.runInScript() with user-provided input. These functions execute arbitrary code with the full privileges of your Node.js process — an attacker can read files, access databases, or take over the entire server.
Store all environment-specific configuration (API URLs, database connections, feature flags) in environment variables, never in source code. Hardcoded config means your staging code talks to production databases, your API keys are in git history, and deploying to a new environment requires code changes.
CI must run the full test suite and block deployment on failure. Without gate checks, a broken commit reaches production, users experience bugs, and you spend hours debugging under pressure instead of catching it in CI for free.
Pin exact versions for all dependencies in production (no ^, ~, or * ranges). Unpinned dependencies silently pull in new versions that can introduce breaking changes, security vulnerabilities, or performance regressions — and you won't know until production breaks.
BeforeMerge scans your pull requests against these rules automatically. Get actionable feedback before code ships to production.