Skip to main content
Laravel 5 min read 5 views

Laravel Best Practices I Wish I Knew 3 Years Ago

After 3+ years shipping Laravel apps, these are the 10 practices I wish I’d learned sooner—project domains, DI, strict validation, relational DB design, cache invalidation, security headers, profiling, a testing culture, and environment hygiene. Real code, gotchas, and a copy‑paste checklist.

H

Hardik Kanajariya

Oct 6, 2025

Share:

Summary

Laravel Best Practices I Wish I Knew 3 Years AgoBy Hardik Kanajariya — full‑stack dev who learned a lot the hard way so you don’t have to.A quick hello 👋When I started with Laravel, I built shippable stuff fast… and created future headaches even faster. After 3+ years in the trenches (client apps, side projects, and refactors I’m still emotionally processing), here are the practices I wish I knew on day one. If you’re a beginner or intermediate dev, this...

Laravel Best Practices I Wish I Knew 3 Years Ago

Laravel Best Practices I Wish I Knew 3 Years Ago

By Hardik Kanajariya — full‑stack dev who learned a lot the hard way so you don’t have to.

A quick hello 👋

When I started with Laravel, I built shippable stuff fast… and created future headaches even faster. After 3+ years in the trenches (client apps, side projects, and refactors I’m still emotionally processing), here are the practices I wish I knew on day one. If you’re a beginner or intermediate dev, this is the blueprint I’d hand to my younger self.


1) Design your project by domains, not folders

Flat Controllers/Models/Requests directories scale like spaghetti. Group features by bounded context: Auth, Billing, Catalog, Checkout, etc. This keeps code co-located and reviewable.

Structure

app/
  Domain/
    Catalog/
      Actions/
      DTOs/
      Events/
      Http/
        Controllers/
        Requests/
        Resources/
      Models/
      Policies/
      Queries/
      Services/
      routes.php

routes/web.php

// routes/web.php

app/Domain/Catalog/routes.php

use

Why it slaps: Onboarding is faster, coupling is lower, and features read like chapters not scattered sentences.

Common pitfalls

  • Dumping everything into Services/ as a junk drawer.

  • Sharing Models across domains without anti-corruption layers.


2) Embrace the Service Container & Dependency Injection

Stop new-ing classes in controllers. Ask the container for dependencies and make code testable.

Controller with DI

namespace

Binding interfaces

// app/Providers/AppServiceProvider.php

Pro tip: prefer bind() for per-resolve, singleton() for a shared instance, and scoped() for per-request.

Smells to avoid

  • new StripeGateway(...) inside controllers (hard to swap/test).

  • Static facades everywhere — use them sparingly in app code, lean on DI.


3) Form Requests are your first firewall

Validate and authorize at the edge. Keep controllers skinny.

Form Request

// app/Domain/Catalog/Http/Requests/StoreProductRequest.php

Controller

public

Bonus: Return JsonResponse on validation failure automatically for API routes.

Mistakes

  • Validating in controllers or models. Duplication guaranteed.

  • Not using bail to short-circuit noisy errors.


4) Model your database like a grown-up

Design tables with purpose. Normalize first, denormalize later for read performance.

Migrations

Schema::create(

Relationships

class

Indexes matter

$table->index([

Avoid

  • Storing arrays/JSON for relational data you filter on (hard to index).

  • Nullable FKs everywhere — model real-world rules with constraints.


5) Smart caching: cache truth, not lies

Use cache as a read accelerator, not a source of truth.

Query caching with tags

use

Invalidate on write

public

Response caching (API)

// Example with spatie/laravel-responsecache (see packages)

Gotchas

  • Never cache per-user data without keys that include the user ID.

  • Don’t forget to set a driver like Redis in production.


6) Security must‑dos: treat prod like a crime scene

Checklist vibes but mandatory.

Basics baked-in

  • CSRF on POST/PUT/PATCH/DELETE (Blade @csrf or csrf_token()).

  • Mass assignment: use $fillable or guarded DTOs.

  • Always authorize: policies or gates — not if/else in controllers.

Examples

// Policy

Headers/Middleware

// app/Http/Kernel.php

.env hygiene

  • Never commit .env or .env.*.

  • Rotate keys (php artisan key:generate) if leaked.

Avoid

  • Building your own auth. Use Laravel Breeze/Fortify/Passport/Sanctum.

  • Storing secrets in config files — use env or Vault.


7) Performance tuning: make it fast by default

Start with visibility, then fix the hotspots.

Eager load

// Bad: N+1

Chunk & queue

Order::where(

Indexes & EXPLAIN

EXPLAIN

Config & route cache

php artisan config:cache
php artisan route:cache
php artisan view:cache

Octane (when it fits)

  • If your app is I/O heavy and stateless, Laravel Octane with Swoole/RoadRunner is a W.

Avoid

  • Micro-optimizing PHP before fixing queries.

  • Caching broken queries — you’ll just serve bad data faster.


8) Build a testing culture, not just tests

Aim for confidence: key flows, domain services, policies, and edge cases.

Pest example (my go-to)

// tests/Feature/CreateProductTest.php

Fakes and spies

Storage::fake(

Run it

php artisan 

Avoid

  • Only testing controllers. Your core logic should live in services/actions and be unit-testable.

  • Brittle snapshot tests for HTML — prefer feature tests that assert behavior.


9) Environment config hygiene

Keep secrets and settings sane across dev/stage/prod.

.env.example

APP_NAME=

Per‑env config

// config/services.php

Secrets

  • Use environment variables in CI/CD. For Docker/Kubernetes, mount secrets, don’t bake them into images.

  • Prefer parameter stores (AWS SSM/Secrets Manager, Vault) for production.

Avoid

  • Mixing config logic into .env (keep .env dumb; logic lives in config/*).


10) Bonus: API resources & DTOs for clean boundaries

Don’t return Eloquent models raw. Shape your output.

API Resource

// app/Domain/Catalog/Http/Resources/ProductResource.php

DTO (Spatie data)

// Using spatie/laravel-data

  • spatie/laravel-permission — roles & permissions done right.

  • spatie/laravel-data — typed DTOs with validation & casting.

  • spatie/laravel-responsecache — cache entire responses.

  • laravel/pint — opinionated code style, zero bikeshedding.

  • barryvdh/laravel-debugbar — quick visibility in dev (never prod).

  • spatie/laravel-query-builder — filter/sort includes for APIs.

  • pestphp/pest — minimal, readable tests with great DX.

  • laravel/octane — long‑running workers for perf.

  • laravel/telescope — request/DB/job insights in non‑prod.


Common mistakes I see (and made 🙃)

  • Treating Eloquent models as god objects: fat models, anemic services. Split responsibilities.

  • Skipping database indexes. If it’s in a WHERE or JOIN, it probably deserves an index.

  • Pushing everything through observers/events without a domain story. Over‑architecting is a tax.

  • Forgetting queue failures & retries — configure failed_jobs table and retry logic.

  • Not measuring. Add metrics/logging before guessing.


Copy‑paste checklist

  • Features grouped by Domain/ with routes per domain

  • Dependencies injected; interfaces bound in providers

  • Form Requests for validation + authorization

  • Migrations with constraints, FKs, and indexes

  • Caching with tags; flush on writes

  • Policies/Gates enforced; CSRF, mass‑assignment, headers

  • N+1 killed; heavy jobs queued; configs cached

  • Tests (Pest) for services, policies, critical flows

  • .env.example complete; secrets in SSM/Secrets Manager/Vault

  • API Resources/DTOs for clean IO


Final words

Laravel makes it easy to ship. These practices make it easy to maintain. Start small: pick one domain, add a Form Request, bind an interface, write one Pest test. Momentum compounds.

If this saved you a future refactor, my work here is done. Now go delete that 500‑line controller. You know the one. 😉

Tags

#PHP #Laravel #Best Practices
H

Hardik Kanajariya

Full Stack Developer with expertise in Laravel, Vue.js, and mobile app development. Passionate about creating innovative solutions and sharing knowledge with the developer community.

Comments

Be the first to comment on this post!

Leave a Comment

Your comment will be reviewed before being published.

Related Articles

Continue reading with these related posts

Stay Updated

Get More Articles Like This

Subscribe to my newsletter and get the latest articles, tutorials, and insights delivered directly to your inbox. No spam, just quality content.

Join 500+ developers who get weekly updates. Unsubscribe anytime.

Ready to Start Your Project?

Let's bring your vision to life with a custom solution that drives results and exceeds expectations.

Fast Delivery
Quality Assured
24/7 Support

Trusted by 6+ clients worldwide

5.0 Rating
100% Success Rate
24h Response