@@ -40,12 +40,39 @@ Breaking changes inside `Internal/` are not semver-breaking for the library.
40401 . Every class, property, method, and exception name reflects the ** concept** the library represents. A math library
4141 uses ` Precision ` , ` RoundingMode ` ; a money library uses ` Currency ` , ` Amount ` ; a collection library uses
4242 ` Collectible ` , ` Order ` .
43- 2 . Never use generic technical names as class suffixes, prefixes, or method names: ` Manager ` , ` Helper ` , ` Processor ` ,
44- ` Handler ` , ` Service ` , ` Data ` , ` Info ` , ` Utils ` , ` Item ` , ` Record ` , ` Entity ` , ` Exception ` , ` process ` , ` handle ` ,
45- ` execute ` , ` mark ` , ` enforce ` , ` manage ` , ` ensure ` , ` validate ` , ` check ` , ` verify ` , ` assert ` , ` transform ` , ` parse ` ,
46- ` compute ` , ` sanitize ` , or ` normalize ` .
47- 3 . Name classes after what they represent: ` Money ` , ` Color ` , ` Pipeline ` — not after what they do technically.
48- 4 . Name methods after the operation in its vocabulary: ` add() ` , ` convertTo() ` , ` splitAt() ` .
43+ 2 . Name classes after what they represent: ` Money ` , ` Color ` , ` Pipeline ` — not after what they do technically.
44+ 3 . Name methods after the operation in the library's vocabulary: ` add() ` , ` convertTo() ` , ` splitAt() ` .
45+
46+ ### Always banned
47+
48+ These names carry zero semantic content. Never use them anywhere, as class suffixes, prefixes, or method names:
49+
50+ - ` Data ` , ` Info ` , ` Utils ` , ` Item ` , ` Record ` , ` Entity ` .
51+ - ` Exception ` as a class suffix (e.g., ` FooException ` — use ` Foo ` when it already extends a native exception).
52+
53+ ### Anemic verbs (banned by default)
54+
55+ These verbs hide what is actually happening behind a generic action. Banned unless the verb ** is** the operation
56+ that constitutes the library's reason to exist (e.g., a JSON parser may have ` parse() ` ; a hashing library may
57+ have ` compute() ` ):
58+
59+ - ` ensure ` , ` validate ` , ` check ` , ` verify ` , ` assert ` , ` mark ` , ` enforce ` , ` sanitize ` , ` normalize ` , ` compute ` ,
60+ ` transform ` , ` parse ` .
61+
62+ When in doubt, prefer the domain operation name. ` Password::hash() ` beats ` Password::compute() ` ; ` Email::parse() `
63+ is fine in a parser library but suspicious elsewhere (use ` Email::from() ` instead).
64+
65+ ### Architectural roles (allowed with justification)
66+
67+ These names describe a role the library offers as a building block. Acceptable when the class ** is** that role
68+ (e.g., ` EventHandler ` in an events library, ` CacheManager ` in a cache library, ` Upcaster ` in an event-sourcing
69+ library). Not acceptable on domain objects inside the library (value objects, enums, contract interfaces):
70+
71+ - ` Manager ` , ` Handler ` , ` Processor ` , ` Service ` , and their verb forms ` process ` , ` handle ` , ` execute ` .
72+
73+ The test: if the consumer instantiates or extends this class to integrate with the library, the role name is
74+ legitimate. If the class models a concept the consumer manipulates (a money amount, a country code, a color),
75+ the role name is wrong.
4976
5077## Value objects
5178
@@ -60,20 +87,23 @@ Breaking changes inside `Internal/` are not semver-breaking for the library.
6087
61881 . Every failure throws a ** dedicated exception class** named after the invariant it guards — never
6289 ` throw new DomainException('...') ` , ` throw new InvalidArgumentException('...') ` ,
63- ` throw new RuntimeException('...') ` , or any other generic native exception with a string message . If the
64- invariant is worth throwing for, it is worth a named class.
90+ ` throw new RuntimeException('...') ` , or any other generic native exception thrown directly . If the invariant
91+ is worth throwing for, it is worth a named class.
65922 . Dedicated exception classes ** extend** the appropriate native PHP exception (` DomainException ` ,
6693 ` InvalidArgumentException ` , ` OverflowException ` , etc.) — the native class is the parent, never the thing that
6794 is thrown. Consumers that catch the broad standard types continue to work; consumers that need precise handling
6895 can catch the specific classes.
69- 3 . Exceptions are pure: no transport-specific fields (` code ` , formatted ` message ` ). Formatting to any transport
70- happens at the consumer's boundary, not inside the library.
96+ 3 . Exceptions are pure: no transport-specific fields (` code ` populated with HTTP status , formatted ` message ` meant
97+ for end-user display). Formatting to any transport happens at the consumer's boundary, not inside the library.
71984 . Exceptions signal invariant violations only, not control flow.
72995 . Name the class after the invariant violated, never after the technical type:
73100 - ` PrecisionOutOfRange ` — not ` InvalidPrecisionException ` .
74101 - ` CurrencyMismatch ` — not ` BadCurrencyException ` .
75102 - ` ContainerWaitTimeout ` — not ` TimeoutException ` .
76- 6 . No exception-formatting constructor, no custom message argument — the class name is the message.
103+ 6 . A descriptive ` message ` argument is allowed and encouraged when it carries ** debugging context** — the violating
104+ value, the boundary that was crossed, the state the library was in. The class name identifies the invariant;
105+ the message describes the specific violation for stack traces and test assertions. Do not build messages meant
106+ for end-user display or transport rendering. Keep them short, factual, and in American English.
771077 . Public exceptions live in ` src/Exceptions/ ` . Internal exceptions live in ` src/Internal/Exceptions/ ` .
78108
79109** Prohibited** — throwing a native exception directly:
@@ -84,7 +114,7 @@ if ($value < 0) {
84114}
85115```
86116
87- ** Correct** — dedicated class extending the native exception :
117+ ** Correct** — dedicated class, no message (class name is sufficient) :
88118
89119``` php
90120// src/Exceptions/PrecisionOutOfRange.php
@@ -98,6 +128,14 @@ if ($value < 0) {
98128}
99129```
100130
131+ ** Correct** — dedicated class with debugging context:
132+
133+ ``` php
134+ if ($value < 0 || $value > 16) {
135+ throw new PrecisionOutOfRange(sprintf('Precision must be between 0 and 16, got %d.', $value));
136+ }
137+ ```
138+
101139## Enums
102140
1031411 . Are PHP backed enums.
0 commit comments