← Lessons |

Machine Code Review — The First Line of Defense

The Problem With Human Code Review

Human code review is expensive. It requires context-switching, careful reading, and genuine expertise — and that attention is finite. When it gets burned on style violations, missing type hints, or a failing unit test that should have been caught before the PR was even opened, everyone loses.

The reviewer gets frustrated. The author gets feedback that feels trivial. The review takes twice as long as it should. And the actual design question — is this the right approach? is this readable by someone who didn’t write it? does this interact safely with the rest of the system? — gets less attention than it deserves.

Machine code review exists to prevent that.

What It Is (And What It Isn’t)

Machine code review is automated static analysis, style enforcement, and test execution run as a single unified check before a human ever looks at the code. It is the first line of defense, not a replacement for human review.

The distinction matters. Machine code review catches things machines are better at:

  • Style and formatting — inconsistent indentation, missing newlines, violated naming conventions
  • Static type errors — passing the wrong type, calling a method that doesn’t exist, unreachable code paths
  • Test failures — regressions that were already covered by the test suite

Human code review then focuses on what humans are better at:

  • Is this solving the right problem?
  • Is the design pattern appropriate for the context?
  • Is the logic correct in ways the type system can’t verify?
  • Will the next developer be able to understand this without asking the author?

When the machine has already handled the mechanical checks, a human reviewer can read the diff with full attention on the design and logic — which is the review that actually requires judgment.

How It Works in UbixCore

UbixCore implements machine code review as a CLI command:

bin/ubix code:review
bin/ubix code:review --modified          # review only files changed in this branch
bin/ubix code:review path/to/file.php   # review a specific file

Under the hood, three tools run in sequence:

PHPStan — Static analysis at level max with bleeding-edge rules enabled. PHPStan reads the entire codebase’s type graph and flags errors that would only surface at runtime: passing null where a string is expected, calling methods on potentially-null objects, using array shapes incorrectly. At level max it has no tolerance for ambiguity — if PHPStan can’t prove the code is type-safe, it says so.

PHP_CodeSniffer (PHPCS) — Style and standards enforcement against a custom ruleset. This covers PSR/PER standard formatting plus project-specific rules: docblock requirements, import ordering, function complexity limits, and anything else the team has decided is non-negotiable. Violations that PHPCS can fix automatically are flagged as AUTO-FIXABLE — the tool will offer to run PHP Code Beautifier/Fixer (phpcbf) on the spot, up to three passes, to resolve what it can without human involvement.

PHPUnit — The full test suite. A violation here is a regression — code that broke something that was already working. This runs last because it’s the slowest and the most definitive: if tests fail, nothing else matters until they pass.

Results are reported file-by-file, violation-by-violation, with the file path, line number, rule name, and tool clearly labeled:

3 violations  php/Ubix/Service/SomeService.php
  42  Missing return type declaration  — phpstan: missingType.return
  67  AUTO-FIXABLE Line exceeds 120 characters  — phpcs: Generic.Files.LineLength.TooLong
  89  TEST FAILED SomeServiceTest::testHandlesNullInput  — phpunit: SomeServiceTest::testHandlesNullInput

At the end, a summary shows pass/fail per tool and a total violation count. If auto-fixable violations exist, the developer is prompted to apply them immediately.

Why the Machine Is the “Bad Guy”

One of the most underrated benefits of machine code review is social.

Nobody likes being told their code is wrong. When that feedback comes from a teammate in a PR comment, it can feel personal — even when it isn’t. The reviewer has to decide whether to say something about the missing type hint or let it go to avoid friction. The author has to receive the comment in the spirit it was intended.

When a machine catches it before the PR is opened, that dynamic disappears. The machine has no feelings and no politics. It flags every violation with the same flat affect regardless of who wrote the code, how senior they are, or what mood anyone is in. The author fixes it, the machine confirms it’s fixed, and the PR opens clean.

This means human reviewers never have to be the style police. They can focus entirely on the design and logic questions — the review that actually requires a person.

The Right Place in the Workflow

Machine code review belongs between writing code and opening a pull request. In UbixCore, bin/ubix code:review --modified runs against everything changed in the current branch — so a developer gets a full report of what needs to be addressed before anyone else sees the work.

It is not a CI gate replacement, though it can inform one. Running PHPStan at level max and the full PHPUnit suite in CI is still important — the CI run is the authoritative record. But catching violations locally before pushing means the CI run succeeds on the first attempt, which keeps the pipeline fast and the history clean.

The goal is simple: by the time a human opens the diff, the code already compiles cleanly, passes all the tests, and follows every style rule the team has agreed on. The reviewer’s job is to evaluate the thinking, not the formatting.

That’s the right division of labor.

Tags: Code Quality PHP PHPStan PHPUnit Developer Tooling Engineering Culture