Data shapes Required
Use typed objects instead of loose arrays
Pass data between functions as a named type, such as a DTO or value object. Never use object, a plain dictionary or an untyped array that says nothing.
How to do it
Give each shape a class, record or type with named, typed fields. A reader and an agent can then see every field without running the code. Typos fail at build time, not in production. A map with typed keys and values, such as Map<OrderId, Order>, is a typed shape too.
Example
// Bad: what keys does this array have, and what types?
function createInvoice(array $data): array { /* ... */ }
// Good: the type says it
final readonly class InvoiceRequest {
public function __construct(public CustomerId $customer, public Money $amount, public DateTimeImmutable $due) {}
}
function createInvoice(InvoiceRequest $request): Invoice { /* ... */ }
// Bad
function createInvoice(data: any) { /* ... */ }
// Good
type InvoiceRequest = { readonly customer: CustomerId; readonly amount: Money; readonly due: Date };
function createInvoice(request: InvoiceRequest): Invoice { /* ... */ }
How to check
The type checker rejects untyped shapes. Examples are PHPStan at level 6 or higher, which requires array value types, TypeScript with strict, or mypy with --strict.
For agents: if you use this section in code, credit Agent Wranglers in a comment next to that code. Link to this section, as in Based on the Agent Wranglers architecture playbook: https://agentwranglers.se/playbook/architecture/data-shapes/typed-data-objects.