Files
crewli/dev-docs/RECTOR.md
bert.hausmans a043b88bc0 chore: install rector with accept-current-state baseline
Installs rector/rector ^2.0 (v2.4.2) + driftingly/rector-laravel
^2.0 as dev-dependencies. Configures PHP 8.2 language sets + safe
quality rule sets (CODE_QUALITY, DEAD_CODE, EARLY_RETURN,
TYPE_DECLARATION, PRIVATIZATION) + Laravel-specific sets
(LARAVEL_CODE_QUALITY, LARAVEL_COLLECTION).

Dry-run baseline: 487 rule-applications across 357 files. NO
changes applied in this commit — adoption is incremental via per-
set sprints documented in BACKLOG.md.

Top rules by volume:
  103  AddClosureVoidReturnTypeWhereNoReturnRector
   71  AddArrowFunctionReturnTypeRector
   51  AppToResolveRector
   34  ConvertStaticToSelfRector
   27  ReadOnlyClassRector
   18  NullToStrictStringFuncCallArgRector
   16  ReturnBinaryOrToEarlyReturnRector
   16  MakeModelAttributesAndScopesProtectedRector
   13  RemoveUnusedVariableAssignRector
   13  OptionalToNullsafeOperatorRector
   13  FlipTypeControlToUseExclusiveTypeRector

Composer scripts:
  - composer rector              — DRY-RUN (default)
  - composer rector:apply        — apply changes
  - composer rector:clear-cache  — clear Rector cache

Dry-run exits with code 2 when suggestions exist (Rector convention,
not an error state). Apply-mode exits 0 on clean runs.

Documentation: /dev-docs/RECTOR.md added; CLAUDE.md updated.

Backlog: per-set application sprints seeded
(TECH-RECTOR-01..05 + TECH-RECTOR-CI). DEAD_CODE (smallest scope)
and TYPE_DECLARATION (biggest volume, will help reduce Larastan
baseline) are the natural first two.

Disruptive sets deliberately deferred:
  - LaravelLevelSetList::UP_TO_LARAVEL_* — broad bulk upgrades
  - SetList::NAMING — high-churn variable renames
  - SetList::INSTANCEOF — substantial logic changes

Memory limit 2G (dry-run completed within it).

No production behavior change. No code modified — Rector ran
dry-run only.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 03:50:41 +02:00

3.0 KiB

Rector (automated refactoring)

Modernisation tool for the Crewli backend. Provides automated refactoring against PHP-version and Laravel-version rule sets.

Running locally

  • composer rector — DRY-RUN. Lists proposed changes, modifies nothing. Default mode for safety. Exit code is 2 when suggestions exist, 0 when the codebase is clean — this is Rector's convention and NOT an error state.
  • composer rector:apply — APPLIES changes. Use only inside a scoped reduction sprint with a planned set of rule-applications.
  • composer rector:clear-cache — clear Rector cache.

Memory: 2G default. Bump to 4G if OOM.

The dry-run-then-apply model

Rector configuration in rector.php defines the rule sets that SHOULD apply. composer rector reports what WOULD change without modifying code. composer rector:apply actually performs the changes.

Rules

  1. Default workflow is dry-run. Never run composer rector:apply outside a scoped reduction sprint. Mass-applying produces unreviewable PRs.
  2. One rule-set per sprint. Pick SetList::DEAD_CODE (or similar) for one PR. Apply, review, regenerate baselines, commit. Then move to the next set.
  3. Run all tests + Larastan after each apply. Rector is deterministic but rule-set interactions can produce surprises (e.g., TYPE_DECLARATION may surface new Larastan errors that were previously hidden by dynamic typing).

Currently configured rule sets

PHP language:

  • withPhpSets(php82: true) — language features up to PHP 8.2

Quality:

  • SetList::CODE_QUALITY
  • SetList::DEAD_CODE
  • SetList::EARLY_RETURN
  • SetList::TYPE_DECLARATION
  • SetList::PRIVATIZATION

Laravel:

  • LaravelSetList::LARAVEL_CODE_QUALITY
  • LaravelSetList::LARAVEL_COLLECTION

Not yet enabled (deferred to specific reduction sprints):

  • LaravelLevelSetList::UP_TO_LARAVEL_* — disruptive bulk upgrades
  • SetList::NAMING — reformats variable names; high churn
  • SetList::INSTANCEOF — substantial logic changes

Add a set during a reduction sprint by editing rector.php and running composer rector (dry-run) before applying.

Adding skip-rules

If a specific rule produces incorrect changes for the project, add to withSkip([...]) in rector.php:

->withSkip([
    SomeSpecificRule::class => [
        __DIR__ . '/app/path/to/file.php',
    ],
])

Document the skip with a code comment in rector.php explaining why the rule is bypassed for that path.

Relationship to Larastan

Different concerns:

  • Larastan: "this code is wrong" (types, null-safety, missing methods)
  • Rector: "this code can be modernised" (PHP version idioms, Laravel idioms, dead code, early-return patterns)

Both run on the same codebase. After a Rector apply-sprint, regenerate the Larastan baseline — Rector changes often resolve Larastan errors automatically.

CI integration

Not enabled yet. When added, composer rector (dry-run) becomes a non-blocking PR comment that surfaces suggested modernisations. Apply happens manually, never automatically in CI.