Rules Hub
Coding Rules Library
Rule priority, scope & exceptions
Use this to align rules with the senior-level structure (P0/P1/P2, scope, exceptions/tradeoffs).
Use one account identity policy for login and recovery
Use the same identifier normalization and account resolver for registration, login and account recovery. Enforce uniqueness under that policy and handle legacy ambiguous matches explicitly instead of selecting an arbitrary account or silently dropping recovery.
Bad example
| 1 | type Account = { id: number; email: string }; |
| 2 | export const loginLookup = (accounts: Account[], email: string) => |
| 3 | accounts.find(account => account.email === email); |
| 4 | export const resetLookup = (accounts: Account[], email: string) => |
| 5 | accounts.find(account => account.email.toLowerCase() === email.toLowerCase()); |
Explanation (EN)
Recovery accepts an address spelling that login rejects, and find silently chooses one case-variant account.
Objašnjenje (HR)
Oporavak prihvaća zapis adrese koji prijava odbija, a find tiho bira jedan od računa koji se razlikuju po velikim slovima.
Good example
| 1 | type Account = { id: number; email: string }; |
| 2 | type Lookup = |
| 3 | | { status: 'found'; account: Account } |
| 4 | | { status: 'missing' } |
| 5 | | { status: 'ambiguous' }; |
| 6 | const accountKey = (email: string) => email.trim().toLowerCase(); |
| 7 | export function resolveAccount(accounts: Account[], email: string): Lookup { |
| 8 | const matches = accounts.filter(account => accountKey(account.email) === accountKey(email)); |
| 9 | if (matches.length === 0) return { status: 'missing' }; |
| 10 | if (matches.length > 1) return { status: 'ambiguous' }; |
| 11 | return { status: 'found', account: matches[0] }; |
| 12 | } |
| 13 | export const loginLookup = resolveAccount; |
| 14 | export const resetLookup = resolveAccount; |
Explanation (EN)
Both flows use the same explicit matching policy and distinguish ambiguity from absence.
Objašnjenje (HR)
Oba toka koriste istu politiku podudaranja i razlikuju nejednoznačan rezultat od nepostojećeg računa.
Notes (EN)
The example assumes the application has chosen case-insensitive email identity. Use the same canonical key on writes and rate-limit identifiers, enforce it with a database unique constraint, and reconcile existing collisions before migration. Record ambiguous outcomes internally without exposing raw addresses or account existence in public recovery responses.
Bilješke (HR)
Primjer pretpostavlja da aplikacija tretira email kao identitet neovisan o veličini slova. Isti ključ koristi pri upisu i ograničavanju zahtjeva, uz UNIQUE ograničenje u bazi. Prije migracije razriješi postojeće kolizije. Nejednoznačnost zabilježi interno bez otkrivanja sirovih adresa ili postojanja računa kroz javni odgovor za oporavak.
Exceptions / Tradeoffs (EN)
An external identity provider or a case-sensitive identifier may require another policy. Preserve that policy consistently; do not apply provider-specific dot or plus-address normalization to arbitrary email addresses.
Iznimke / Tradeoffi (HR)
Vanjski identity provider ili identifikator osjetljiv na veličinu slova može zahtijevati drugu politiku. Primjenjuj je dosljedno; ne uklanjaj točke ili plus-dodatke proizvoljnim email adresama.