Originally published on decentphp.dev — part of the Writing decent PHP course. View this lesson on the original site.
File and folder names are the table of contents for your codebase
Before any developer reads a line of your code, they read the file tree. Folder and file names should answer two questions at a glance:
- "where does X live?"
- "what kind of code lives in this part of the project?"
If the folder structure is organised by technical layer only (Controllers, Models, Helpers, Services), the answer to "where does billing logic live?" is "spread across all of them". If it is organised by feature or domain (Billing, Registration, Reporting), the answer is one folder.
Before
A typical "everything by layer" structure. Easy to set up, harder to navigate as the project grows.
src/
Controllers/
UserController.php
OrderController.php
StuffController.php
Models/
User.php
Order.php
Misc.php
Services/
UserService.php
OrderService.php
Helper.php
Utils.php
Helpers/
functions.php
common.php
Problems you can read straight off the tree:
- to change registration, you touch
UserController,UserService,User, and probablyHelpertoo - there is a
Helpers/folder and aHelperclass — duplication of the worst suffixes (see lesson 2.6) StuffController,Misc,Utils,common.php— names with no commitment to a concept- nothing tells you where a new feature should go
After
The same code reorganised so each folder answers "what feature lives here?", with a small Shared/ area for code that genuinely crosses features.
src/
Registration/
RegisterUserController.php
RegisterUserHandler.php
SignupInputValidator.php
Billing/
ChargeCustomerController.php
InvoiceRepository.php
OrderTotalCalculator.php
Reporting/
MonthlySalesReport.php
SalesReportRenderer.php
Shared/
Money.php
EmailAddress.php
Now the file tree alone tells you:
- where new registration code goes
- which features touch billing
- that
MoneyandEmailAddressare reusable value objects, not feature-specific code - that there is no dumping ground — the lack of a
Misc/folder is intentional
Why this version is better
- finding code is folder-level, not project-wide search
- a feature change usually fits in one folder, which makes diffs and reviews smaller
- adding a new feature means adding a new folder, not editing six existing ones
- removing a feature is a
rm -rf, not an archaeology dig
File naming rules that pay off
| Rule | Why it matters |
|---|---|
| One PHP class per file | matches PSR-4 autoloading; the file name is the class name |
| File name = class name (case-sensitive) | broken on case-insensitive macOS / case-sensitive Linux otherwise |
PascalCase.php for class files |
e.g. OrderTotalCalculator.php |
kebab-case for assets and docs |
e.g. signup-flow.md, order-receipt.css |
| Avoid catch-all files | no functions.php, no helpers.php, no common.php |
| Avoid version numbers in file names | use git history; OrderServiceV2.php rots fast |
Folder naming rules that pay off
| Rule | Why it matters |
|---|---|
| Folder names are domain nouns | Billing/, Registration/, not Stuff/, Misc/ |
| Group by feature first, by type second | Billing/Controllers/ is fine; Controllers/Billing/ is harder to navigate |
| Keep nesting shallow | three levels is plenty for most apps |
| One folder per concept | not two folders called Helpers/ and Utils/ doing the same thing |
| No abbreviations | Reporting/ not Rpt/, Subscriptions/ not Subs/ |
| Singular for concept folders, plural for collections | Billing/ (the concept), Invoices/ (the collection of invoice classes) |
Names to avoid
helpers/,utils/,common/,misc/,stuff/,lib/— bins that turn into junk drawers- generic file names like
index.php,script.php,class.php(outside framework entry points) - mixed casing in the same folder (
User.phpnext touserprofile.php) — fragile across operating systems - duplicated concept names across folders (
Auth/User.phpandProfile/User.phpreferring to the same concept)
A small file/folder smell test
Stand up the folder tree in your head and ask:
- Could a new developer guess which folder a feature lives in without asking?
- Does any folder name contain
Helper,Util,Common, orMisc? - If I add a new feature next sprint, do I know exactly where it goes?
If the answer to (1) or (3) is no, or the answer to (2) is yes, the structure is doing less work for you than it should be.
From Writing decent PHP — Naming variables, functions, classes, and files well