You are starting a new open-source PHP package in 2026; which coding style standard do you adopt, and how do you make contributors follow it?
answer
- name the standard and its version
- PER Coding Style builds on PSR-12
- a formatter and a check in CI
- PSR-1 rules a formatter cannot fix
- one formatting commit, no mixed diffs
basics
~20 sAdopt PER Coding Style at a pinned version, which builds on PSR-12 and PSR-1, state it in the contributing guide, and enforce it with committed formatter configuration plus a CI check, so style never becomes a review discussion.
solid answer
~50 sFor a new package I would adopt **PER Coding Style**, the evolving successor to PSR-12, at a **named version**, because a PER can change across major releases and contributors need a fixed target. Since PER builds on PSR-12 and PSR-1, users who know either will find nothing surprising. Then make it mechanical: commit the formatter and sniffer configuration, pinned to that version rather than a moving 'latest', provide a single Composer script to fix and one to check, and fail CI when the check fails. State the choice in `CONTRIBUTING.md`. Some rules no formatter can apply, such as PSR-1's declare-or-side-effects rule and class-name casing, so reviewers still check those. When upgrading the PER version later, do it in one commit that changes only formatting, so `git blame` and review stay readable. If supporting very old PHP versions, PSR-12 notes that rules for syntax your minimum version lacks may be ignored.
code
json · 10 lines{
"name": "acme/money",
"require": {
"php": ">=8.3"
},
"scripts": {
"cs:check": "php-cs-fixer check --diff",
"cs:fix": "php-cs-fixer fix"
}
}go deeper
Know which standard to name for new code (PER Coding Style or at least PSR-12, never PSR-2) and that a formatter applies it for you.
Describe the setup: pinned standard in committed tool config, Composer scripts to fix and check, a CI job that fails on violations.
Cover the rules tools cannot enforce, version pinning for an evolving standard, and a migration done as one formatting-only commit excluded from blame.
Explain how a package's style choice affects its contributors and users across frameworks, and why a shared PHP-FIG standard beats a house style for an open-source library.
## The decision For a new open-source package, the realistic candidates are: | Option | Pros | Cons | |---|---|---| | **PER Coding Style**, pinned version | current PHP-FIG work, covers newer syntax, builds on PSR-12 | changes across major releases, so you must pick and pin a version | | **PSR-12** | stable, frozen, universally known | silent on syntax added after 2019 | | A custom or framework style | matches one ecosystem | contributors must learn it; tools need custom configuration | | **PSR-2** | none for new code | deprecated since 2019 | For a library meant to be used across frameworks, PER Coding Style is the natural default: it is PHP-FIG's own continuation of PSR-12, so contributors familiar with any PHP-FIG style already know most of it. PSR-12 remains a defensible choice if you value a text that will never change. ## Pin the version A PER evolves with Semantic Versioning, and a major release can change what is required. So the package should name, for example, "PER Coding Style 3.0", not just "PER". Formatting tools often offer both a pinned preset and an alias for the latest version. Choosing the pinned one means a tool upgrade never reformats the codebase by surprise; moving to the next PER release becomes a deliberate change. ## Make it mechanical Style rules that depend on reviewers' attention are applied inconsistently. The adoption steps: 1. **Commit the tool configuration** for the formatter and, if you use one, the sniffer, with the pinned standard. 2. **Add Composer scripts**, such as `composer cs:fix` and `composer cs:check`, so contributors run one command without knowing the tool. 3. **Check in CI** on every pull request and fail the build on violations, reporting the offending files. 4. **Document it** in `CONTRIBUTING.md`: the standard and version, the two commands, and a note that style-only feedback in review is replaced by the check. 5. **Optionally**, share an `.editorconfig` for indentation and line endings, so editors get 4 spaces and LF right before any tool runs. ## What tools cannot enforce Formatters handle layout: indentation, braces, spacing, blank lines, import order. Some PSR-1 rules are about meaning, not layout: - a file SHOULD declare symbols or cause side effects, not both; - class names in `StudlyCaps` and method names in `camelCase` (renaming a public method is an API change, not a formatting fix); - consistent property naming within a scope. Reviewers still watch for these, especially the side-effects rule, which matters most for a library loaded by other people's autoloaders. ## Existing code and upgrades If the package starts from existing code, or later moves to a new PER version: - apply the new style in **one commit that changes only formatting**, with no logic changes mixed in; - list that commit in a `.git-blame-ignore-revs` file so `git blame` skips it; - merge or rebase open pull requests right after, since they will conflict with the reformatting. Mixing formatting and behaviour changes in one diff is the most common way a style migration makes a real bug hard to review. ## A contributor's first pull request With the setup above, a new contributor's experience is short: 1. Clone the repository and run `composer install`. 2. Make the change. 3. Run the fix script, which rewrites layout to the pinned standard. 4. Push; CI runs the check script and passes, or reports exactly which files still differ. No reviewer comments about braces or blank lines are needed, and review time goes to behaviour, naming and design. If the check fails in CI but passes locally, the usual cause is a different tool version, which is why the tool itself should be a pinned development dependency rather than a globally installed binary. ## Minimum PHP version PSR-12 says rules for features that do not exist in the PHP versions your project supports may be ignored. A package supporting old PHP versions simply has no property hooks or enums to format. The standard does not force you to raise your minimum version.
- Why pin a specific PER Coding Style version rather than always following the latest release?PER Coding Style follows Semantic Versioning, and a major release may require changes the previous one did not. Following 'latest' means a routine tool update can reformat many files and fail CI for contributors who changed nothing. Pinning makes each upgrade a deliberate, single formatting commit that you schedule.
- A contributor's pull request mixes a bug fix with reformatting of the whole file. What do you ask for?Split it: the bug fix in its own commit or pull request, and any formatting change separately, ideally applied by the project's formatter in one repository-wide commit. Mixed diffs hide the behaviour change among hundreds of whitespace changes, which defeats review. The CI style check should make unrequested reformatting unnecessary in the first place.
saying these in an interview costs you the question
- Chooses PSR-2 for a new package because older projects use it.
- Relies on reviewers to spot brace and indentation problems by eye.
- Follows the latest PER release with no pin and is surprised by mass reformatting.
- Assumes a formatter enforces PSR-1's side-effects rule.
- Reformats the whole codebase in the same commit as a behaviour change.