Stop reading every line of code

A developer inspects a tiny trust-budget slice while PHPStan, Infection, and CI robots handle the larger share in front of a mountain of code diffs.

7/27/2026

AI changed the speed of software. More importantly, it changed the economics of attention. Code arrives faster than teams can seriously inspect it. Yet many teams still behave as if quality means opening more tabs and scrolling through bigger diffs.

Quality cannot depend on a human reading every line before merge.

This is not an argument for lower standards. It is an argument for a trust budget: humans spend scarce attention on meaning and risk; machines spend the rest on structure. In AI-assisted PHP work, that is the only review model that still scales.

There is a public pull request against shopware/shopware with roughly +32,500 lines: Introduce Universal Commerce Protocol (UCP) server foundation. Feature flag. Discovery. Signing. Idempotency. OAuth. Admin module. Migrations. Tests. ADRs.

That PR was serious engineering. It was also the moment the old quality story broke for me.

Nobody can honestly read every line of a change that size and still call it a quality system. Reviewers can argue architecture, security posture, and extension contracts. They cannot be infinite parsers for thirty thousand new lines, especially when AI helps produce that volume in the first place.

The trust budgetLink to this section

Traditional code review assumes a reviewer can validate a change by reading enough of the diff carefully. That was never perfect. With AI it fails faster, because code can look plausible long before it is trustworthy.

GitClear and the follow-up 2026 maintainability work show the shape of the problem at industry scale: less refactoring, more duplication, less legacy upkeep. An ISSRE 2025 study found AI-generated samples often simpler and more repetitive, and more prone to certain defect profiles and high-risk security issues than human-written code.

Faster code generation without stronger verification is just faster risk creation.

So the job of review changes:

Spend human attention onAutomate hard
Intent and product fitType safety and impossible states
Architecture and boundariesRegressions and mechanical consistency
Domain invariantsStyle, upgrades, repetitive refactors
Security-sensitive behaviorEvidence that tests actually detect faults

Humans should review meaning. Machines should review structure.

The question is no longer “did someone scroll the whole diff?” The question is “why should this system trust the change?”

What I reject in reviewLink to this section

If a PR asks me to approve volume instead of risk, I push back. Concrete rejects:

  1. One PR that invents a product. Protocol + admin + migrations + crypto + “also the sales channel” in a single review unit.
  2. “Tests are green” as the whole argument. Especially when the same model wrote the implementation and the tests.
  3. Core as the default home for experimental agent surfaces. Fast-moving standards do not belong behind a major-version release train by default.
  4. Review theater. Approve because the diff looks neat, the names look familiar, and nobody wants to be the person who blocks shipping.
  5. Unbounded prompts upstream. “Build Agentic Commerce” is not a task. “Add checkout session complete with signature policy strict” is a task.

False confidence looks like this: AI opens a PR that “adds signature verification.” Tests assert 200 and that a header exists. PHPStan is clean. Mutation would have killed a mutant that skips the signature check on empty body. Nobody ran Infection . The diff looked professional. That is the failure mode. Not syntax errors. False confidence.

What I want instead: small PRs, failing gates before human review, and a short written claim of what must remain true after merge.

Shopware: Agentic Commerce should not wait on one core dumpLink to this section

Shopware merchants need to be reachable by AI shopping agents. The open standard is the Universal Commerce Protocol ( UCP ). The product surface is Agentic Commerce: discovery, feeds, carts, checkout, orders, transports like REST , A2A , embedded, and MCP .

Putting a full UCP server into core felt clean. One repo. First-class. “Proper.”

It was the wrong packaging for review and for velocity.

So we split the work so each piece can move at its own speed:

LayerOwnsWhy
Shopware plugin (SwagAgenticCommerce / store Agentic Commerce)Merchant UX, sales-channel product, DAL wiringShip without waiting on a core release
UCP PHP SDKProtocol models, signing, negotiation, validationReusable PHP, reviewable on its own
Symfony bundleHTTP wiring, routes, default storage, console commandsFramework integration without Shopware in the protocol
Core (thin)Platform primitives only: sales-channel hooks, MCP building blocks, extension pointsKeep core stable; do not freeze a moving protocol in the monorepo

Should Agentic Commerce live in core?Link to this section

Not as a full product and not as a full UCP server.

Core is the wrong home when the protocol still moves on short cycles, the blast radius includes crypto and agent trust boundaries, the change is too large for one meaningful human review, or other PHP stacks should reuse the same protocol code without depending on Shopware.

Core can own thin infrastructure. Shopware already started pulling Agentic Commerce product code toward the plugin and deprecating core copies ahead of 6.8. That matches the trust model.

Putting everything in core can feel elegant. Shipping plugin + SDK + bundle is often the useful solution.

I am close to both sides of this: Shopware platform work and the UCP SDK. The tradeoff is real. Core ownership buys one product story and one review surface. An SDK buys speed, reuse, and reviewable units, but also more repos, more release choreography, and the risk that “not in core” gets read as “not serious.” We accepted that cost because an unreviewable core dump was worse.

We did not split to lower the bar. We split so standards could keep up with the speed.

Where trust actually lives: the UCP PHP SDKLink to this section

If the protocol layer leaves core, trust has to live around it: not inside a model, and not inside a heroic review of one mega-diff.

That is why we built agentic-commerce-alliance/ucp-php-sdk: merchant-side UCP for plain PHP and Symfony. Shopware stays DAL-first in the plugin and talks to the SDK through public contracts.

The useful part for this post is the quality model, not the feature list:

  • narrow packages ( ucp-php-sdk/core vs ucp-php-sdk/symfony-bundle ) instead of one unreviewable dump
  • static analysis and CI as merge gates
  • coverage gates on internal code
  • Infection mutation testing, including a focused security run over signing and key material

If you build PHP agent commerce integrations, start here:

  • Shopware storefront / Admin / DAL product behavior: use the Agentic Commerce plugin path, not a core dump
  • Protocol models, signing, negotiation, validation in plain PHP or Symfony: clone the UCP PHP SDK and keep core thin

PHP tools as a trust stack, not a brochureLink to this section

PHP already has mature tools for this model. The point is not “use nice tooling.” The point is what each tool buys in the trust budget.

PHPStan: kill impossible states before humans lookLink to this section

PHPStan finds bugs without writing tests. In an AI workflow that is not a slogan. It is the first filter.

Strict analysis catches type mismatches, invalid calls, dead assumptions, and broken contracts before review starts. A reviewer who deeply reads one PR and skims the next is not a system. PHPStan is.

In Shopware, PHPStan is not “run level 5 and hope.” Core analysis sits at level 8 with bleeding edge, phpstan/phpstan-symfony, and Shopware-specific rules on top of generic PHP checks. The Symfony extension is what makes the container XML bootstrap useful: PHPStan can see real services and parameters, catch unknown or private service access, and type Container::get() correctly, instead of treating the DI graph as mystery meat. That is how you catch wiring mistakes that look fine in a diff and explode at runtime.

For plugins and apps, shopwarelabs/phpstan-shopware adds the Shopware smell checks AI loves to ignore:

  • no EntityRepository calls inside loops (N+1 by autocomplete)
  • no lazy DAL “filter by ID” shortcuts that bypass the intended query shape
  • no session usage in payment handlers / Store API paths
  • no weak crypto, insecure cookies, or disabled SSL verification

Those are not style nits. They are production failure modes that survive a skim review because the code “looks like Shopware.”

Rule of thumb: human review does not begin while PHPStan is red . In Shopware work, that means core-level strictness or shopwarelabs/phpstan-shopware rules in CI, not a decorative config nobody fails.

Infection: prove tests notice wrong behaviorLink to this section

Coverage is easy to overrate. A green suite can still be happy-path theater.

Infection mutates the AST and asks whether tests fail when the code is subtly wrong. That question gets sharper when AI writes both production code and tests.

Good tests do not just execute code. They detect wrong behavior.

Rule of thumb: for crypto, signing, money, auth, and idempotency paths, mutation results are part of the review evidence, not an optional local hobby.

On the UCP SDK we treat security mutation as a focused gate over signing and key management for exactly that reason. And Infection is not a local hobby there. On PHP 8.2 CI, the scoped mutation gate generated 63 mutants and hit 100% Covered Code MSI . The broader sweep generated 1963 mutants and still landed at 92% Covered Code MSI . Line coverage can say “this ran.” Mutation asks whether the suite would notice if this broke.

Rector: stop spending attention on mechanical translationLink to this section

Rector automates upgrades and repetitive refactors. Necessary work. Bad use of scarce human focus when done by hand across fifty files.

In Shopware land this is not theoretical. frosh/shopware-rector ships version sets for platform upgrades, and Shopware CLI automatic refactoring runs Rector as part of the upgrade path. Shopware core also keeps Rector in CI. The point is not “Rector exists.” The point is that mechanical Shopware migrations should not burn review attention.

A typical rule-shaped change looks like this:

-$context->addExtension(EntityIndexerRegistry::USE_INDEXING_QUEUE, new ArrayEntity());
+$context->addState(EntityIndexerRegistry::USE_INDEXING_QUEUE);

That is not architecture review. That is translation. Let the machine do the translation. Spend humans on whether the indexer queue still means the right thing in your flow.

Rule of thumb: if a change is mechanical, automate it. Save humans for judgment.

A practical workflow for AI-assisted PHPLink to this section

Replace “please review carefully” with controls:

  1. Bound the task. One capability, one boundary, one PR story.
  2. Generate narrow. Prefer small prompts with explicit invariants over “build the feature.”
  3. Fail early in CI. PHPStan / CS / unit tests before anyone is asked for approval.
  4. Demand evidence. For critical paths: tests that fail when behavior breaks; Infection on the risky slice.
  5. Keep PRs reviewable for intent. If you need a scroll marathon, split again.
  6. Review meaning manually. Architecture, domain assumptions, authZ, money, PII, agent trust boundaries.
  7. Treat CI as enforcement. A dashboard nobody fails is decoration.
  8. Measure structural health. Duplication, refactoring, churn. Not just “lines shipped.”

Checklist I actually use on AI-heavy PRsLink to this section

  • What invariant must still hold after merge?
  • What is the blast radius if this is wrong in production?
  • Did we reuse an existing path, or paste a sibling of one?
  • Are tests asserting behavior, or only that code runs?
  • Is security-sensitive logic covered by focused tests / mutation?
  • Could this have been a plugin/SDK change instead of core?
  • Is the PR small enough that a human can review intent in one sitting?

The goal of review is not to read everything. The goal is to prove what matters.

Start MondayLink to this section

If you change nothing else this week:

  1. PHPStan at the highest level you can afford, failing CI before human review.
  2. Infection (or equivalent mutation) on Security/ , signing, money, and auth paths. Not on the whole monorepo on day one.
  3. Every AI-assisted PR states the invariant in the description: what must still be true after merge.
  4. Reject any PR that needs a scroll marathon to understand intent. Split it.

That is the minimum trust pack. Fancy agent orchestration can wait.

The same trust problem shows up one layer higher. CI and PR shape decide whether you can trust a change. Model choice decides whether you can keep shipping when the expensive API blinks.

The new dependencyLink to this section

If team throughput depends on a small number of commercial frontier models, then part of engineering capability now lives outside the team’s control. That is a new dependency, not a clever shortcut.

Frontier models are an operational riskLink to this section

Frontier models are not just expensive in subscription terms. They are expensive in tokens, retries, orchestration overhead, throwaway generations, and workflows that quietly depend on very large context windows. Pricing changes, rate limits, policy shifts, and model regressions become operational risks.

A workflow is not robust if it only works when the most expensive model behaves perfectly.

That is why trust cannot live inside the model. It has to live around it: contracts, tests, static analysis, mutation gates, and small reviewable units.

Useful tokens beat maximum tokensLink to this section

Token maximizing is not the answer. Useful token usage is.

A simple example: an agent sitting in a loop, polling CI every few seconds, re-sending the whole conversation each time, while GitHub Actions paints green or red in the browser. That is not intelligence. That is a very expensive F5 . The human version is free: open the Actions tab, or run gh run watch once and wait for a terminal state. The agent version burns tokens on waiting, not on deciding.

Same pattern shows up everywhere: agents re-reading files they already know, regenerating the same patch five times, stuffing huge context windows “just in case.” More tokens is not more quality. More useful work per token is.

Open source is the pressure valveLink to this section

Open source models and local workflows are not a rejection of frontier models. They are a refusal to make frontier models the foundation of every engineering process.

Local runners and open coding models reduce exposure to pricing shocks, availability changes, and vendor lock-in. They also force better discipline. Smaller context windows and less magical output push teams toward narrower tasks, clearer prompts, stronger contracts, and better validation. That is not always a downgrade. In many cases it is a healthier operating model.

What to use for whatLink to this section

A compact stack that is actually useful:

NeedToolWhy
Local model host with a GUILM StudioEasy downloads, local OpenAI-compatible server, good for trying models
Local model host in the terminalOllamaFast pull/run, simple API, easy to wire into agents
Terminal coding agentAider or OpenCodeGit-native edits, reviewable diffs, works with local or frontier models
IDE multi-step workZedFast editor with agentic multi-step work in the IDE
Hard planning / ambiguous designFrontier modelSpend expensive tokens where judgment leverage is real
Drafts, renames, small refactors, commit messages, local helpersLocal coding modelCheap, private, always available

The point is not to collect tools. The point is to stop paying frontier prices for work a local model can do around your workflow: draft a Rector dry-run summary, explain a PHPStan error, suggest a test name, rewrite a commit message, sketch a narrow prompt. Keep the expensive model for the hard cut.

Which local models people actually useLink to this section

The useful answer is not a model name. Local coding leaders change too often for a blog post to stay current. Download counts are also the wrong filter: they mostly reward chat and embedding defaults, not multi-file refactors.

Most public coding leaderboards are frontier-first, and many “open” filters still surface rack-sized open models. Open is not the same as runnable on your machine. Treat leaderboards as a shortlist generator:

  1. Start from an open shortlist that lands filtered: Arena Coding, open source for human coding preference, or the LLM Stats Open LLM Leaderboard for open weights with coding / SWE-bench columns. Prefer multi-file / agentic evidence over single-function toy scores.
  2. Discard anything that needs multi-GPU or hundreds of GB.
  3. Look up the survivors in Ollama: do they have a first-party tag and agent launch support, and is the disk size inside roughly one third to half of your unified memory? On a 48GB Mac that is about 15 to 25GB. Leave the rest for IDE, browser, and Docker.
  4. Try one or two on a messy task from your own repo for an hour. Keep the one with fewer retries. Re-check every few months.

Arena and LLM Stats tell you which open families are currently worth trying. Ollama is just the install catalog for size and wiring. Your repo tells you which one survives. A general chat model you already downloaded is fine for summaries. It is rarely your best coding model.

Hybrid is the point: frontier models where judgment leverage is real, local models for the cheaper loop around them, and the same trust stack either way. Crypto, checkout, and permission edges still deserve close reading. As a default quality model, “I stared at the diff” does not.

Code is cheaper now. Trust is not.

Split for reviewability. Put protocol trust in gates and packages.

Spend human attention where machines cannot replace it.

Useful tokens. Local exits.

Trust around the model. Never depend on it.

Comments 💬 & Reactions ✨