prr-kit 1.1.3 → 1.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +1 -1
- package/README.md +260 -235
- package/docs/assets/banner.svg +30 -248
- package/docs/assets/how-it-works.svg +87 -0
- package/package.json +60 -60
- package/src/core/agents/prr-master.agent.yaml +18 -7
- package/src/core/tasks/clear.md +140 -0
- package/src/core/tasks/help.md +15 -13
- package/src/core/workflows/clear/workflow.md +6 -0
- package/src/core/workflows/help/workflow.md +6 -0
- package/src/core/workflows/party-mode/steps/step-01-load-reviewers.md +35 -24
- package/src/core/workflows/party-mode/steps/step-02-discussion.md +45 -25
- package/src/core/workflows/party-mode/workflow.md +2 -2
- package/src/prr/agents/architecture-reviewer.agent.yaml +65 -45
- package/src/prr/agents/business-reviewer.agent.yaml +66 -0
- package/src/prr/agents/general-reviewer.agent.yaml +64 -48
- package/src/prr/agents/performance-reviewer.agent.yaml +65 -45
- package/src/prr/agents/security-reviewer.agent.yaml +67 -43
- package/src/prr/config-template.yaml +97 -0
- package/src/prr/data/stacks/actix.md +55 -0
- package/src/prr/data/stacks/alpine.md +47 -0
- package/src/prr/data/stacks/android.md +53 -0
- package/src/prr/data/stacks/angular.md +96 -0
- package/src/prr/data/stacks/ansible.md +55 -0
- package/src/prr/data/stacks/apollo.md +54 -0
- package/src/prr/data/stacks/astro.md +48 -0
- package/src/prr/data/stacks/aws-cdk.md +55 -0
- package/src/prr/data/stacks/axum.md +56 -0
- package/src/prr/data/stacks/babylonjs.md +55 -0
- package/src/prr/data/stacks/bash.md +53 -0
- package/src/prr/data/stacks/bevy.md +53 -0
- package/src/prr/data/stacks/bootstrap.md +52 -0
- package/src/prr/data/stacks/bun.md +55 -0
- package/src/prr/data/stacks/cpp.md +57 -0
- package/src/prr/data/stacks/csharp.md +95 -0
- package/src/prr/data/stacks/css.md +55 -0
- package/src/prr/data/stacks/cypress.md +53 -0
- package/src/prr/data/stacks/d3.md +53 -0
- package/src/prr/data/stacks/deno.md +49 -0
- package/src/prr/data/stacks/django.md +92 -0
- package/src/prr/data/stacks/docker.md +79 -0
- package/src/prr/data/stacks/drizzle.md +54 -0
- package/src/prr/data/stacks/dynamodb.md +55 -0
- package/src/prr/data/stacks/electron.md +44 -0
- package/src/prr/data/stacks/elixir.md +53 -0
- package/src/prr/data/stacks/expo.md +53 -0
- package/src/prr/data/stacks/expressjs.md +82 -0
- package/src/prr/data/stacks/fastapi.md +88 -0
- package/src/prr/data/stacks/fastify.md +60 -0
- package/src/prr/data/stacks/fiber.md +55 -0
- package/src/prr/data/stacks/firebase.md +43 -0
- package/src/prr/data/stacks/flask.md +46 -0
- package/src/prr/data/stacks/flutter.md +75 -0
- package/src/prr/data/stacks/gin.md +57 -0
- package/src/prr/data/stacks/github-actions.md +71 -0
- package/src/prr/data/stacks/go.md +88 -0
- package/src/prr/data/stacks/godot.md +56 -0
- package/src/prr/data/stacks/graphql.md +76 -0
- package/src/prr/data/stacks/grpc.md +56 -0
- package/src/prr/data/stacks/haskell.md +48 -0
- package/src/prr/data/stacks/helm.md +54 -0
- package/src/prr/data/stacks/hono.md +54 -0
- package/src/prr/data/stacks/htmx.md +38 -0
- package/src/prr/data/stacks/java.md +87 -0
- package/src/prr/data/stacks/jest-vitest.md +87 -0
- package/src/prr/data/stacks/jquery.md +50 -0
- package/src/prr/data/stacks/junit.md +53 -0
- package/src/prr/data/stacks/kotlin.md +89 -0
- package/src/prr/data/stacks/kubernetes.md +148 -0
- package/src/prr/data/stacks/langchain.md +56 -0
- package/src/prr/data/stacks/laravel.md +56 -0
- package/src/prr/data/stacks/libgdx.md +46 -0
- package/src/prr/data/stacks/lit.md +49 -0
- package/src/prr/data/stacks/love2d.md +51 -0
- package/src/prr/data/stacks/lua.md +51 -0
- package/src/prr/data/stacks/mobx.md +54 -0
- package/src/prr/data/stacks/mongodb.md +85 -0
- package/src/prr/data/stacks/monogame.md +51 -0
- package/src/prr/data/stacks/mysql.md +57 -0
- package/src/prr/data/stacks/nestjs.md +95 -0
- package/src/prr/data/stacks/nextjs.md +88 -0
- package/src/prr/data/stacks/nginx.md +55 -0
- package/src/prr/data/stacks/node.md +56 -0
- package/src/prr/data/stacks/nuxtjs.md +91 -0
- package/src/prr/data/stacks/openai-api.md +54 -0
- package/src/prr/data/stacks/opengl.md +54 -0
- package/src/prr/data/stacks/phaser.md +54 -0
- package/src/prr/data/stacks/phoenix.md +55 -0
- package/src/prr/data/stacks/php.md +56 -0
- package/src/prr/data/stacks/playwright.md +86 -0
- package/src/prr/data/stacks/postgresql.md +60 -0
- package/src/prr/data/stacks/prisma.md +81 -0
- package/src/prr/data/stacks/pygame.md +52 -0
- package/src/prr/data/stacks/pytest.md +53 -0
- package/src/prr/data/stacks/python.md +94 -0
- package/src/prr/data/stacks/pytorch.md +54 -0
- package/src/prr/data/stacks/qwik.md +50 -0
- package/src/prr/data/stacks/rails.md +48 -0
- package/src/prr/data/stacks/react-native.md +77 -0
- package/src/prr/data/stacks/react.md +104 -0
- package/src/prr/data/stacks/redis.md +76 -0
- package/src/prr/data/stacks/redux.md +107 -0
- package/src/prr/data/stacks/remix.md +51 -0
- package/src/prr/data/stacks/rust.md +88 -0
- package/src/prr/data/stacks/sass.md +51 -0
- package/src/prr/data/stacks/scala.md +50 -0
- package/src/prr/data/stacks/scikit-learn.md +53 -0
- package/src/prr/data/stacks/sequelize.md +54 -0
- package/src/prr/data/stacks/socket-io.md +54 -0
- package/src/prr/data/stacks/solidity.md +53 -0
- package/src/prr/data/stacks/solidjs.md +45 -0
- package/src/prr/data/stacks/spring-boot.md +92 -0
- package/src/prr/data/stacks/sql.md +85 -0
- package/src/prr/data/stacks/sqlite.md +55 -0
- package/src/prr/data/stacks/styled-components.md +51 -0
- package/src/prr/data/stacks/supabase.md +57 -0
- package/src/prr/data/stacks/svelte.md +77 -0
- package/src/prr/data/stacks/sveltekit.md +54 -0
- package/src/prr/data/stacks/swift.md +61 -0
- package/src/prr/data/stacks/tailwindcss.md +10 -0
- package/src/prr/data/stacks/tanstack-query.md +48 -0
- package/src/prr/data/stacks/tauri.md +52 -0
- package/src/prr/data/stacks/terraform.md +53 -0
- package/src/prr/data/stacks/three.md +53 -0
- package/src/prr/data/stacks/trpc.md +49 -0
- package/src/prr/data/stacks/typeorm.md +40 -0
- package/src/prr/data/stacks/typescript.md +83 -0
- package/src/prr/data/stacks/unity.md +61 -0
- package/src/prr/data/stacks/unreal.md +58 -0
- package/src/prr/data/stacks/vite.md +48 -0
- package/src/prr/data/stacks/vue3.md +95 -0
- package/src/prr/data/stacks/vulkan.md +53 -0
- package/src/prr/data/stacks/wasm.md +49 -0
- package/src/prr/data/stacks/webpack.md +48 -0
- package/src/prr/data/stacks/zig.md +51 -0
- package/src/prr/data/stacks/zustand.md +56 -0
- package/src/prr/workflows/1-discover/select-pr/steps/step-05-confirm.md +1 -0
- package/src/prr/workflows/1-discover/select-pr/workflow.md +1 -1
- package/src/prr/workflows/2-analyze/collect-pr-context/steps/step-01-analyze-files.md +334 -0
- package/src/prr/workflows/2-analyze/collect-pr-context/steps/step-02-collect-sources.md +451 -0
- package/src/prr/workflows/2-analyze/collect-pr-context/steps/step-03-build-knowledge-base.md +337 -0
- package/src/prr/workflows/2-analyze/collect-pr-context/workflow.md +123 -0
- package/src/prr/workflows/2-analyze/describe-pr/steps/step-02-classify.md +12 -6
- package/src/prr/workflows/2-analyze/describe-pr/steps/step-03-walkthrough.md +59 -1
- package/src/prr/workflows/3-review/architecture-review/checklist.md +4 -0
- package/src/prr/workflows/3-review/architecture-review/instructions.xml +32 -4
- package/src/prr/workflows/3-review/architecture-review/workflow.yaml +17 -18
- package/src/prr/workflows/3-review/business-review/checklist.md +27 -0
- package/src/prr/workflows/3-review/business-review/instructions.xml +153 -0
- package/src/prr/workflows/3-review/business-review/workflow.yaml +17 -0
- package/src/prr/workflows/3-review/general-review/checklist.md +5 -1
- package/src/prr/workflows/3-review/general-review/instructions.xml +39 -8
- package/src/prr/workflows/3-review/general-review/workflow.yaml +17 -18
- package/src/prr/workflows/3-review/performance-review/checklist.md +3 -1
- package/src/prr/workflows/3-review/performance-review/instructions.xml +10 -3
- package/src/prr/workflows/3-review/performance-review/workflow.yaml +17 -18
- package/src/prr/workflows/3-review/security-review/checklist.md +2 -1
- package/src/prr/workflows/3-review/security-review/instructions.xml +8 -3
- package/src/prr/workflows/3-review/security-review/workflow.yaml +18 -19
- package/src/prr/workflows/4-improve/improve-code/workflow.yaml +17 -18
- package/src/prr/workflows/6-report/generate-report/steps/step-01-collect.md +9 -2
- package/src/prr/workflows/6-report/generate-report/steps/step-02-organize.md +28 -7
- package/src/prr/workflows/6-report/generate-report/steps/step-03-write.md +6 -4
- package/src/prr/workflows/6-report/generate-report/templates/review-report.template.md +124 -78
- package/src/prr/workflows/6-report/post-comments/steps/step-01-format.md +104 -13
- package/src/prr/workflows/6-report/post-comments/steps/step-02-post.md +92 -21
- package/src/prr/workflows/6-report/post-comments/workflow.md +6 -0
- package/src/prr/workflows/quick/workflow.md +138 -32
- package/src/prr/workflows/0-setup/collect-project-context/steps/step-01-scan-configs.md +0 -106
- package/src/prr/workflows/0-setup/collect-project-context/steps/step-02-extract-rules.md +0 -131
- package/src/prr/workflows/0-setup/collect-project-context/steps/step-03-ask-context.md +0 -194
- package/src/prr/workflows/0-setup/collect-project-context/steps/step-04-save-context.md +0 -161
- package/src/prr/workflows/0-setup/collect-project-context/workflow.md +0 -58
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# Prisma — Stack-Specific Review Rules
|
|
2
|
+
|
|
3
|
+
> Applies to: GR · SR · PR · AR · BR
|
|
4
|
+
> Detection signals: @prisma/client, prisma.schema, schema.prisma, PrismaClient, prisma., prisma., prisma., Prisma.sql, npx prisma migrate, DATABASE_URL
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Security
|
|
9
|
+
|
|
10
|
+
- **[CRITICAL]** `` used with user-supplied input → direct SQL injection; unsanitised strings are interpolated verbatim into the query. Replace with `` and Prisma's tagged template literal `Prisma.sql`, which parameterises all values automatically.
|
|
11
|
+
- **[CRITICAL]** `` used with dynamic SQL built from user input → SQL injection allowing arbitrary data modification or deletion. Replace with `` and `Prisma.sql` tagged template for full parameterisation.
|
|
12
|
+
- **[HIGH]** Full Prisma model objects returned from API route handlers without field selection → password hashes, internal tokens, and sensitive metadata are exposed to the client. Use `select` or `omit` in every query that feeds an API response.
|
|
13
|
+
- **[HIGH]** Prisma schema using `@default(uuid())` on IDs exposed in public URLs where sequential or enumerable IDs are not desired → UUIDs are fine but confirm intent; for human-readable IDs consider `cuid2` or `ulid` which are also URL-safe and sortable.
|
|
14
|
+
- **[HIGH]** Multi-tenant or multi-user queries missing a `userId` (or tenant) filter → horizontal privilege escalation (IDOR): any authenticated user can read or modify any other user's data. Every query on user-owned resources must include a `where: { userId: session.user.id }` clause.
|
|
15
|
+
- **[MEDIUM]** Prisma error objects (`PrismaClientKnownRequestError`) forwarded directly to API responses → leaks table names, column names, and constraint names from the error message. Catch Prisma errors and rethrow sanitised, generic messages; log full errors server-side only.
|
|
16
|
+
- **[MEDIUM]** Migration files not reviewed before running in production → data-destructive operations (column drops, type changes) execute without a safety net. Review every generated migration file before applying; use a staging database as a dry-run environment.
|
|
17
|
+
- **[LOW]** `DATABASE_URL` containing credentials printed in server debug logs → credentials appear in log aggregators accessible to developers without DB access. Redact the connection string in logging middleware and avoid logging environment variables.
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## Performance
|
|
22
|
+
|
|
23
|
+
- **[CRITICAL]** N+1 query pattern — accessing a relation field inside a loop without `include` on the parent query → Prisma fires a separate SELECT for every iteration. Use `include: { relation: true }` on the outer query, or switch to a raw aggregation.
|
|
24
|
+
- **[HIGH]** `findMany` without `take` / `skip` or cursor pagination → unbounded result set; the entire table is loaded into application memory on large datasets, causing OOM. Always add `take: N` and implement pagination.
|
|
25
|
+
- **[HIGH]** Missing `@@index` in `schema.prisma` for fields used in `where`, `orderBy`, or relation lookups → full table scan on every query. Add compound or single-column indexes and run `EXPLAIN` to verify they are used.
|
|
26
|
+
- **[HIGH]** `include: { relation: true }` on a relation that can have thousands of child records → all children are loaded into memory even when only a count or summary is needed. Use `_count` for counts or paginate the relation separately.
|
|
27
|
+
- **[HIGH]** Multiple sequential Prisma calls in a single request that could be batched → each call is a separate database round-trip with its own latency. Batch with `prisma.([call1, call2])` for atomicity and fewer round-trips.
|
|
28
|
+
- **[HIGH]** `` used without a `timeout` option → long-running transactions hold row-level locks indefinitely, causing deadlocks under concurrent load. Set `{ timeout: 5000 }` (or an appropriate maximum) on every transaction.
|
|
29
|
+
- **[MEDIUM]** `findMany` with deeply nested `include` → Prisma may generate N+1 queries for nested relations even with `include`. Use `select` with only the specific fields needed to limit the query tree depth.
|
|
30
|
+
- **[MEDIUM]** Not using `select` to limit returned columns → all columns, including large text or binary fields, are fetched and serialised even when only two fields are consumed. Add a `select` clause to every query in a hot path.
|
|
31
|
+
- **[MEDIUM]** Offset-based pagination (`skip / take`) used on large tables → the database must scan and discard all skipped rows, getting slower as the offset grows. Switch to cursor-based pagination using `cursor: { id: lastId }` and `take`.
|
|
32
|
+
- **[MEDIUM]** `new PrismaClient()` created per request in a serverless function → each invocation opens a new connection pool, exhausting database connections under load. Use a module-level singleton with `globalThis` caching, and add a connection pooler (PgBouncer or Prisma Accelerate).
|
|
33
|
+
- **[MEDIUM]** No connection pooler (PgBouncer or Prisma Accelerate) used in a serverless deployment → each function invocation creates a fresh TCP connection to the database, which is expensive and limited by the database's max connection count. Add Prisma Accelerate or a sidecar pooler.
|
|
34
|
+
- **[LOW]** `prisma generate` not run in CI after `schema.prisma` changes → the committed generated client is out of sync, causing type errors or runtime mismatches that only surface in production. Run `prisma generate` as a CI step after any schema change.
|
|
35
|
+
- **[LOW]** `prisma db pull` not used when the database is the source of truth → `schema.prisma` drifts from the actual database schema, leading to migration conflicts. Run `db pull` regularly when working with an existing database.
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
## Architecture
|
|
40
|
+
|
|
41
|
+
- **[CRITICAL]** `new PrismaClient()` instantiated inside a route handler or on every request → each call opens a new connection pool; the database max-connection limit is hit quickly under any real load. Instantiate once at module level and export the singleton; use `globalThis` caching in serverless.
|
|
42
|
+
- **[HIGH]** `PrismaClient` used directly inside route handlers with no abstraction layer → business logic is tightly coupled to the ORM, making unit testing require a real database. Extract all DB calls into repository or service modules that can be mocked independently.
|
|
43
|
+
- **[HIGH]** `schema.prisma` without explicit field ordering or using `@map` to control column names → in some migration scenarios columns are reordered or renamed unexpectedly, causing migration drift. Use `@map` and `@@map` consistently and review generated SQL before applying.
|
|
44
|
+
- **[HIGH]** Prisma client used in an Edge Runtime (Vercel Edge, Cloudflare Workers) without Prisma Accelerate → the standard Prisma client uses Node.js APIs unavailable in the Edge runtime, causing deployment failures. Use Prisma Accelerate or Prisma's Driver Adapters for Edge deployments.
|
|
45
|
+
- **[MEDIUM]** Update timestamp fields defined with `@default(now())` instead of `@updatedAt` → the field is only set on creation, never on subsequent updates. Replace with `@updatedAt` so Prisma automatically sets the field on every update operation.
|
|
46
|
+
- **[MEDIUM]** Adding a NOT NULL column to a live table without a default value in the migration → the migration fails on a non-empty production table. Always provide a default (`@default`) or make the column optional (`?`) when adding columns to tables with existing data.
|
|
47
|
+
- **[MEDIUM]** Soft-delete implemented via a custom middleware that only intercepts `findMany` / `findFirst` → direct `delete` / `deleteMany` calls bypass the filter and hard-delete records. The middleware must also intercept `delete` and `deleteMany` to redirect them to an update of the `deletedAt` field.
|
|
48
|
+
- **[MEDIUM]** Prisma used for complex analytics or reporting queries → Prisma's query builder cannot express window functions, CTEs, or advanced aggregations efficiently. Use `` with `Prisma.sql` or a dedicated analytics connection for reporting workloads.
|
|
49
|
+
- **[MEDIUM]** Cross-cutting concerns (audit logging, soft-delete, multi-tenancy row filters) implemented ad hoc in each service rather than via Prisma middleware → inconsistent application and easy to miss in new code. Centralise these concerns in `prisma.()` middleware or Prisma Client extensions.
|
|
50
|
+
- **[LOW]** Prisma model names not matching SQL table naming convention and `@@map` not used → Prisma generates PascalCase table names that differ from the snake_case convention of the existing database, causing migration confusion. Use `@@map("table_name")` to align model names with SQL conventions.
|
|
51
|
+
- **[LOW]** Enums defined in `schema.prisma` but not yet reflected in the database via a migration → Prisma generates the enum type but queries fail until the migration is applied. Always run `prisma migrate dev` after adding or modifying enums.
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
## Code Quality
|
|
56
|
+
|
|
57
|
+
- **[HIGH]** `prisma.model.update()` called on a record that does not exist → Prisma throws `PrismaClientKnownRequestError` with code `P2025` (Record not found), not `null`. Handle the P2025 error explicitly and return a 404; do not assume the result can be `null`.
|
|
58
|
+
- **[HIGH]** `upsert` used without a guaranteed unique constraint on the `where` field → under concurrent requests two `create` branches may race and produce duplicate rows. Ensure the `where` field has a `@unique` or `@@unique` constraint in the schema.
|
|
59
|
+
- **[HIGH]** Multi-step database operations not wrapped in `` → a failure in any step leaves the database in a partially updated, inconsistent state with no rollback. Wrap all related mutations in a single `prisma.(async (tx) => { ... })` call.
|
|
60
|
+
- **[MEDIUM]** `connect` vs `create` in nested writes confused → `connect` links an existing related record by ID; `create` makes a new one. Using `connect` with a non-existent ID throws P2025; using `create` when the record already exists creates a duplicate. Understand the distinction before writing nested relation operations.
|
|
61
|
+
- **[MEDIUM]** `findUnique` vs `findFirst` confused → `findUnique` strictly requires a field with a `@unique` or `@@unique` constraint and cannot use `orderBy`; `findFirst` can use any field and supports sorting but acquires no uniqueness guarantee. Use the correct variant for the use-case.
|
|
62
|
+
- **[MEDIUM]** Prisma error codes (`P2002` unique constraint, `P2025` record not found) not caught and mapped to HTTP status codes → clients receive a generic 500 for recoverable errors. Catch `PrismaClientKnownRequestError` and map `P2002` to 409 Conflict, `P2025` to 404 Not Found, etc.
|
|
63
|
+
- **[MEDIUM]** `prisma.("query")` logging enabled in production → every SQL query, including those containing sensitive parameter values, is written to logs. Disable query logging in production or filter out sensitive query events.
|
|
64
|
+
- **[MEDIUM]** `prisma.()` not called in scripts, Lambda handlers, or test teardown → the connection pool is not released cleanly, leaving idle connections open on the database server. Always call `()` in a `finally` block for scripts and in `afterAll` for tests.
|
|
65
|
+
- **[LOW]** `@default(now())` used on the `updatedAt` field instead of `@updatedAt` → the field is set only on document creation and never updated by Prisma on subsequent writes. Change to `updatedAt DateTime @updatedAt`.
|
|
66
|
+
- **[LOW]** Missing `relationMode = "prisma"` in the datasource block when using PlanetScale (which does not support foreign key constraints) → Prisma skips relation validation and referential integrity is not enforced at the ORM level. Add `relationMode = "prisma"` and use Prisma's emulated relation checks.
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
## Common Bugs & Pitfalls
|
|
71
|
+
|
|
72
|
+
- **[HIGH]** `findUnique` used with a field that does not have a `@unique` or `@@unique` constraint → Prisma throws a runtime error; it does not silently fall back to `findFirst`. Switch to `findFirst` when querying non-unique fields.
|
|
73
|
+
- **[HIGH]** `prisma.` callback using the outer `prisma` instance instead of the `tx` argument → queries inside the callback execute outside the transaction scope and are not rolled back on error. Always use the `tx` parameter for all queries inside a transaction callback.
|
|
74
|
+
- **[HIGH]** `prisma migrate deploy` run in production without first running `prisma generate` → the deployed code uses an outdated generated client that may be missing new models or fields, causing runtime type errors. Run `prisma generate` as part of the deployment pipeline before starting the server.
|
|
75
|
+
- **[MEDIUM]** `prisma.model.create()` with a nested `connect` to a non-existent related record ID → Prisma throws P2025 with a message referencing the related model, which can be confusing. Validate that related IDs exist before the create, or handle P2025 and surface a clear error.
|
|
76
|
+
- **[MEDIUM]** `count` aggregate result compared with `!= null` → `count` always returns a number (0 when no records match), so the null check is always true and masks the real condition. Compare with `=== 0` or `> 0` explicitly.
|
|
77
|
+
- **[MEDIUM]** Soft-delete Prisma middleware intercepts `findMany` and `findFirst` but not `delete` / `deleteMany` → direct delete calls bypass the soft-delete logic, permanently removing records. Extend the middleware to intercept `delete` and `deleteMany` and convert them to `update({ data: { deletedAt: new Date() } })`.
|
|
78
|
+
- **[MEDIUM]** `` or `` with `COUNT(*)` → PostgreSQL (and some other databases) return `COUNT` as a `BigInt`, which `JSON.stringify` cannot serialise, throwing a runtime error. Convert with `Number()` or `.toString()` before returning the result.
|
|
79
|
+
- **[MEDIUM]** `updateMany` return value used to retrieve the updated records → `updateMany` returns `{ count: N }`, not the updated records. Call a subsequent `findMany` with the same `where` clause if the updated records are needed.
|
|
80
|
+
- **[LOW]** `schema.prisma` not formatted with `prisma format` before committing → unformatted schema files produce noisy, hard-to-read diffs and may trigger unnecessary reformatting by another developer. Add `prisma format` to a pre-commit hook.
|
|
81
|
+
- **[LOW]** Prisma CLI version and `@prisma/client` version out of sync → version mismatches produce deprecation warnings, unexpected query behaviour, and subtle type errors. Pin both `prisma` and `@prisma/client` to the same version in `package.json` and update them together.
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# Pygame — Stack-Specific Review Rules
|
|
2
|
+
|
|
3
|
+
> Applies to: GR · SR · PR · AR · BR
|
|
4
|
+
> Detection signals: `import pygame`, `pygame.init()`, `pygame.display`, `pygame.event`, `pygame.draw`, `QUIT`, `clock.tick`
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Security
|
|
9
|
+
- **[MEDIUM]** Asset paths constructed from user-provided input passed to `pygame.image.load()` or `pygame.mixer.Sound()` → path traversal allowing arbitrary file reads; sanitize and validate all file paths against an allowlist directory.
|
|
10
|
+
- **[MEDIUM]** Multiplayer game accepting player inputs over the network without server-side validation → cheating by sending crafted move packets; validate all game actions server-side and treat client input as untrusted.
|
|
11
|
+
- **[MEDIUM]** Save game files serialized with `pickle` → `pickle.loads()` on a tampered save file executes arbitrary Python code; use `json` with strict schema validation instead of `pickle` for save data.
|
|
12
|
+
- **[LOW]** Hardcoded server IP addresses or API keys in source code → exposed in the distributed package; load configuration from environment variables or an encrypted config file.
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## Performance
|
|
17
|
+
- **[HIGH]** `pygame.display.flip()` or `pygame.display.update()` called without frame rate limiting → game loop runs at thousands of FPS consuming 100% CPU/GPU; add `clock.tick(target_fps)` every frame.
|
|
18
|
+
- **[HIGH]** `pygame.Surface` objects created inside the game loop (new images, text renders, drawn shapes) → heap allocations every frame causing GC pressure and hitches; create surfaces once at startup or load time and reuse.
|
|
19
|
+
- **[HIGH]** `pygame.image.load()` called inside the game loop or `update()` function → blocking disk I/O on every frame; load all assets before the game loop starts and cache the returned surfaces.
|
|
20
|
+
- **[MEDIUM]** Loaded surfaces not converted with `.convert()` or `.convert_alpha()` → slow blitting due to pixel-format conversion at draw time; call `surface.convert()` (opaque) or `surface.convert_alpha()` (transparent) immediately after loading.
|
|
21
|
+
- **[MEDIUM]** Full-screen `display.fill()` + full redraw every frame instead of dirty rectangle updates → unnecessary pixel writes; use `pygame.display.update(dirty_rects)` with a list of changed regions for pixel-art or static-background games.
|
|
22
|
+
- **[MEDIUM]** Per-sprite draw calls not using `pygame.sprite.Group.draw()` batching → manual loops with more Python overhead; use `sprite.Group` for batch rendering.
|
|
23
|
+
- **[LOW]** `pygame.font.SysFont()` called every frame for text rendering → expensive font lookup and surface creation; pre-render text to a surface and only re-render when the text value changes.
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## Architecture
|
|
28
|
+
- **[HIGH]** Entire game (event handling, update logic, rendering, asset loading) written in one function or module → monolithic and untestable; separate into distinct classes or modules: Game, Scene/State machine, Renderer, InputHandler, AssetManager.
|
|
29
|
+
- **[HIGH]** Game loop lacking separation between fixed-timestep `update(dt)` and `draw()` → movement speed is frame-rate dependent; use a fixed-timestep accumulator pattern to decouple physics/logic updates from rendering.
|
|
30
|
+
- **[MEDIUM]** No state machine for game screens (menu, game, pause, game-over) → ad-hoc `if` flags spread across the loop; implement a `Scene` base class with `handle_events`, `update`, and `draw` methods and a scene stack/manager.
|
|
31
|
+
- **[MEDIUM]** Event handling code mixed directly with game logic in the same block → hard to test input in isolation; route events through a dedicated input handler that sets state flags consumed by the update step.
|
|
32
|
+
- **[LOW]** No separation between game objects and their rendering → game objects directly call `screen.blit()`; decouple by having a renderer that takes game state and produces draw calls.
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## Code Quality
|
|
37
|
+
- **[HIGH]** `pygame.quit()` not called before `sys.exit()` or on application close → SDL resources not released cleanly, potential hang on some platforms; always call `pygame.quit()` before exiting.
|
|
38
|
+
- **[MEDIUM]** Screen dimensions, target FPS, colors, and tile sizes hardcoded as numeric literals throughout the codebase → magic numbers cause inconsistency when changed; define all constants in a dedicated `constants.py` or `config.py` module.
|
|
39
|
+
- **[MEDIUM]** Not all relevant event types handled in the event loop (e.g., `VIDEORESIZE`, `ACTIVEEVENT`, window focus) → game behaves incorrectly on window resize or focus loss; handle all events the game needs to respond to.
|
|
40
|
+
- **[MEDIUM]** `pygame.key.get_pressed()` used for single-press actions (menu selection, firing) → returns held state, fires every frame; use `KEYDOWN` events for single-press actions and `get_pressed()` only for held-key movement.
|
|
41
|
+
- **[LOW]** `pygame.Vector2` not used for 2D position and velocity math → manual tuple arithmetic is error-prone and verbose; use `pygame.Vector2` for all 2D math to benefit from built-in methods.
|
|
42
|
+
|
|
43
|
+
---
|
|
44
|
+
|
|
45
|
+
## Common Bugs & Pitfalls
|
|
46
|
+
- **[HIGH]** Forgetting to call `pygame.display.flip()` or `pygame.display.update()` at the end of the draw step → screen buffer never presented, game shows a black or frozen window.
|
|
47
|
+
- **[HIGH]** Event loop (`for event in pygame.event.get()`) not called every frame → SDL event queue fills up, OS marks the window as "not responding" and may terminate it; always drain the event queue every frame.
|
|
48
|
+
- **[MEDIUM]** `pygame.time.Clock()` instantiated inside the game loop instead of before it → each iteration creates a new Clock with no frame history, making `clock.tick()` ineffective for rate limiting; create the Clock once before the loop.
|
|
49
|
+
- **[MEDIUM]** Blit coordinates passed as floats (e.g., from `Vector2` position) → `pygame.Surface.blit()` truncates silently, but accumulated float positions cause drift; convert to `int` explicitly: `screen.blit(img, (int(pos.x), int(pos.y)))`.
|
|
50
|
+
- **[MEDIUM]** `pygame.mixer.init()` not called before playing sounds, or audio not initialized with correct frequency/buffer settings → `pygame.error: mixer not initialized`; initialize the mixer with explicit parameters before loading any audio.
|
|
51
|
+
- **[LOW]** RGB color tuples with component values outside the 0–255 range → pygame wraps or raises depending on the surface format; always clamp color values with `max(0, min(255, value))`.
|
|
52
|
+
- **[LOW]** `pygame.sprite.Sprite.kill()` not called when removing sprites from groups → sprite remains in the group and continues to receive `update()` calls; always call `sprite.kill()` to remove from all groups.
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# pytest — Stack-Specific Review Rules
|
|
2
|
+
|
|
3
|
+
> Applies to: GR · SR · PR · AR · BR
|
|
4
|
+
> Detection signals: `pytest`, `import pytest`, `def test_`, `conftest.py`, `@pytest.fixture`, `@pytest.mark`, `pytest.ini`, `pyproject.toml` with `[tool.pytest`
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Security
|
|
9
|
+
- **[HIGH]** Test fixtures creating real external API calls → API keys used in tests may be logged in CI output; rate limits and billing charges incurred. Mock all external HTTP calls with `pytest-httpx`, `responses`, or `unittest.mock.patch`.
|
|
10
|
+
- **[HIGH]** Hardcoded secrets (API keys, passwords, tokens) in `conftest.py` or test files → secrets committed to version control. Load test secrets from environment variables using `os.environ` or `pytest-dotenv`.
|
|
11
|
+
- **[MEDIUM]** Temporary files created in tests not cleaned up via fixture teardown → sensitive data left on disk after the test suite exits. Use `tmp_path` (built-in pytest fixture) which automatically cleans up after each test.
|
|
12
|
+
- **[MEDIUM]** Test database not isolated from production database → tests modify or read production data, causing data corruption or false test passes. Use a dedicated test database connection string and wipe/seed it before each test session.
|
|
13
|
+
- **[LOW]** Test output not suppressed for passing tests in CI → secrets or PII from fixtures logged to CI output on success. Pass `-q` or `--no-header` flags and avoid printing sensitive values in fixture setup.
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## Performance
|
|
18
|
+
- **[HIGH]** Database not reset between tests allowing state pollution → a passing test leaves data that causes a subsequent test to fail non-deterministically. Use a rollback-based fixture (`db.rollback()` in teardown) or truncate tables in `autouse` fixtures.
|
|
19
|
+
- **[HIGH]** `@pytest.fixture(scope='session')` not used for expensive setup (DB connections, server startup) → expensive setup repeated for every test, multiplying suite runtime. Set `scope='session'` for fixtures that are safe to share across all tests.
|
|
20
|
+
- **[HIGH]** Synchronous test functions used for async code without `pytest-asyncio` and `@pytest.mark.asyncio` → async functions return coroutines that are never awaited; tests pass without actually running. Install `pytest-asyncio` and mark all async test functions with `@pytest.mark.asyncio`.
|
|
21
|
+
- **[MEDIUM]** `pytest-xdist` not used to run tests in parallel → large test suites run sequentially, taking many minutes in CI. Configure `pytest -n auto` with `pytest-xdist` for parallelism, ensuring tests are truly isolated first.
|
|
22
|
+
- **[MEDIUM]** Heavy fixtures (loading large files, starting subprocesses) not cached with appropriate scope → identical setup re-run for each test in the module. Choose the broadest safe scope: `module` or `session` for fixtures with no side effects.
|
|
23
|
+
- **[LOW]** `--tb=short` not configured in CI → verbose traceback output on failure slows CI log parsing. Set `addopts = --tb=short -q` in `pyproject.toml` under `[tool.pytest.ini_options]`.
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## Architecture
|
|
28
|
+
- **[HIGH]** Tests not following Arrange-Act-Assert (AAA) pattern → unclear what is being tested and why; failures are hard to diagnose. Structure every test with a clear setup block, a single action, and an assertion section.
|
|
29
|
+
- **[HIGH]** Test logic duplicated across multiple test files instead of shared fixtures → changing setup requires editing every test file. Extract shared setup into `conftest.py` fixtures and import them implicitly.
|
|
30
|
+
- **[MEDIUM]** Integration tests not separated from unit tests with markers → fast unit tests mixed with slow integration tests; no way to run only unit tests in CI. Use `@pytest.mark.unit` / `@pytest.mark.integration` and configure `pytest -m unit` for fast feedback.
|
|
31
|
+
- **[MEDIUM]** Fixture dependency chains too deep (fixture requiring fixture requiring fixture requiring fixture) → test setup becomes hard to trace and debug. Flatten deep chains by combining related setup into a single higher-level fixture.
|
|
32
|
+
- **[MEDIUM]** No coverage reporting configured with `pytest-cov` → coverage gaps go undetected; dead code and untested branches accumulate. Add `pytest --cov=src --cov-report=xml` to CI and set a minimum coverage threshold.
|
|
33
|
+
- **[LOW]** Test files and functions not following the `test_*.py` and `def test_` naming convention → pytest does not collect them; tests silently never run. Enforce the naming convention in code review and configure `python_files` and `python_functions` in pytest config if custom names are needed.
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## Code Quality
|
|
38
|
+
- **[HIGH]** `assert` statements with no message in failing conditions → failure output shows only "AssertionError" with no context. Write `assert result == expected, f"Expected {expected}, got {result}"` or use pytest's built-in assertion introspection with descriptive variable names.
|
|
39
|
+
- **[HIGH]** Broad `except Exception` in tests masking actual failures → test catches the error it was meant to test plus unexpected errors; always passes. Remove broad exception catches from test bodies; let unexpected exceptions propagate and fail the test.
|
|
40
|
+
- **[MEDIUM]** Not using `pytest.raises()` for expected exceptions → test catches the exception manually with try/except, missing edge cases if the exception is not raised. Use `with pytest.raises(ValueError, match="expected message"):` for cleaner exception assertions.
|
|
41
|
+
- **[MEDIUM]** Test functions named non-descriptively (`test_1`, `test_func`, `test_stuff`) → failing test names give no information about what broke. Name tests to describe the behavior: `test_create_user_returns_400_when_email_is_invalid`.
|
|
42
|
+
- **[MEDIUM]** `@pytest.mark.parametrize` not used for data-driven tests → multiple nearly identical test functions for slight input variations. Consolidate with `@pytest.mark.parametrize("input,expected", [(...),...])`.
|
|
43
|
+
- **[LOW]** Test files importing from other test files directly → import order dependencies cause collection errors. Share code via `conftest.py` fixtures or a `tests/helpers/` module, not direct inter-test imports.
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## Common Bugs & Pitfalls
|
|
48
|
+
- **[HIGH]** Fixture yielding without teardown (no code after `yield`) when cleanup is needed → database connections, temp files, and external resources leak. Always place cleanup code after `yield` in generator fixtures: `yield value; db.close()`.
|
|
49
|
+
- **[HIGH]** `scope='session'` fixture that modifies shared mutable state → test that runs first passes; subsequent tests fail due to accumulated state mutation. Never mutate shared state in session-scoped fixtures; use `scope='function'` for anything that modifies data.
|
|
50
|
+
- **[MEDIUM]** `monkeypatch` used without relying on its automatic restoration → patches applied inside test logic without using the `monkeypatch` fixture are not automatically restored. Always use the `monkeypatch` fixture parameter; do not call `unittest.mock.patch` without a context manager.
|
|
51
|
+
- **[MEDIUM]** `asyncio` event loop not properly managed across tests with `pytest-asyncio` → "Event loop is closed" errors or tests sharing a loop when they shouldn't. Set `asyncio_mode = "auto"` in `pyproject.toml` or use `@pytest.fixture(loop_scope="session")` consistently.
|
|
52
|
+
- **[MEDIUM]** Fixtures with `scope='module'` or `scope='session'` depending on `scope='function'` fixtures → pytest raises a ScopeMismatch error or silently uses a broader scope. Ensure fixture scope hierarchy is consistent: session > module > class > function.
|
|
53
|
+
- **[LOW]** Test files missing the `test_` prefix collected by accident or not collected as intended → tests silently never run. Verify collection with `pytest --collect-only` before merging new test files.
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# Python (General) — Stack-Specific Review Rules
|
|
2
|
+
|
|
3
|
+
> Applies to: GR · SR · PR · AR · BR
|
|
4
|
+
> Detection signals: `*.py` files, `requirements.txt`, `pyproject.toml`, `setup.py` — without more specific framework signals
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Security
|
|
9
|
+
|
|
10
|
+
- **[CRITICAL]** `eval()` / `exec()` with user input → arbitrary code execution.
|
|
11
|
+
- **[CRITICAL]** `subprocess.call(shell=True, args=userInput)` → command injection. Use list args without shell=True.
|
|
12
|
+
- **[CRITICAL]** `pickle.loads()` / `pickle.load()` on untrusted data → arbitrary code execution. Use JSON or `json.loads`.
|
|
13
|
+
- **[CRITICAL]** `yaml.load()` without `Loader=yaml.SafeLoader` → arbitrary Python object deserialization.
|
|
14
|
+
- **[HIGH]** `os.path.join` with user path segments not normalized/restricted → path traversal. Use `pathlib.Path.resolve()` + prefix check.
|
|
15
|
+
- **[HIGH]** `xml.etree.ElementTree` / `xml.sax` parsing untrusted XML → XXE / billion-laugh DoS. Use `defusedxml`.
|
|
16
|
+
- **[HIGH]** `tempfile.mktemp()` (deprecated) → TOCTOU race condition. Use `tempfile.NamedTemporaryFile()`.
|
|
17
|
+
- **[HIGH]** Hardcoded credentials in source code → version control exposure.
|
|
18
|
+
- **[HIGH]** `random` module used for security tokens/nonces → not cryptographically secure. Use `secrets` module.
|
|
19
|
+
- **[HIGH]** `hashlib.md5()` / `hashlib.sha1()` for password hashing → insecure. Use `bcrypt` / `argon2-cffi`.
|
|
20
|
+
- **[MEDIUM]** `assert` used for security checks → optimized away with `-O` flag. Use explicit `if` + `raise`.
|
|
21
|
+
- **[MEDIUM]** Regular expressions from user input without timeout → ReDoS catastrophic backtracking.
|
|
22
|
+
- **[MEDIUM]** `os.environ` secrets accessed without validation → app runs with missing/empty secrets.
|
|
23
|
+
- **[LOW]** Sensitive data in logging statements at DEBUG level that reaches production.
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## Performance
|
|
28
|
+
|
|
29
|
+
- **[HIGH]** String concatenation in loop (`result += str`) → O(n²). Use `''.join(list)` or `io.StringIO`.
|
|
30
|
+
- **[HIGH]** Loading entire file into memory with `f.read()` for large files → OOM. Use line iteration or `mmap`.
|
|
31
|
+
- **[HIGH]** GIL-bound CPU work not offloaded to `ProcessPoolExecutor` → single-threaded for CPU tasks.
|
|
32
|
+
- **[HIGH]** `asyncio` event loop blocked by synchronous I/O inside `async def` → use `asyncio.to_thread()` or async libraries.
|
|
33
|
+
- **[HIGH]** N+1 queries in ORM loop not using `select_related`/`prefetch_related` (Django) or `joinedload` (SQLAlchemy).
|
|
34
|
+
- **[MEDIUM]** `list.append()` in loop when list comprehension is available → slower, less readable.
|
|
35
|
+
- **[MEDIUM]** `for i in range(len(lst))` instead of `enumerate()` → unpythonic, slower.
|
|
36
|
+
- **[MEDIUM]** Unnecessary list creation when generator suffices (`[x for x in ...]` vs `(x for x in ...)`).
|
|
37
|
+
- **[MEDIUM]** `deepcopy()` used unnecessarily in hot path → expensive recursive copy.
|
|
38
|
+
- **[MEDIUM]** `re.compile()` not cached at module level → regex recompiled on every call.
|
|
39
|
+
- **[MEDIUM]** Pandas `iterrows()` in hot path → use vectorized operations instead.
|
|
40
|
+
- **[LOW]** `global` variable for mutable shared state → race condition in multithreaded code.
|
|
41
|
+
- **[LOW]** `__slots__` not used for classes with many instances → excess memory per object.
|
|
42
|
+
|
|
43
|
+
---
|
|
44
|
+
|
|
45
|
+
## Architecture
|
|
46
|
+
|
|
47
|
+
- **[HIGH]** Missing type hints on public functions → poor IDE support, runtime type errors hard to catch.
|
|
48
|
+
- **[HIGH]** `except Exception` too broad → catching and suppressing unrelated exceptions.
|
|
49
|
+
- **[HIGH]** `except:` bare clause catches `SystemExit` / `KeyboardInterrupt` → prevents graceful shutdown.
|
|
50
|
+
- **[HIGH]** Mutable default argument: `def fn(items=[])` → shared state across all calls. Use `None` + `items = items or []`.
|
|
51
|
+
- **[HIGH]** Not using context managers (`with`) for resources → file handles, DB connections not closed on exception.
|
|
52
|
+
- **[HIGH]** Circular imports between modules → `ImportError` or partially-initialized module access.
|
|
53
|
+
- **[HIGH]** Thread-unsafe global state modification without `threading.Lock`.
|
|
54
|
+
- **[MEDIUM]** Long function >50 lines → decompose.
|
|
55
|
+
- **[MEDIUM]** Missing `__all__` in module → unclear public API, `import *` imports everything.
|
|
56
|
+
- **[MEDIUM]** Not using `dataclasses` or `pydantic` for data containers → manual `__init__` with no validation.
|
|
57
|
+
- **[MEDIUM]** Mixing sync and async code without proper bridge → `asyncio.run()` called from within event loop.
|
|
58
|
+
- **[MEDIUM]** Not using `pathlib.Path` instead of `os.path` string manipulation → error-prone.
|
|
59
|
+
- **[LOW]** Large monolithic module instead of package structure.
|
|
60
|
+
- **[LOW]** Not using `__repr__` and `__str__` on domain classes → unhelpful debugging.
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
## Code Quality
|
|
65
|
+
|
|
66
|
+
- **[HIGH]** Catching exception and doing nothing: `except: pass` → silent failure.
|
|
67
|
+
- **[HIGH]** `raise Exception(str(e))` from except → loses original traceback; use `raise NewException() from e`.
|
|
68
|
+
- **[MEDIUM]** `print()` used for logging → use `logging` module with levels and handlers.
|
|
69
|
+
- **[MEDIUM]** `import *` from module → pollutes namespace, unclear dependencies.
|
|
70
|
+
- **[MEDIUM]** Not using `f-strings` for formatting in Python 3.6+ → older `.format()` less readable.
|
|
71
|
+
- **[MEDIUM]** Comparing to `True`/`False` with `== True` → use `if condition:` directly.
|
|
72
|
+
- **[MEDIUM]** `type()` used for type checking instead of `isinstance()` → misses subclasses.
|
|
73
|
+
- **[MEDIUM]** Not using `abc.ABC` / `@abstractmethod` for abstract base classes → subclass can forget to implement.
|
|
74
|
+
- **[LOW]** Non-PEP8 naming (camelCase for functions/variables).
|
|
75
|
+
- **[LOW]** Missing docstring on public functions/classes.
|
|
76
|
+
- **[LOW]** Not using `black` / `ruff` for consistent formatting.
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
## Common Bugs & Pitfalls
|
|
81
|
+
|
|
82
|
+
- **[HIGH]** `is` used for equality check (`x is "hello"`) → checks identity not value. Use `==`.
|
|
83
|
+
- **[HIGH]** Mutable default argument modified inside function → state persists across calls.
|
|
84
|
+
- **[HIGH]** `datetime.datetime.now()` without timezone → naive datetime causes comparison bugs. Use `.now(tz=timezone.utc)`.
|
|
85
|
+
- **[HIGH]** Dictionary modified during iteration → `RuntimeError`. Iterate `list(dict.items())`.
|
|
86
|
+
- **[HIGH]** `asyncio.create_task()` without storing reference → task garbage collected mid-execution.
|
|
87
|
+
- **[HIGH]** Catching `Exception` in `async` code swallowing `asyncio.CancelledError` (Python <3.8) → use `BaseException` check.
|
|
88
|
+
- **[MEDIUM]** Late binding closures in loops with lambda → all lambdas reference final loop variable.
|
|
89
|
+
- **[MEDIUM]** Integer division `//` vs float division `/` confusion.
|
|
90
|
+
- **[MEDIUM]** `list(d.keys())` in Python 3 unnecessary → dict views are already iterable.
|
|
91
|
+
- **[MEDIUM]** `open()` without explicit `encoding='utf-8'` → platform-dependent default encoding.
|
|
92
|
+
- **[MEDIUM]** `os.path.exists()` TOCTOU race: check then act → file deleted between check and use.
|
|
93
|
+
- **[LOW]** `bool` subclasses `int` in Python → `True + True == 2` causing subtle bugs.
|
|
94
|
+
- **[LOW]** `None` comparison with `==` instead of `is` → custom `__eq__` can return unexpected result.
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# PyTorch — Stack-Specific Review Rules
|
|
2
|
+
|
|
3
|
+
> Applies to: GR · SR · PR · AR · BR
|
|
4
|
+
> Detection signals: `import torch`, `from torch`, `torch.nn`, `torch.Tensor`, `DataLoader`, `nn.Module`, `optimizer.step()`, `loss.backward()`
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Security
|
|
9
|
+
- **[HIGH]** `torch.load()` called on a file from an untrusted or user-supplied source → arbitrary Python code executed via pickle deserialization. Only load weights from trusted sources; use `weights_only=True` (PyTorch 2.0+).
|
|
10
|
+
- **[HIGH]** `torch.load()` called without `weights_only=True` even for internal files → any future exposure of model files creates a deserialization attack vector. Always pass `weights_only=True` when loading state dicts.
|
|
11
|
+
- **[MEDIUM]** Training dataset not validated for adversarial or poisoned examples → model learns malicious behaviour embedded in data. Apply data validation, deduplication, and provenance tracking before training.
|
|
12
|
+
- **[MEDIUM]** Model serving endpoint returns raw embedding vectors in API response → model inversion attacks can reconstruct training data. Return only final predictions; never expose intermediate embeddings.
|
|
13
|
+
- **[LOW]** Jupyter notebooks used for data loading contain hardcoded API keys or credentials → keys committed to version control. Use environment variables or a secrets manager; add notebooks to `.gitignore` or strip outputs with `nbstripout`.
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## Performance
|
|
18
|
+
- **[CRITICAL]** Training running on CPU when CUDA is available → 10-100x slower than GPU training. Check `torch.cuda.is_available()` and move model and tensors to the appropriate device.
|
|
19
|
+
- **[HIGH]** `DataLoader` created with `num_workers=0` → data loading is single-threaded and becomes the training bottleneck. Set `num_workers` to the number of available CPU cores (typically 4–8).
|
|
20
|
+
- **[HIGH]** `pin_memory=False` on DataLoader for GPU training → host-to-device memory transfers are slower. Set `pin_memory=True` when training on GPU for faster async data transfer.
|
|
21
|
+
- **[HIGH]** Large effective batch size not achievable due to VRAM limits and gradient accumulation not used → training diverges or uses a suboptimal batch size. Accumulate gradients over N steps and call `optimizer.step()` every N iterations.
|
|
22
|
+
- **[HIGH]** `torch.compile()` not used for production inference (PyTorch 2.0+) → model runs in eager mode, missing graph-level optimisations. Wrap the model with `torch.compile(model)` for deployment.
|
|
23
|
+
- **[MEDIUM]** Mixed-precision training not enabled → full FP32 used for all operations, doubling VRAM usage and halving throughput on modern GPUs. Use `torch.cuda.amp.autocast()` and `GradScaler` for FP16 training.
|
|
24
|
+
- **[MEDIUM]** Model not switched to `eval()` mode during inference → `BatchNorm` uses batch statistics instead of running statistics, and `Dropout` randomly zeros activations. Always call `model.eval()` before inference and `model.train()` before training.
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## Architecture
|
|
29
|
+
- **[HIGH]** Model not implemented as an `nn.Module` subclass → `parameters()`, `state_dict()`, `to(device)`, and `train()`/`eval()` are unavailable. Subclass `nn.Module` and define all layers in `__init__` and forward pass in `forward()`.
|
|
30
|
+
- **[HIGH]** Training loop, model definition, and data loading all in a single script → not reusable or testable. Separate into `model.py`, `dataset.py`, `train.py`, and `evaluate.py` with clear interfaces.
|
|
31
|
+
- **[MEDIUM]** Custom data loading not using `torch.utils.data.Dataset` and `DataLoader` → non-reproducible shuffling, no multi-worker support, and no collation. Implement `__len__` and `__getitem__` in a `Dataset` subclass and wrap with `DataLoader`.
|
|
32
|
+
- **[MEDIUM]** Hyperparameters hardcoded as module-level constants → cannot run experiments without editing source. Use `argparse`, `hydra`, or a config YAML file for all hyperparameters.
|
|
33
|
+
- **[MEDIUM]** No checkpointing implemented → training not resumable after interruption. Save `model.state_dict()` and `optimizer.state_dict()` at regular intervals with `torch.save`.
|
|
34
|
+
- **[LOW]** Experiment not logged with TensorBoard or Weights & Biases → training dynamics invisible, hard to compare runs. Add metric logging with `torch.utils.tensorboard.SummaryWriter` or `wandb.log`.
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
## Code Quality
|
|
39
|
+
- **[HIGH]** `optimizer.zero_grad()` not called before `loss.backward()` → gradients accumulate across batches, producing incorrect parameter updates. Call `optimizer.zero_grad()` at the start of each training step (or use `set_to_none=True` for efficiency).
|
|
40
|
+
- **[HIGH]** Tensors on different devices (CPU vs CUDA) passed to the same operation → `RuntimeError: Expected all tensors to be on the same device`. Explicitly move all tensors to the target device before operations.
|
|
41
|
+
- **[MEDIUM]** Inference not wrapped in `torch.no_grad()` → autograd graph is built unnecessarily during inference, wasting memory and time. Wrap inference code in `with torch.no_grad():` or decorate with `@torch.inference_mode()`.
|
|
42
|
+
- **[MEDIUM]** Random seeds not set for reproducibility → experiments not reproducible across runs. Set `torch.manual_seed`, `torch.cuda.manual_seed_all`, `numpy.random.seed`, and `random.seed` at the entry point.
|
|
43
|
+
- **[MEDIUM]** Loss not reduced correctly for variable-length sequences (e.g., padding tokens included in mean) → biased gradient signal. Use `ignore_index` in `CrossEntropyLoss` or mask padding tokens before reducing.
|
|
44
|
+
- **[LOW]** Gradient norms not clipped during training → exploding gradients cause NaN loss. Add `torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0)` before `optimizer.step()`.
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## Common Bugs & Pitfalls
|
|
49
|
+
- **[HIGH]** In-place operation on a leaf tensor that requires gradient (`tensor += 1`, `tensor.fill_()`) → autograd cannot track the in-place modification and raises a `RuntimeError`. Use out-of-place operations (`tensor = tensor + 1`) on tensors in the computation graph.
|
|
50
|
+
- **[HIGH]** `.numpy()` called on a tensor without `.detach()` first → raises `RuntimeError: Can't call numpy() on Tensor that requires grad`. Always call `.detach().cpu().numpy()` when converting to numpy.
|
|
51
|
+
- **[MEDIUM]** DataLoader `drop_last=False` with batch-dependent operations (e.g., `BatchNorm`) → final batch has a different size and causes shape mismatches or unstable normalisation. Set `drop_last=True` when the model requires fixed batch sizes.
|
|
52
|
+
- **[MEDIUM]** `model.train()` not called after evaluation → subsequent training runs use `BatchNorm` and `Dropout` in eval mode. Explicitly call `model.train()` after every validation phase.
|
|
53
|
+
- **[MEDIUM]** Tensor shape assumptions broken after dimension squeeze/unsqueeze → operations silently produce wrong-shaped outputs. Add `.shape` assertions at key points and use named dimensions or `einops` for clarity.
|
|
54
|
+
- **[LOW]** CUDA out-of-memory error not caught and handled gracefully → training script crashes with an unreadable CUDA error. Wrap forward/backward in a try/except for `torch.cuda.OutOfMemoryError` and reduce batch size dynamically.
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# Qwik — Stack-Specific Review Rules
|
|
2
|
+
|
|
3
|
+
> Applies to: GR · SR · PR · AR · BR
|
|
4
|
+
> Detection signals: `*.qwik.tsx`, `from '@builder.io/qwik'`, `component$()`, `useSignal()`, `$()` suffix, `qwik.config.ts`
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Security
|
|
9
|
+
- **[CRITICAL]** `innerHTML` binding with user-controlled content (Qwik's equivalent of `dangerouslySetInnerHTML`) → XSS. Use text interpolation `{userContent}` or sanitize with DOMPurify before binding.
|
|
10
|
+
- **[CRITICAL]** Missing authentication check in `routeLoader$` → unauthenticated access to server data. Validate session at the start of every `routeLoader$` before fetching data.
|
|
11
|
+
- **[HIGH]** `routeAction$` / `formAction$` without input validation → malformed or malicious payloads processed. Validate all action inputs with Zod; return `fail()` on schema errors.
|
|
12
|
+
- **[HIGH]** Sensitive data (tokens, PII) returned from `routeLoader$` that is serialized into the HTML payload → client exposure. Return only the minimal fields needed; strip secrets before returning.
|
|
13
|
+
- **[HIGH]** Missing CSRF protection on `formAction$` → state-changing requests forgeable. Verify `Origin` header or use a CSRF token pattern in all form actions.
|
|
14
|
+
- **[MEDIUM]** Secrets accessed inside client-reachable code paths (not guarded by `server$`) → secret exposure in bundle. Move secret access exclusively into `server$`, `routeLoader$`, or `routeAction$`.
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## Performance
|
|
19
|
+
- **[HIGH]** Eagerly importing heavy modules at the top level instead of lazy `$()` wrapping → defeats Qwik's resumability and ships unnecessary JS upfront. Wrap heavy imports in `$()` or use dynamic `import()` inside event handlers.
|
|
20
|
+
- **[HIGH]** `useStore()` used for large, deeply nested objects → over-serialization into HTML and large resumability payload. Prefer `useSignal()` for primitive values; flatten store shape to minimize serialized state.
|
|
21
|
+
- **[HIGH]** Inline event handlers not extracted to `$()` → Qwik cannot lazy-load the handler, breaking the core resumability model. Extract all event handlers to named `$()` QRL functions.
|
|
22
|
+
- **[MEDIUM]** `useVisibleTask$` used for work that can be done server-side → unnecessary client JS execution. Move server-compatible work to `routeLoader$` and reserve `useVisibleTask$` for browser-only tasks.
|
|
23
|
+
- **[MEDIUM]** `useTask$` running on both server and client without `isServer`/`isBrowser` guard → duplicate side effects. Use `import { isServer } from '@builder.io/qwik/build'` to guard environment-specific logic.
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## Architecture
|
|
28
|
+
- **[HIGH]** Business logic (DB calls, external API) inside `component$` JSX → untestable, mixes rendering with data concerns. Move data fetching to `routeLoader$` and mutations to `routeAction$`.
|
|
29
|
+
- **[HIGH]** Mutable state shared across components via module-level variables → state leaks between SSR requests. Use `useContext` with `createContextId` for scoped shared state.
|
|
30
|
+
- **[MEDIUM]** Overusing `useVisibleTask$` for work that could be handled server-side → increases client-side JS and defeats Qwik's zero-JS goal. Audit each `useVisibleTask$` and migrate server-compatible logic to loaders.
|
|
31
|
+
- **[MEDIUM]** Not following Qwik City file-based routing conventions (`index.tsx`, `layout.tsx`) → routing inconsistencies and missed framework optimizations. Adhere to Qwik City directory/file conventions for all routes and layouts.
|
|
32
|
+
- **[MEDIUM]** Missing `useContext` for cross-component state, using prop drilling instead → verbose and brittle component trees. Define context with `createContextId` and provide/consume via `useProvide`/`useContext`.
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## Code Quality
|
|
37
|
+
- **[HIGH]** `$()` used on closures that capture non-serializable values (class instances, DOM refs, functions) → runtime serialization error on resumption. Ensure all values captured inside `$()` are serializable (plain objects, primitives, signals).
|
|
38
|
+
- **[HIGH]** React-style hooks (no `$` suffix) used expecting equivalent behavior → hooks don't integrate with Qwik's reactivity system. Replace with Qwik equivalents: `useSignal`, `useStore`, `useTask$`, `useVisibleTask$`.
|
|
39
|
+
- **[HIGH]** Components defined as plain functions instead of `component$()` → no lazy loading boundary, component is always eagerly included. Wrap every component definition with `component$()`.
|
|
40
|
+
- **[MEDIUM]** `useSignal()` without TypeScript generic `useSignal<T>()` → signal typed as `Signal<unknown>`, losing type safety. Always provide an explicit generic: `useSignal<string>('')`.
|
|
41
|
+
- **[LOW]** Magic string route paths hardcoded in `routeAction$` redirects → breaks on path refactoring. Use typed route path constants or Qwik City's link helpers.
|
|
42
|
+
|
|
43
|
+
---
|
|
44
|
+
|
|
45
|
+
## Common Bugs & Pitfalls
|
|
46
|
+
- **[CRITICAL]** Closure inside `$()` captures non-serializable value (e.g. a class instance, `Map`, `Set`, DOM element) → runtime error during resumption: "Value cannot be serialized". Only capture signals, stores, and plain-object state inside `$()`.
|
|
47
|
+
- **[HIGH]** `useTask$` with `track()` on a signal that is also mutated inside the task → infinite reactive loop. Ensure the tracked signal is only written from outside the task, or add a value-change guard.
|
|
48
|
+
- **[HIGH]** `useVisibleTask$` with `{ strategy: 'document-ready' }` not providing a server-rendered fallback → blank content before hydration. Always render meaningful server-side HTML as a fallback for visibly-deferred tasks.
|
|
49
|
+
- **[HIGH]** Event handler capturing stale component state due to incorrect `$()` boundary placement → handler operates on outdated values. Capture state via signals/stores rather than closures to get always-current values.
|
|
50
|
+
- **[MEDIUM]** `routeLoader$` return value mutated on the client → breaks resumability assumptions and causes unpredictable UI. Treat loader data as immutable; derive client state into separate signals.
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# Ruby on Rails — Stack-Specific Review Rules
|
|
2
|
+
|
|
3
|
+
> Applies to: GR · SR · PR · AR · BR
|
|
4
|
+
> Detection signals: `*.rb` files, `Gemfile` with rails, `ApplicationController`, `config/routes.rb`, `ActiveRecord`, `db/schema.rb`
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Security
|
|
9
|
+
|
|
10
|
+
- **[CRITICAL]** `raw()` / `html_safe` used on user-controlled content in ERB → XSS. Rails escapes by default; don't bypass.
|
|
11
|
+
- **[CRITICAL]** `ActiveRecord::Base.where("name = '#{params[:name]}'")` → SQL injection. Always use parameterized `where(name: params[:name])`.
|
|
12
|
+
- **[HIGH]** Missing `before_action :authenticate_user!` on protected controllers/actions.
|
|
13
|
+
- **[HIGH]** Mass assignment without `permit()`: `User.new(params[:user])` → attribute injection. Always use `params.require().permit()`.
|
|
14
|
+
- **[HIGH]** `send(params[:method])` — dynamic method dispatch with user input → arbitrary method invocation.
|
|
15
|
+
- **[MEDIUM]** Missing authorization check (Pundit/CanCanCan policy) — authentication ≠ authorization.
|
|
16
|
+
- **[MEDIUM]** `eval()` or `constantize` with user-controlled string → remote code execution.
|
|
17
|
+
- **[MEDIUM]** IDOR — `current_user.orders` not scoped: `Order.find(params[:id])` vs `current_user.orders.find(params[:id])`.
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## Performance
|
|
22
|
+
|
|
23
|
+
- **[HIGH]** N+1 ActiveRecord queries — accessing association in loop without `includes()`, `preload()`, or `eager_load()`.
|
|
24
|
+
- **[HIGH]** `Model.all` without `.find_each` (batch) for large datasets → loads all records into memory.
|
|
25
|
+
- **[MEDIUM]** Missing database index on frequently queried columns.
|
|
26
|
+
- **[MEDIUM]** Synchronous external API calls in controller/model → blocks Puma thread. Use ActiveJob.
|
|
27
|
+
- **[MEDIUM]** Missing `counter_cache` for association counts accessed frequently.
|
|
28
|
+
- **[LOW]** `select('*')` when only specific columns needed → unnecessary data transfer.
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## Architecture
|
|
33
|
+
|
|
34
|
+
- **[HIGH]** Business logic in controller → move to service objects, model methods, or concerns.
|
|
35
|
+
- **[HIGH]** Fat model (>500 lines) → extract concerns, service objects, or value objects.
|
|
36
|
+
- **[MEDIUM]** ActiveRecord callback (`after_create`, `after_save`) for critical business logic → hard to test, unpredictable execution.
|
|
37
|
+
- **[MEDIUM]** Direct model access in view → move to presenter/decorator pattern.
|
|
38
|
+
- **[LOW]** Missing namespaced routes for API versioning.
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
## Common Bugs & Pitfalls
|
|
43
|
+
|
|
44
|
+
- **[HIGH]** `save` vs `save!` — `save` silently returns false on validation failure. Use `save!` or check return value.
|
|
45
|
+
- **[HIGH]** ActiveRecord callback not running because `update_column` bypasses callbacks and validations.
|
|
46
|
+
- **[MEDIUM]** `Time.now` instead of `Time.zone.now` in Rails → timezone bugs.
|
|
47
|
+
- **[MEDIUM]** `destroy_all` in migration without background job for large tables → table lock, downtime.
|
|
48
|
+
- **[LOW]** Missing `dependent: :destroy` on `has_many` → orphaned records accumulate.
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# React Native — Stack-Specific Review Rules
|
|
2
|
+
|
|
3
|
+
> Applies to: GR · SR · PR · AR · BR
|
|
4
|
+
> Detection signals: `react-native` in deps, `*.tsx` with RN imports (`View`, `Text`, `StyleSheet`), `metro.config.*`, `app.json`, `react-navigation`, `@react-native-`, New Architecture
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Security
|
|
9
|
+
|
|
10
|
+
- **[CRITICAL]** Sensitive data (tokens, passwords) in `AsyncStorage` → plaintext on device, accessible on rooted devices. Use `react-native-encrypted-storage` or Keychain/Keystore.
|
|
11
|
+
- **[CRITICAL]** `WebView` with `javaScriptEnabled: true` loading user-controlled URLs → XSS and code injection. Allowlist URLs strictly or disable JS.
|
|
12
|
+
- **[HIGH]** Deep link params not validated → malicious apps open with crafted data. Validate scheme, host, and all params.
|
|
13
|
+
- **[HIGH]** `EXPO_PUBLIC_*` / `REACT_APP_*` env vars containing API secrets → bundled into JS bundle, extractable from APK/IPA.
|
|
14
|
+
- **[HIGH]** Missing certificate pinning for sensitive API calls → MITM on compromised or public networks.
|
|
15
|
+
- **[HIGH]** `WebView` with `onMessage` accepting arbitrary data without validation → message injection from malicious web content.
|
|
16
|
+
- **[HIGH]** Native module exposing filesystem access without path validation → path traversal from JS layer.
|
|
17
|
+
- **[MEDIUM]** Screenshot allowed on sensitive screens (PIN, payment) → use `FLAG_SECURE` (Android) or `allowScreenCapture: false`.
|
|
18
|
+
- **[MEDIUM]** Biometric auth result trusted on JS side → native layer must be source of truth for auth decisions.
|
|
19
|
+
- **[LOW]** Debug menu accessible in production builds → disable `__DEV__` checks properly.
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## Performance
|
|
24
|
+
|
|
25
|
+
- **[HIGH]** `ScrollView` with `map()` for long lists → all items rendered at once. Use `FlatList`/`SectionList` with `keyExtractor`.
|
|
26
|
+
- **[HIGH]** Inline function/object as prop to `React.memo` or `FlatList` `renderItem` → new reference every render, memo ineffective.
|
|
27
|
+
- **[HIGH]** Heavy computation on JS thread (image processing, large transforms) → blocks UI. Use `InteractionManager.runAfterInteractions`, `Reanimated worklets`, or native module.
|
|
28
|
+
- **[HIGH]** `FlatList` missing `getItemLayout` for fixed-height items → scroll-to-index slow, no scroll optimization.
|
|
29
|
+
- **[HIGH]** Images not sized for display — loading 4K for 200px display → excess memory, OOM on low-end devices.
|
|
30
|
+
- **[HIGH]** Unoptimized re-renders causing dropped frames → profile with Flipper/React DevTools Profiler.
|
|
31
|
+
- **[MEDIUM]** Missing `keyExtractor` on `FlatList` → React uses index, broken reconciliation on reorder.
|
|
32
|
+
- **[MEDIUM]** `StyleSheet.create` not used → inline style objects created on every render → GC pressure.
|
|
33
|
+
- **[MEDIUM]** `useEffect` cascade on navigation-heavy screens → multiple re-renders during transition.
|
|
34
|
+
- **[MEDIUM]** Not using `Hermes` engine (default RN 0.70+) → slower startup without bytecode precompilation.
|
|
35
|
+
- **[LOW]** Missing `removeClippedSubviews` on long `FlatList` → off-screen views kept in memory.
|
|
36
|
+
- **[LOW]** Not using `useMemo` for expensive list data transformations.
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
## Architecture
|
|
41
|
+
|
|
42
|
+
- **[HIGH]** Navigation state in Redux/Zustand instead of React Navigation → complex sync, back button issues.
|
|
43
|
+
- **[HIGH]** Async operation in `useEffect` without cleanup → state update on unmounted screen (navigate back).
|
|
44
|
+
- **[HIGH]** Not separating platform logic → `Platform.OS === 'ios'` scattered everywhere. Use `.ios.tsx`/`.android.tsx` file extensions.
|
|
45
|
+
- **[HIGH]** Business logic in screen components → extract to hooks, services, or state management.
|
|
46
|
+
- **[MEDIUM]** Not using React Navigation's `useNavigation` hook → prop drilling `navigation` prop deeply.
|
|
47
|
+
- **[MEDIUM]** Native module called without null check → unlinked module on older RN or missing platform.
|
|
48
|
+
- **[MEDIUM]** Not using `ErrorBoundary` for JS crashes → red screen in production. Use `react-native-error-boundary`.
|
|
49
|
+
- **[MEDIUM]** Deep linking not handling all app states (cold start, background, foreground) differently.
|
|
50
|
+
- **[LOW]** Not using `AppState` API to pause/resume work when app backgrounds.
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## Code Quality
|
|
55
|
+
|
|
56
|
+
- **[HIGH]** `Keyboard.dismiss()` not called before navigation → keyboard visible on next screen.
|
|
57
|
+
- **[HIGH]** `StatusBar` style not set per-screen → wrong appearance on some screens.
|
|
58
|
+
- **[MEDIUM]** `TouchableOpacity` vs `Pressable` not chosen consistently (prefer `Pressable` in RN 0.63+).
|
|
59
|
+
- **[MEDIUM]** Hardcoded pixel values without `PixelRatio` or responsive scaling → poor tablet/accessibility experience.
|
|
60
|
+
- **[MEDIUM]** `useRef` not used for values that should persist without causing re-renders (animation values, timers).
|
|
61
|
+
- **[MEDIUM]** Missing `accessibilityLabel` on interactive elements → screen reader unusable.
|
|
62
|
+
- **[LOW]** Console logs left in production → use `__DEV__` guard or logging service.
|
|
63
|
+
- **[LOW]** Not using TypeScript strict mode → RN component prop types unchecked.
|
|
64
|
+
|
|
65
|
+
---
|
|
66
|
+
|
|
67
|
+
## Common Bugs & Pitfalls
|
|
68
|
+
|
|
69
|
+
- **[HIGH]** `TouchableOpacity` inside `ScrollView` touch conflict on Android → `nestedScrollEnabled` or restructure.
|
|
70
|
+
- **[HIGH]** Metro bundler caching stale files → `--reset-cache` needed after certain changes.
|
|
71
|
+
- **[HIGH]** New Architecture (Fabric/TurboModules) not compatible with unupgraded native modules → runtime crash.
|
|
72
|
+
- **[MEDIUM]** `Animated.Value` directly mutated instead of using `Animated.setValue()` → animation state corrupted.
|
|
73
|
+
- **[MEDIUM]** Android back button not handled → exits app unexpectedly. Use `BackHandler.addEventListener`.
|
|
74
|
+
- **[MEDIUM]** Orientation changes not handled → UI breaks on tablet or landscape mode.
|
|
75
|
+
- **[MEDIUM]** `Linking.openURL()` without `canOpenURL()` check → crash on devices without handler.
|
|
76
|
+
- **[LOW]** `Platform.Version` used for feature detection → use capability check instead.
|
|
77
|
+
- **[LOW]** Not testing on low-end Android device → performance issues invisible on dev machine.
|