Let Kody automatically check quality, security and performance in every pull request—cloud or self-hosted, in under 2 minutes.
Check every pull request with this TypeScript code review checklist to maintain strong typing, solid security and reliable performance before production.
Here’s what this checklist helps you review before approving a pull request.
✅ Code & Design Quality – consistent naming and formatting, small focused functions, and clean modular structure
✅ Type Safety & Contracts – strict typing, precise generics, safe narrowing and runtime validation
✅ Async & Concurrency – explicit Promise types, proper cancellation, bounded retries and controlled parallelism
✅ Error Handling & Observability – typed errors with context, structured logs, metrics and health checks
✅ Security & Performance – validated inputs, safe DOM/secret handling, efficient data flows and scalable execution
✅ Data & Integration – well-typed APIs and schemas, safe migrations and correct handling of dates, numbers and configuration
Use these points as a starting guide to automate code reviews with your agent.
Naming is meaningful and consistent (files kebab-case; types/interfaces PascalCase; functions/variables camelCase; enums PascalCase).
Modules expose a minimal, focused surface (no “barrel dumping” everything from index.ts).
Functions are small and single-purpose; parameters ≤ ~5; avoid boolean flags—prefer options objects.
Comments explain “why,” not “what”; no stale or commented-out code; TODOs link to an issue.
Avoid clever type gymnastics in app code—move complex types to types.ts with docs.
Prettier formatting and ESLint pass; no ts-ignore/eslint-disable unless justified with a comment.
strict mode enabled; no implicit any; noImplicitOverride, noUncheckedIndexedAccess, exactOptionalPropertyTypes turned on when possible.
Prefer unknown over any; model runtime-validated inputs as unknown → refine to safe types.
Discriminated unions used over ad-hoc flags; exhaustive switch guarded by never.
Narrowing uses type predicates or assertion functions (not as casts); avoid non-null ! except at validated boundaries.
Use readonly for immutable structures and tuples where applicable.
Use satisfies to validate value shapes against types without widening.
Generics have clear type params (<TData, TError = unknown> with defaults where useful).
Constraints reflect intent (<T extends Record<string, unknown>>); avoid over-generic <T = any>.
Conditional/mapped/template-literal types are readable and tested; avoid exponential complexity.
Output types are computed via helpers (e.g., Pick, Omit, Exclude, NonNullable, Awaited)—don’t reinvent them.
Variance considered for callback params (don’t over-constrain contravariant positions).
Branded/opaque types used for IDs/unsafe primitives to prevent mix-ups.
All external inputs (env, JSON, network, user input) are validated at runtime before use.
Optional fields respect exactOptionalPropertyTypes; avoid foo?.bar?.() chains that hide errors.
Index access checks use noUncheckedIndexedAccess or guards.
Nullable flows narrowed with explicit guards; no reliance on truthiness for 0/”/false.
Non-null assertions ! appear only after rigorous validation or in performance-critical hot paths with comments.
Public APIs expose minimal, stable contracts; breaking changes are versioned.
Types exported for consumers are intentionally designed (no leaking internal helpers).
DTOs/requests/responses are declared once and reused across layers (derive from schema when possible).
Error contracts are typed (error shapes or Result<E, T> style); no throw string.
Backwards compatibility considered (deprecated fields documented with removal plan).
Interop with JS code uses clear boundary modules; inputs validated and narrowed at the edge.
JSDoc typings in JS files are accurate if mixed repo; no duplication between JSDoc and .d.ts.
Avoid default exports for libraries; named exports ease tree-shaking and refactors.
ESM/CJS interop handled via exports field and correct moduleResolution (e.g., nodenext).
allowJs/checkJs usage is intentional for migration, not permanent drift.
async functions have explicit return types (Promise<...>); no floating promises—guard with void or await.
Cancellation supported via AbortSignal or equivalent; propagated through layers.
Backoff with jitter for retries; max attempts bounded; idempotency keys for mutating calls.
Avoid forEach(async ...); prefer for...of with await, or Promise.allSettled with concurrency limits.
Timers/intervals cleaned up; no orphaned handles in long-lived services.
Shared state is synchronized (atomic updates, message queues, or locks when needed).
Throw Error (or subclasses) with cause/context; never throw plain strings.
Errors are mapped at boundaries to typed domain errors; avoid leaking infra details upward.
Logs are structured (key-value), typed, and redact secrets; correlation/trace IDs propagate.
Metrics (latency, saturation, errors) and health checks exist for critical paths.
try/catch blocks are minimal; don’t swallow errors—surface or log with level and action item.
Inputs sanitized/validated; no direct DOM injection; escape HTML; use textContent over innerHTML.
Avoid eval, Function, dynamic require; no unsafe any in security-sensitive areas.
Secrets not in source; use typed config; rotate and scope credentials; least privilege enforced.
HTTP security headers present on server; CSRF/XSRF, CORS, and content security policies set correctly.
Dependency risk managed (pin versions, review transitive impacts); sandbox untrusted code.
Hot paths avoid excessive allocations; prefer object reuse where safe; avoid megabyte-scale JSON in memory.
Data transforms fused where possible (map+filter combined); avoid O(n²) surprises.
Awareness of V8/JIT deopts (stable object shapes, avoid polymorphic megamorphic call sites).
Use streaming/iterators for large payloads; backpressure respected.
Avoid synchronous blocking in event loops (crypto, zlib) without worker threads.
Unit tests cover logic and edge cases; integration tests for boundaries; E2E for critical flows.
Type-level tests (e.g., expectTypeOf, tsd) validate public types and generics.
Property-based or fuzz tests for parsers/serializers; snapshot tests used sparingly and focused.
Mocks/fakes are typed; avoid over-mocking—prefer real modules in integration tests.
Coverage meaningful (branches/conditions), not just lines; flakiness tracked and fixed.
typescript-eslint rules enforce safety: no-unsafe-*, no-floating-promises, restrict-template-expressions, consistent-type-imports.
Lint and typecheck run in CI with --noEmit; failing PRs block merge.
Dead code detection and dependency checks in CI; bundle size budgets enforced where applicable.
Commit hooks run fast (e.g., lint-staged); no long-running local gates.
tsconfig per package with clear target, module, moduleResolution, lib; composite/project references for large repos.
Declarations (declaration, declarationMap) emitted for libraries; sourceMap on for debuggability.
isolatedModules compatible with the transpiler (swc/esbuild/babel) if used.
Reproducible builds (locked deps, pinned toolchain); environment-specific condition exports used instead of ad-hoc flags.
CI caches TypeScript build info; incremental builds enabled locally.
Proper package.json fields: name, version, type, exports, types, files, sideEffects, engines, peerDependencies.
Dual ESM/CJS support via conditional exports; no deep import leaks.
typesVersions provided if necessary; public API surface minimal and documented.
Monorepo uses workspaces (pnpm/npm/yarn), project refs, and consistent tooling; no cross-package path hacks.
Changesets or equivalent for versioning/release notes; publish pipeline typed and automated.
React: components typed (FC avoided for children), props/interfaces minimal, hooks dependencies correct, useMemo/useCallback purposeful.
Next.js/Nuxt/SvelteKit: server/client boundaries typed; no secret leakage to client; route handlers typed and validated.
Node/Nest/Express/Fastify: schemas (zod/yup/valibot) validate at edges; handlers return typed results/errors.
Angular: strictTemplates enabled; inputs/outputs typed; DI tokens typed and scoped.
State management (Redux/Zustand/MobX/etc.) has typed selectors/actions; side effects isolated and testable.
HTTP clients are typed; request/response models derived from a single schema source (OpenAPI → types or zod → OpenAPI).
Retries use idempotent methods only; timeouts and AbortSignal in place; circuit breakers for flaky deps.
Pagination, rate limits, and partial failures handled; streaming where applicable.
Authentication flows strongly typed; tokens/claims validated; clock skew handled.
WebSocket/Server-Sent Events types for messages and lifecycle events.
DB access layer typed end-to-end (e.g., Prisma/Typed SQL); migrations reviewed; defaults and constraints reflected in types.
bigint/decimal handled safely (no silent number coercion); dates/timezones explicit (Temporal/Date strategy documented).
Serialization formats typed and versioned; backward compatibility planned.
Caching layers typed; invalidation keys modeled; TTLs explicit.
Clear boundaries between domain/app/infra; dependencies flow inward; no circular imports.
Public vs. internal types separated; unstable internals not exported.
Feature modules cohesive; avoid “god” utility modules; avoid overuse of re-exports that obscure ownership.
ADRs or lightweight design notes exist for non-obvious decisions; complexity budget respected.
Prefer composition over inheritance; Strategy/Command/Factory used where they reduce complexity.
Result/Either types over exceptions for expected failures; never for logic holes.
Builders fluent but typed (no unsafe chaining); brand types protect invariants.
Observables/iterators/async iterables typed across producers/consumers.
Config read once, validated (zod/valibot) and exported as typed singleton; no ad-hoc process.env access in app code.
Feature flags typed (enum/union) with explicit rollout strategy; defaults safe; guards centralized.
Client/server flag evaluation boundaries respected; no leaking server-only flags to client bundles.
Twelve-factor: stateless services, config via env (typed), logs to stdout in JSON, graceful shutdown with signal handling.
Health/readiness probes typed and reflect real dependencies.
OpenTelemetry types for traces/metrics/logs; context propagation typed across async boundaries.
Resource limits/timeouts/cancellations enforced in code and infra config.
CLIs typed (e.g., oclif/commander) with clear help, validation, and exit codes.
Shell completions and --json machine output where relevant.
Error messages actionable; verbose/debug modes; telemetry gated and documented.
Fast local dev loop (tsx/esbuild) without compromising type safety.
any usage forbidden except at narrow, documented boundaries; prefer unknown + refinement.
as casts minimized; if required, justified; no double-casting to bypass compiler.
Declaration merging/module augmentation documented and covered by tests.
ts-ignore/expect-error lines include rationale and a ticket to remove; time-boxed.
const assertions and satisfies used to keep literals precise without widening.
Let Kody automatically check quality, security and performance in every pull request—cloud or self-hosted, in under 2 minutes.