TL;DR
Pasting Laravel files into a chatbot leaves out what the framework decides elsewhere: middleware, policies, model scopes, listeners and the database itself. Give the agent the application's own account first, through Artisan's inspection commands or Laravel Boost, plus your recorded conventions. Then brief the feature around the existing permission and data rules, have it write the forbidden-user and persistence tests before the code, and review the route, policy and migration diff. Your normal review then judges evidence, not a chat transcript.
What a pasted controller leaves out
A Laravel controller rarely shows the whole behaviour of the request it handles. The middleware that runs is decided in the route files and middleware groups. Who may act is decided by a policy that Laravel finds by naming convention. Which rows a query returns can depend on a global scope declared on the model, and what happens after a save can sit in an observer or an event listener. Paste the controller into a chatbot and none of that travels with it, so the model fills the gaps with a typical Laravel application, not yours.
In August 2025 a long-time Laravel developer asked on Laracasts whether experienced developers had changed their habits; they still wrote code by hand and only sometimes asked a chatbot. Replies were wary: one doubted how many business rules would come out wrong, another said every project has specifics the assistant can lose. In April 2026 a developer put it more bluntly on Hacker News: agents worked well on a new repository and badly on legacy ones.
Both point at the same gap: an established Laravel application holds years of decisions that no single file shows. Four steps close it: inventory, brief, tests and evidence.
Let the application describe itself first
Before the agent plans anything, have it collect the application's own account of the area the feature touches. Laravel 13 (13.34.0 is the current release, from 29 September 2026) ships the commands for most of it; on an older application, check php artisan --version and use the documentation for that version:
Comparison
| What the agent needs | Where it comes from |
|---|---|
| Routes, their names and middleware | php artisan route:list -v, or -vv to expand middleware groups (routing) |
| A model's attributes and relationships | php artisan model:show Project (Eloquent) |
| Tables, columns and the database engine | php artisan db:show and php artisan db:table projects |
| Listeners that run after an event | php artisan event:list |
| Who may do what | The policies in app/Policies, Gate definitions in service providers and the authorize methods of form requests |
| Work that happens later | The jobs and notifications the area dispatches; search for the dispatch calls |
| How this team writes code | The existing tests for the area, and any recorded project rules |
Laravel Boost, Laravel's first-party package for coding agents (2.10.1 on Packagist, released 1 October 2026), puts much of this behind an MCP server. Its tools read the PHP, Laravel and package versions, the database schema, recent log entries and the last error, and its documentation search answers from documentation matched to the packages you have installed (Laravel Boost). It also has a Database Query tool that runs queries against the configured connection, so point the agent at a local or seeded database, never at production.
The part no command prints is your own convention. Boost's project rules are Markdown files in .ai/rules, each scoped to file globs and committed with the code, with an index that agents are told to read before planning or editing. For an existing application, its infer-conventions skill sweeps the code for non-default conventions in validation, controllers, authorization, models, testing and other areas, and presents each one with its evidence for your approval before recording it. Approve them one at a time; telling the skill "yolo" records everything unasked.
Without Boost, the same inventory works as a read-only first prompt: run those commands, list what the feature will touch, and stop. The article on starting a coding agent on an existing codebase safely covers that first read-only session in general terms.

Brief the feature around the rules it must not change
A common way a new feature damages an established application is a new path to existing data: a fresh query that skips a scope, or a permission check written in a controller instead of the policy. Write the brief so the agent has to reuse the paths that already exist. As an illustration, not a real project: an application has projects with an owner and members, and the feature lets the owner archive a project.
- Name the ability and where the rule lives. The rule is a new
archivemethod on the existingProjectPolicy, true only for the owner. The controller may invoke that check but must not re-implement it, so no ownership comparison in the controller body, and no edits toGate::before, whose closure decides every check in the application whenever it returns a non-null result (authorization). - Invoke it the way the neighbours do. The
can:archive,projectroute middleware, the#[Authorize]controller attribute, or aGate::authorize('archive', $project)call inside the controller method. Each asks the policy, and a denial becomes a 403. Pick whichever the existing controllers already use. - Keep the data change additive. One new migration adding a nullable
archived_atcolumn. No edits to existing migrations, columns, foreign keys or cascade rules. - Say which queries change and which stay. Archived projects leave the members' project list; reports and exports keep them unless you decide otherwise. If the model has a global scope, decide deliberately whether archiving belongs in it, because a global scope changes every query on that model.
- List the files it may touch. The policy, one controller method, one route, the migration, the model, the list query and the tests. Anything else needs a reason in the plan.
Ask for the plan before any code and check it against the inventory: every route it adds should carry the same middleware as its neighbours, and every query it changes should be one the brief named.

Make the forbidden case the first test
Laravel's test setup already supports this order. phpunit.xml sets the environment to testing, an optional .env.testing replaces .env while tests run, and the RefreshDatabase trait resets the database between tests (testing, database testing). Sail creates a separate testing database by default, and sail test runs the same thing as sail artisan test (Sail). Give the agent the exact command your project uses, then have it write tests like these, in whichever of Pest or PHPUnit the suite already uses:
it('lets the owner archive a project', function () {
$project = Project::factory()->create();
$this->actingAs($project->owner)
->patch(route('projects.archive', $project))
->assertRedirect();
expect($project->fresh()->archived_at)->not->toBeNull();
});
it('does not let a member archive the project', function () {
$project = Project::factory()->create();
$member = User::factory()->create();
$project->members()->attach($member);
$this->actingAs($member)
->patch(route('projects.archive', $project))
->assertForbidden();
expect($project->fresh()->archived_at)->toBeNull();
});
The example is illustrative and was not run; it assumes the application's factories and its owner and members relationships. The second test matters more: a controller that forgot the policy still passes the owner test, and only the member test shows both the 403 and that nothing was written. Add a guest case and one existing test of the project list. Before the implementation, with the route and an empty controller method in place, the list test should pass and the owner and member tests should fail on their assertions; the guest case may already pass through existing auth middleware. A test failing because projects.archive does not exist says nothing about authorization. The article on getting an agent to follow a real test-first workflow explains how to hold it to that order.
Fake the side effects, for example Queue::fake() and an assertion that the job was pushed, rather than running a real queue. And if your local setup wraps commands in Sail, Lando or another tool, say so in the shared instructions. A Laracasts user working on Laravel 13 with Lando wrote a guideline so their agent would use Lando's commands, then found it would not suit a colleague without Lando, and asked how to keep it local.
Evidence your delivery process can accept
A change is ready for normal review when it brings evidence a reviewer can check without the chat:
- The test run itself. The command, the result for the whole suite, and the new member test failing on its assertion before the implementation and passing after it.
- The route listing before and after. Diff the
route:list -voutput. The only difference should be the new route, carrying the same middleware as its neighbours. - The migration on a realistic schema. Apply it to a database at your current schema, not only to the empty one the tests build, and roll it back if your team relies on rollbacks.
- A sentence for every hidden-behaviour file in the diff. Policies,
Gatedefinitions, middleware, global scopes, observers, model events, mass-assignment settings and service providers. If the change touches one, the pull request says why. - The checks the project already runs, such as Pint, static analysis or
composer audit.
Paqad can carry part of this. Its built-in Laravel pack in release 1.93.0 recognises a Laravel application from laravel/framework in composer.json or an artisan file, declares php artisan test as the test command with JUnit XML output its checks can read, and gives agents Laravel testing rules that ask for a forbidden user's 403 and an assertion that no row was written. It does not write your tests or make any model better at Laravel, and its pre-edit checks run only on Claude Code and Codex CLI. Paqad's built-in packs page lists what the Laravel pack declares.
Frequently Asked Questions
What is a good way to use AI to add features to an existing Laravel application?
Start with a feature that touches one policy and one table, and widen only after a reviewed change has landed. Laravel 13's AI SDK is a different thing: it is for building AI features into your application, not for having an agent change your code.
What should the agent learn about this application's routes, models, policies, jobs and conventions first?
Ask it to name the existing feature most like the new one and to describe how that feature is built: its route, policy, form request, test file and any job it dispatches. Copying a working sibling's shape is more reliable than any general Laravel convention. It should also find where this application registers middleware: bootstrap/app.php in applications created on Laravel 11 or later, often app/Http/Kernel.php in older applications upgraded without adopting the new structure, which Laravel's upgrade guide does not require.
How do I constrain the feature so existing authorization rules and data relationships remain intact?
Tell the agent to stop and ask, rather than edit, whenever it believes an existing policy method, relationship or migration must change. Then cover each existing role with a regression test that passes before the change, so a widened permission fails a test instead of reaching review unnoticed.
Which Laravel tests and environment setup should be part of the implementation workflow?
Run the full suite once before the agent starts and record the result, so failures that were already there are not blamed on the new code. Run persistence tests on the same database engine as production if you can; an in-memory SQLite database can accept queries and constraints that MySQL or PostgreSQL treat differently.
What review and verification evidence would make the change safe enough for our normal delivery process?
Put the evidence list into the pull request template, so every agent-assisted change brings the same items. Leave approval of any policy or migration change to someone who knows the domain, and judge the pasted outputs of the commands, not the agent's summary of them.
