Back to Blog

Where AI Agents Perform: Laravel vs WordPress vs Astro

Same AI agents, three stacks, different results. What matters is how much of the system is visible from the repo and how cheap verification is - not training data.

Jakub Czechowski

Builds websites and e-commerce at JC Web Studio, runs StackCompass – a publication on content architecture and stack decisions – and co-organizes CMS Conf, a conference on content systems, as well as WordCamp.

/ / 12 min read

I work across three stacks: StackCompass.dev and my portfolio run on Astro, I write WordPress plugins and work with WooCommerce for clients, and I build backend apps in Laravel. Same agents, same models, same prompting habits - and clearly different results. On an Astro repo an agent can take a change from idea to a verified commit with almost no supervision. On Laravel it does well as long as I keep it inside the framework’s conventions. On WordPress it writes good code, and the site still behaves differently than that code promises - often with no relation to the quality of the plugin itself.

The obvious explanation would be training data. WordPress powers more of the web than the other two combined. There is more public WordPress code for models to learn from than Laravel and Astro together. If agent performance scaled with how much code a model had seen, WordPress would be the easiest stack to work with agents on. It isn’t. That reversal is the interesting part: it shows what the quality of that work on a given stack actually depends on.

What an agent needs is not what a developer needs

A human working an unfamiliar system constantly fills in the gaps: they click through the admin, check staging, remember that this client has a caching plugin, ask a colleague. An agent has none of that. It has the repository, the tools you give it, and whatever it can verify by running commands.

Stack popularity still matters. A model usually guesses syntax, idioms, and common patterns better where it has seen more code - which is why TypeScript in Astro, and PHP in WordPress and Laravel, give it an easier first pass than a niche environment. That mainly helps the first proposed solution. In agent work, and especially in review, something else matters more: whether the agent can lean on a deterministic signal instead of a feeling that the code looks right because it matches a pattern.

Once the model is past the threshold of syntactic familiarity, performance hangs on two questions:

How much of the system’s truth lives in the repo? If the repository is the whole system, the agent’s world model is complete. Every piece of behavior that lives elsewhere - a database row, an options table, an admin toggle, another plugin’s hook - is a dead zone the agent does not know exists.

How cheap is verification? An agent that can run a build, a type check, or a test suite gets a feedback loop: propose, check, correct. The form of the signal matters as much as its existence. Astro and Laravel ship deterministic review tools with the stack - Astro’s build and type check, Laravel’s test runner - and each answers with an exit code: 0 or 1. Pass or fail, no interpretation, no mood. That binary verdict is what an agent can act on on its own. An agent whose only check is “a human loads the page and looks” is producing plausible code, not verified code. The gap compounds at every step of a multi-step task.

Put the three stacks on those two axes and the results I see day to day stop being surprising.

Astro: the repo is the whole system

On a static Astro site, the repository is the system, not a description of it. Content lives in Markdown files with schemas enforced by content collections. Routing is the file tree. There is no database, no admin state, no runtime configuration that could drift from what the agent reads. When an agent searches this codebase, it sees everything there is to see.

Verification is equally closed. astro build validates content schemas, resolves every route, and fails on a broken frontmatter field or a missing import; astro check adds the TypeScript pass. For an agent, that set of commands answers most of “did I break something?” - in seconds, without a deploy. The propose-build-correct loop stays entirely inside the agent’s reach.

That is why the workflow on this site has drifted so far toward delegation. An agent here does not just write code. It publishes content, restructures components, and maintains machine-readable docs of the module graph. None of that required a smarter agent. It required a substrate where nothing that matters to the project is hidden.

The honest caveat: the build verifies structure, not appearance. An agent can pass every check and still ship a visually broken card, because CSS regressions do not fail builds. That is not an Astro gap. It is a dead zone all three stacks share. No build, type check, or test suite checks how the page looks. Visual checks need a human eye or separate browser-and-screenshot tooling, and that investment is independent of the stack.

In Astro this profile holds only while the site stays static. Add server-side rendering, sessions, and a database, and Astro starts inheriting the visibility problems of an application stack - closer to Laravel than to WordPress. Schema and policies in the repo still give you a contract; there is no global hook system where someone else’s plugin changes the behavior of your code. That is still not static Astro. It is also not WordPress.

Laravel: conventions as a contract

Laravel sits in the middle because it is predictable enough for an agent to work on, and application-like enough that some of the truth still lives outside the files. The framework’s conventions - where controllers live, how migrations describe schema, how policies express authorization, how jobs and schedules are declared - function as a contract. Not because the model “knows Laravel from training”, but because the contract is visible in the file tree. An agent that reads it knows what shape of change to look for before it understands the domain. In the ledger application I wrote about earlier, a request for an admin-only action means a route, a controller, a policy, and a test - and it is obvious where to look for each.

The important part: the database schema lives in the repo as migrations. The data layer, which in many apps is invisible, is declared in versioned code. Add php artisan test and you get a verification loop stronger than a build: the agent can write a feature test, run it, and iterate on assertions about behavior, not just on whether the PHP parses. The suite acts as a built-in, deterministic reviewer - it returns 0 or 1. That loop is conditional, though. Where tests are missing, Laravel loses this advantage.

The dead zone is real, but limited. Eloquent’s runtime magic - dynamic attributes, facades, implicit relationships - means the model itself does not expose the schema, and an agent can confidently reach for a column whose rename sits in a two-year-old migration nobody told it to read. Environment values, and whether a queue worker is even running, live outside the repo; the job code does not. Those gaps are known, and a test will often catch them - if the test exists. That is the real split in the Laravel world: on a well-tested repo agents are strong; on an untested one they lose the feedback loop and slide into guesswork.

WordPress: good code, uncertain result

Agents write WordPress code very well. Hooks, filters, WP_Query, block registration, REST endpoints - the volume of WordPress in training data makes the syntax and idiom effortless. Ask for a standalone plugin with a settings page and background jobs, and the code that comes back is often usable immediately.

The failure sits elsewhere: in WordPress the repository is a minority share of the truth. Site behavior is co-authored by the options table, post meta, active plugins and their versions, theme overrides, user roles, and configuration that lives in the admin - none of which the agent will see by reading its own plugin files. The code it writes runs inside a runtime assembled from parts it has never observed. That is the classic failure: the plugin is correct, and the site is still broken, because a cache layer, a security plugin, or another hook on the same filter changed behavior at runtime.

A concrete case from a client’s WooCommerce shop. The task was trivial: stop showing product categories on the listing. The agent did the “clean” thing - it did not hide the markup with display: none in CSS, it hooked at the source, on the category data. I approved that choice, because it looked cleaner to me too. Categories disappeared. So did the per-category B2B discounts, silently. Pricing in that shop runs on a B2B plugin whose rules are pinned to product categories. Rule matching reads the same terms the agent’s hook had just filtered out. Nothing threw. No test the agent could run had a way to catch it: the dependency did not live in its repo, only in another vendor’s plugin and in discount rules in the database.

The lesson is a reversal. By the standards of a single codebase the hook was the elegant fix and the CSS rule was the hack. In a composed runtime that hack was the safe option, precisely because it was shallow: CSS changes what you see, a hook changes shared infrastructure that an unknown number of other plugins stand on. An agent reasons with the aesthetics of the code it can see, not with the dependencies between plugins.

Testing does not save you, and it is worth saying why. The common diagnosis is “WordPress has no native testing tools”. That is nonsense. The tools exist - PHPUnit with the WordPress test suite, wp-env, Playground, @wordpress/scripts for blocks. By default, though, they test a different boundary than the one you need. You can test your plugin in isolation and pass, and the site still breaks, because the failure lives in the interaction: this set of active plugins, at these versions, with this theme, this options table, this data. The unit you can test is a plugin; the unit you deploy is the whole dependency system. It is assembled per site, in the database (active_plugins, options) and at runtime, through a global hook system where any plugin can change any other’s behavior.

There is no default artifact called “this site” to write a deterministic test against. You can assemble one per project from pinned versions, configuration, and a database dump, but that is bespoke work core will not standardize: not ecosystem neglect, a consequence of the distribution model. Separate authors, separate repositories, assembly at the customer’s. Modern block development does not escape this either. @wordpress/scripts brings a real build, TypeScript, and unit tests - a genuine 0-or-1 signal, but for the component. The dependency system stays untested. So the review verdict is a human looking at a rendered page. Without extra tooling, the agent’s loop ends at “the PHP parses”.

The gap can be narrowed with three different levers. WP-CLI is the largest, because it queries the assembled runtime - the only place the truth actually lives. wp option get, wp plugin list, wp db query turn invisible state into command output, which is the interface agents are good at. PHPStan with WordPress stubs restores a static signal the platform does not give you natively. WordPress Playground gives a disposable runtime for actually executing the result - but only if you put the same composition into it, not a blank WordPress. With that tooling in place, agent performance on WordPress improves a lot. At that point, though, you are assembling by hand what Astro gives you by default, and what Laravel gives you when tests exist.

Ranking: visibility and verification

Laid out this way, the comparison stops being about which framework agents “know”. Astro leads not because they know it best - they know it least of the three - but because a static site keeps structure in files and makes verifying that structure cheap and simple. Laravel sits in the middle because the contract is visible in the file tree, and the test loop is strong when it exists. WordPress trails not because agents write bad code - they write excellent code - but because the platform scatters its truth across a database and a UI, which an agent cannot reach from the repo alone.

The pattern generalizes: agent performance depends on the fraction of system truth that is legible from the repo, and on how cheap the verification loop is. Training-data volume is secondary. That pair predicts behavior on stacks I am not comparing here: more truth in files and more verification in commands improves delegation whatever sits underneath.

One honest qualifier: the ranking describes the defaults of each stack, and working discipline can reorder the middle. Laravel without tests loses its verification loop and sits closer to WordPress than its reputation suggests. A WordPress project with WP-CLI, static analysis, and a disposable runtime rises clearly above the standard WordPress development experience. What no amount of discipline changes is the starting position, and how much work it takes to move from it.

Should agent-friendliness change what you pick?

Not as the first criterion. It is tempting to end with “so choose Astro”. That is the wrong conclusion. The warehouse app is in Laravel because the problem is transactional. No amount of agent ergonomics changes that. Client shops run WooCommerce because the number of e-commerce integrations and the simplicity of editing are real value there, not nostalgia. Problem fit still dominates. A stack that agents love but that fights your solution costs more than delegating tasks to an agent will ever return.

Something else changed: when two stacks fit the problem, how easy it is to work with agents is already an important point in the decision, because it changes the cost of upkeep. On one stack, a meaningful share of routine changes can be delegated with cheap verification. On the other, a human stays in the loop by default, because the runtime is poorly visible from the repo and a cheap signal from tests still has to be assembled.

And when the stack is already chosen - which is most of the time - the same analysis tells you where to invest: in a coherent repo with documentation and tests, not in better prompts and elaborate skills. Build the missing axis: WP-CLI, static analysis, a disposable runtime of the same site, tests, state pulled out of the database and into files. Agents do not need the stack you would have picked for them. They need to see what they are doing, and to check whether what they did actually worked.