Created
January 3, 2026 16:27
-
-
Save benjamincrozat/ace3ba292a971ca42dd1d6769efb4dde to your computer and use it in GitHub Desktop.
Put it in .cursor/rules/best-practices or wherever you prefer
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| --- | |
| alwaysApply: true | |
| --- | |
| ## Code style & clarity | |
| - Always document intent for ambiguous or non-obvious code. Explain why the code exists (not what it does). | |
| - Always import namespaces. | |
| - Avoid one letter or ambiguous variable names. | |
| - Comments start with a capital letter and end with a period. | |
| - Don't create a variable if it's used only once. | |
| - Don't write stupidly obvious comments like "Execute the job." or "Create a new job instance." when the method's name says the same thing. | |
| - If a method is 15 lines long, it's probably worth splitting it into smaller methods. | |
| - Never leave debugging helpers in the codebase (e.g., dd(), dump()). | |
| - Never use the "final" keyword. | |
| - Never use the PHP error suppression operator `@`. If it's absolutely unavoidable, document the intent in a comment and prefer an explicit alternative (pre-checks, try/catch, etc.). | |
| - Non-public methods and properties are protected by default unless there's a very good reason. | |
| - Prefer guard clauses / early returns over deep nesting. | |
| - Take inspiration from the codebase for consistency. | |
| ## Architecture & app structure | |
| - Avoid generic “Service/Manager/Handler” classes. Prefer small, verb-named Actions. | |
| - Controllers should be thin. Orchestrate requests and delegate business logic to Actions. | |
| - Group code between three namespaces: Core, Marketing, and Shared. Then, subnamespaces for specific features. For instance, actions for YouTube go in App\Actions\Core\Youtube, controllers for marketing pages (like landing pages, lead magnets, etc.) go in App\Http\Controllers\Marketing, and shared code (like helpers, interfaces, etc.) goes in App\Models\Shared. | |
| - I prefer single-action controllers over resource controllers. | |
| - Jobs should be thin. Delegate business logic to Actions and stay idempotent when possible. | |
| - When creating new test files, map 1:1 the structure to the one of the ./app folder. If you want to write tests for a file that doesn't exist in the app folder, you have to have very good reasons and you have to create the test file at the root (e.g. ./tests/Feature). | |
| - When the directory structure in the app folder changes, make sure tests follow it. | |
| - When you create a new model, create a factory and a seeder for it. | |
| ## Laravel conventions & dependency boundaries | |
| - Always do things the Laravel way like using its helpers, Collections, Facades, attributes (#[Config('foo.bar'), #[Storage('local')], etc.). | |
| - Avoid raw queries unless necessary. If you must, parameterize them and document why. | |
| - Don't use dependency injection. Use Facades, Real-Time Facades, or the app() helper. | |
| - Never call env() outside config files. | |
| - Prefer generating code through Artisan commands to stay up to date with the latest changes. | |
| - Prefer named routes + route() over hardcoded URLs (in app code too, not just tests). | |
| - Use helper functions instead of Facades if available. E.g., session() instead of Session::get(). | |
| ## Testing (Pest) | |
| - In tests, avoid hardcoding hosts/URLs. Prefer `route()` / `url()` so tests work across different `APP_URL`s. | |
| - In tests, fake OpenAI calls using OpenAI::fake() with Responses CreateResponse::fake() instead of mocking from scratch. | |
| - Mocking in tests should be a last resort thing. I prefer reliability over speed for my test suite. | |
| - Pest tests must always import the global functions like "use function Pest\Laravel\actingAs". | |
| - Prefer strict fakes over permissive mocks: make test doubles fail loudly when the integration contract changes (wrong IDs, missing required params like Stripe `expand`, etc.). | |
| - Tests must be as granular as possible. | |
| - Tests must be parallel-safe. Avoid shared fixed file paths and always clean up files you create. | |
| - Use Real-Time Facades in tests if you want to mock something pulled from the container. | |
| - When writing tests, run and fix them until they pass. (Filter or use the --parallel option to speed up the process.) | |
| - When you see $this in Pest tests, it's a smell. Nuke it by importing the equivalent function when possible. | |
| ## Tooling | |
| - Prefer running tests in parallel when possible (`php vendor/bin/pest --parallel`). | |
| - Run `php vendor/bin/pint --parallel` to format the code whenever you make a change. | |
| ## Data & migrations | |
| - Migrations must be reversible when possible. | |
| - Never edit old migrations after they’ve been merged. Create a new migration instead. |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment