The decisions behind a new Laravel codebase
Model the domain before the screens
We start with a session where you explain the business in your own words and we write down the nouns, the states and the rules. An order can be draft, confirmed, shipped or cancelled. A cancelled order cannot be shipped. Only a manager can cancel after confirmation. That list becomes Eloquent models, PHP enums for states and migrations with real foreign keys and unique indexes. Constraints in the database catch bugs that application code misses, and they survive every future developer.
Migrations are the history of your schema
Every schema change is a migration file in the repository, applied in order on every environment. We never edit a migration that has run in production, and we write new ones so they are safe while the previous release is still serving requests: add a nullable column first, backfill it in a job, then tighten it in a later release. Seeders and model factories produce a believable dataset, so a new developer has a working copy within the hour.
Where the logic lives
Controllers stay thin. A form request validates the input, a policy answers whether this user may act on this record, and an action class does the work inside a database transaction. The same action can then be run from a web request, an API endpoint, a scheduled command or a queued job. We do not add repositories, buses or extra layers for their own sake. The test is simple: can a Laravel developer who has never seen the project find the code for "approve invoice" in under a minute?
Queues from the first sprint
Anything that talks to another system or takes longer than a moment becomes a job. Jobs are written to be idempotent, because a retry will happen sooner or later, and those that depend on new records are dispatched after the transaction commits. Failed jobs are recorded and reported. Adding this late means untangling side effects from requests, so it goes in early.
Tests, CI and the road to production
Each feature arrives with feature tests that exercise the HTTP layer, the policy and the database together. The pipeline runs them with Laravel Pint and static analysis on every pull request, and nothing merges red. Hosting is your choice. A server provisioned through Forge suits most applications, Vapor fits spiky workloads that benefit from serverless, and containers make sense when your team already runs them. Whatever you pick, the release is a script, and staging is built from the same script as production.