3.5 KiB
Backend context
Read
ai/shared.mdfirst. This file only covers backend-specific rules.
Project Context
Stack: PHP 8.x, Slim 4, PHP-DI/Slim-Bridge, PHPUnit (dev), Composer.
Persistence: PostgreSQL.
Architecture: Domain-Driven Design. Code is organized by domain entity
under backend/app/ (PSR-4 App\\) into Entities, DTOs, Repositories,
Use Cases, and Fakes (in-memory repos for tests). The entity list is
intentionally omitted here - update this section as entities land.
Code patterns
- Look at similar entities for reference before writing anything new
- Entities: constructor with properties, getters
- DTOs: simple data containers for creation (e.g.
CreateElementDto) - Repositories: interfaces that define data access
- Do not write unit tests for concrete repository implementations
(e.g.
Postgres*Repository). They are exercised by e2e tests. Use cases are tested with fake repositories. - Repository methods that find records by a foreign key should accept
the related entity, not a raw id (e.g.
findBySet(Set $set), notfindBySetId(int $setId)).
- Do not write unit tests for concrete repository implementations
(e.g.
- Use cases: business logic with Request objects
- When throwing exceptions, add
@throwsdocblock
- When throwing exceptions, add
- Fakes: in-memory implementations for testing
- Live under
tests/Fakes/ - Find/lookup methods must return a new instance of the entity, not the stored reference
- Live under
- Tests: follow
tests/Unit/[Entity]/UseCases/layout- In
setUp, only use fake repositories for entities under test - construct dependency objects directly withnew(e.g.new Set(...)) instead of creating them through their fake repositories
- In
PHP rules
- Imports: always put
usestatements at the top of the file, never use inline imports (e.g.\App\Foo\Bar::class) - Closures: never use arrow functions (
fn () =>) - always use regular anonymous functions (function () { return ...; })
Pre-commit
Run phpcs on worked-on directories before committing (phpcs ruleset
to be added to the repo; the Nix flake already provides
php-codesniffer). Run ./vendor/bin/phpunit tests and confirm green.
LLM anti-patterns
Constructs LLMs default to that this project forbids. Name the trap explicitly so you catch yourself before writing it.
- Arrow function:
- Forbidden:
fn ($node) => $node->getId() - Required:
function ($node) { return $node->getId(); }
- Forbidden:
- Inline FQCN type:
- Forbidden:
function f(): \Psr\Http\Message\ResponseInterface - Required: import
ResponseInterface, then returnResponseInterface
- Forbidden:
- Inline
::class:- Forbidden:
Container::get(\App\Foo\Bar::class) - Required: import
Bar, then callContainer::get(Bar::class)
- Forbidden:
- Default param:
- Forbidden:
function f(int $count = 10) - Required:
function f(int $count)
- Forbidden:
- Default in fake:
- Forbidden:
public function create(Dto $dto, bool $strict = true) - Required: no default; every caller passes a value
- Forbidden:
- Lookup returns stored ref:
- Forbidden:
return $this->items[$id] ?? null; - Required: rebuild a new instance with the stored fields
- Forbidden:
- FK lookup by id:
- Forbidden:
findBySetId(int $setId) - Required:
findBySet(Set $set)
- Forbidden:
- Short variable name:
- Forbidden: one-letter or abbreviated names (
$t,$n,$res) - Required: explicit names (
$text,$node,$response)
- Forbidden: one-letter or abbreviated names (
- Em dash:
- Forbidden: comment or docblock containing an em dash character
- Required: use
-
When generating code, scan the diff for these patterns before writing it to disk. If you catch one mid-write, rewrite that line.