Let Kody automatically check quality, security and performance in every pull request—cloud or self-hosted, in under 2 minutes.
A practical checklist to review PHP code for quality, security and speed, helping you keep every pull request production ready.
Here’s what this checklist helps you review before approving a pull request.
✅ Code & Design Quality – correctness, clarity, and maintainable PHP/OOP structure
✅ Security & Performance – protection against vulnerabilities, efficient queries, caching and scalability
✅ Operational Readiness – testing strategy, observability, CI/CD and safe deployments
✅ Integration & Data – APIs and webhooks, database schema & migrations, configuration and multi-tenant data handling
Use these points as a starting guide to automate code reviews with your agent.
Files declare strict typing (declare(strict_types=1);
) and use <?php
only (no short tags).
Names follow conventions: classes PascalCase
, methods/variables camelCase
, constants UPPER_SNAKE_CASE
.
One class per file; single responsibility; functions/methods are short and intention-revealing.
No commented-out/dead code; TODOs tracked in issues (not left in source).
PSR compliance: PSR-1/PSR-12 coding style; PSR-4 autoloading configured.
Consistent formatting enforced via PHP-CS-Fixer or PHP_CodeSniffer (ruleset committed).
Clear namespaces and directory structure; no cyclic dependencies between modules.
Comments explain why, not what; public APIs have PHPDoc for non-obvious behavior/generic types.
No wildcard imports; imports (use
) sorted and minimal; fully qualified names avoided in code body.
Avoid magic numbers/strings; extract constants or enums (PHP 8.1+).
Control flow is straightforward: early returns > deep nesting; match
used where clearer than chained if
s.
Strings use interpolation carefully; prefer sprintf
for complex formatting.
Favor immutability: readonly
properties/readonly
classes where possible (PHP 8.2+).
Visibility is minimal (private
> protected
> public
); no public mutable state.
Use interfaces to program to contracts; prefer composition over inheritance.
Constructors contain no heavy logic or I/O; use factories/builders for complex setup.
Type safety: scalar/union/intersection types present; return/param types and property types declared.
Nullable types used intentionally; avoid returning null
for collections—use []
.
enum
for closed sets; value objects
instead of primitive obsession (e.g., Email
, Money
).
Exceptions are domain-specific; no using return codes for error conditions.
Collections use SPL or dedicated classes; avoid leaking arrays where richer types help.
Avoid static state/singletons; use DI container (Laravel/Symfony container) for wiring.
Magic methods (__get
, __set
, __call
) avoided unless implementing a clear proxy pattern.
Input validation at trust boundaries; centralize validation (Form Requests in Laravel, Validators in Symfony).
SQL Injection: only parameterized queries/QueryBuilder/ORM; never string-concatenate SQL.
XSS: templates auto-escape by default (Blade/Twig); safely escape when outputting raw HTML; sanitize rich text on input.
CSRF: tokens enabled/validated for state-changing requests; excluded routes justified and documented.
AuthN/Z: password hashing via password_hash()
(Argon2id preferred) or framework hashing; role/permission checks on every sensitive action (not just UI).
Session security: secure, HttpOnly, SameSite cookies; session fixation prevention; short TTL for sensitive sessions.
IDOR: server-side ownership checks for resource access; do not trust client-supplied IDs alone.
File upload: strict MIME/extension allow-list; size limits; store outside webroot; randomize filenames; image processing sandboxed.
SSRF: restrict outbound HTTP destinations; disable local/metadata IPs; validate URLs; set allow_url_fopen=0
when possible.
Deserialization: avoid unserialize()
on untrusted data; prefer JSON; if unavoidable, allowed classes list enforced.
Command injection: use Symfony\Process
with argument arrays; never pass unsanitized shell strings.
CORS & Headers: least-permissive CORS; set HSTS, X-Content-Type-Options
, X-Frame-Options
/CSP as appropriate.
Secrets: loaded from env/secret store; never committed; rotate regularly; audit use.
Dependencies: composer.lock
committed; run security audits (e.g., composer audit
, tools like Symfony Security Checker); address CVEs.
Rate limiting & abuse: throttling on auth and expensive endpoints; captcha/challenge where appropriate.
Auditability: sensitive operations logged (non-PII), traceable to user/request.
Composer autoload optimized in prod (composer dump-autoload -o
); dev dependencies excluded in production.
Opcache enabled and tuned; preloading considered for hot classes (PHP-FPM).
Avoid N+1 DB queries; eager load relations; use projections/select specific columns.
Pagination/streaming used for large result sets; never load unbounded collections into memory.
Caching strategy defined (Redis/APCu): keys, TTLs, invalidation rules; avoid unbounded cache growth.
Expensive computations/background tasks offloaded to queues; idempotent workers; concurrency tuned.
HTTP clients have connection pools, timeouts, and retries with backoff (idempotent only).
String concatenation in loops avoided; use buffers/implode
; minimize temporary allocations.
JSON encode/decode hotspots profiled; avoid double encoding; consider Ext-JSON
options judiciously.
Algorithmic complexity reviewed; avoid quadratic behavior on user-supplied list sizes.
PHP-FPM process manager tuned (pm, children, memory); no blocking I/O in request threads when avoidable.
Static asset and HTTP caching (ETag/Last-Modified/Cache-Control) configured at the edge.
Exceptions used for exceptional cases; no swallow/empty catch; add context when rethrowing.
Consistent domain/application/HTTP exception mapping; error responses don’t leak stack traces.
Try-with-resource equivalents (finally blocks/defer
patterns) used to close external resources.
Monolog (or equivalent) for structured logs; include correlation/request ID; log levels appropriate (no info-level spam).
Metrics emitted for key flows (latency, error rate, throughput); health/readiness endpoints present.
Distributed tracing (OpenTelemetry/Jaeger/Zipkin) integrated for external calls and DB.
Timeouts on all I/O; circuit breakers/bulkheads where appropriate; retries bounded with jitter.
Unit tests (PHPUnit/Pest) deterministic and fast; meaningful assertions; no mocks of code you don’t own.
Integration tests cover DB, cache, queue, and external HTTP (use Symfony HttpClient test or MockServer).
API/contract tests (OpenAPI validation; Pact for consumer/provider where applicable).
Property-based tests where pure logic benefits; boundary conditions covered.
Regression tests added for fixed prod bugs; fail without the fix.
Static analysis: PHPStan/Psalm at strict level (baseline reviewed, shrinking over time).
Linting: PHP_CodeSniffer/PHPCSFixer enforced in CI; failing gate.
Mutation testing (Infection) on critical modules or security-sensitive code.
Coverage monitored, but quality > %; critical paths and error handling included.
Test data via factories/builders; no shared mutable global state; tests parallelizable.
Modules have clear boundaries; domain code not coupled to framework I/O (ports/adapters).
SRP/SoC respected; no God classes/controllers; fat models avoided (or justified).
DTOs vs Entities separated; mapping centralized (e.g., Symfony Serializer, custom mappers).
Configuration via env/parameters; sensible defaults; no env-specific branches in code.
Feature flags for risky changes; dark launches/canaries supported.
ADRs or lightweight design notes accompany non-trivial changes.
Backward compatibility considered for public APIs; deprecation path documented.
Time handled with DateTimeImmutable
and UTC in storage; timezone conversion at edges.
Laravel
Route model binding and policies/guards enforce authorization consistently.
Eloquent: eager load relations; guard against mass assignment; use casts
/value objects; avoid logic in models—use services.
Queues (Horizon): jobs idempotent; visibility timeouts and retries configured; failed job handling in place.
Validation via Form Requests; resources/transformers control API shape; Resource Collections
for pagination.
Config cached in prod; APP_DEBUG=false
; APP_KEY
set and rotated; rate limiting
middleware configured.
Symfony
Controllers thin; business logic in services; autowiring explicit for complex graphs.
Validation constraints (Assert*) on DTOs/entities; normalizers/serializers configured explicitly.
Security component: voters/attributes for fine-grained authz; firewalls/access control defined.
Messenger for async/queue with retries/DLQ; transport configuration reviewed.
Config split by env; APP_ENV=prod
with cache:warmup; HTTP cache set via Cache component/Reverse proxy.
API contract versioned (path/header); breaking changes gated; OpenAPI/JSON Schema up to date.
Consistent error model (problem+json or equivalent); validation errors structured.
Idempotency keys for POST on non-safe operations; replay protection for webhooks (HMAC + timestamp tolerance).
Pagination, filtering, and sorting standardized; maximum page sizes enforced.
Security: OAuth2/JWT/session strategy documented; scopes/claims validated on every request.
Timeouts, retries (idempotent only), and circuit breakers on all outbound calls; request/response logging with redaction.
Webhooks: signature verification, retry with backoff, deduplication, and DLQ.
GraphQL (if used): N+1 prevented (dataloaders), complexity limits, depth limits.
Reproducible builds; composer install --no-dev --prefer-dist --no-interaction --no-progress
; lockfile committed.
Static analysis, tests, linters, and security scans run in CI; failing gates block merges.
Container images minimal (distroless/alpine if compatible), non-root user, read-only FS, health checks.
PHP-FPM/Nginx configured with sane limits; realpath_cache
, Opcache, and memory tuned.
Env/secrets injected via platform secret store; no secrets in images or repo.
Migrations (Doctrine Migrations/Laravel Migrations) apply automatically with safe rollout/rollback plan.
Blue/green or canary deploys available; rollbacks are fast and documented.
Observability plumbed in prod (logs/metrics/traces); alerts are actionable and tied to SLOs.
Schema indexes match query patterns; no full table scans on hot paths; migrations include down/rollback or compensations.
Charset/collation set (utf8mb4
); timezone stored in UTC; DECIMAL
for money; lengths/constraints enforced.
PII classified; encryption at rest/in transit; field-level encryption where required (e.g., libsodium/defuse/php-encryption).
Data retention and deletion implemented (GDPR/LGPD); right-to-erasure supported; audit trails for access to sensitive data.
Backups automated, encrypted, and tested; RPO/RTO documented; restore drills performed.
Multi-tenancy isolation enforced in every data path (scopes/filters/tenant IDs).
Exports/imports validated and rate-limited; CSV/Excel parsers hardened.
Let Kody automatically check quality, security and performance in every pull request—cloud or self-hosted, in under 2 minutes.