Laravel Development in Morocco

Laravel is my primary backend framework. I use it for multi-tenant SaaS, billing, permissions, queues, and domain-heavy business software.

The work is not “CRUD in a weekend”. It is modeling the domain, isolating tenants, keeping jobs reliable, and writing APIs that other teams can depend on.

Multi-tenancy usually means row-level scoping enforced at the query layer, not just a tenant_id column that a forgotten WHERE clause can leak past. Queues run on Redis so a slow job — an export, an email batch, a webhook retry — does not block the request that triggered it, and failed jobs stay visible instead of silently dropped.

Billing and permissions get modeled together from the start: a plan defines what a role can do, and subscription events from Stripe keep that mapping current as a tenant upgrades, downgrades or churns. AnyWare ERP and BROC ServiceOS both run this way — Laravel underneath, PostgreSQL for the data, Redis for the jobs that have to be reliable.

I take over existing Laravel codebases about as often as I start new ones. Most of that work is unwinding queries that bypass tenant scoping, jobs with no retry strategy, or permission checks scattered across controllers instead of centralized — the kind of debt that looks fine until the second customer signs up.

Business problems

  • CRUD that cannot express tenancy, billing or permissions, so every new requirement gets bolted on as a special case instead of fitting a model that was designed to hold it.
  • Queued jobs and caches that leak across customers, because a cache key or a job payload was built without tenant scoping in mind from the start.
  • REST APIs that frontend teams cannot depend on, because a response shape changes without a version bump and nobody outside the backend team finds out until something breaks.
  • A Laravel app where every controller talks directly to Eloquent models with no service layer, so the same business rule gets copy-pasted into three controllers and only two of them get updated when it changes.
  • Feature flags and configuration hard-coded per environment instead of driven by the database, so enabling something for one tenant means a deploy instead of a toggle.
  • N+1 queries shipped because Eloquent makes it easy to lazy-load a relationship inside a loop without noticing, until a page that worked fine with ten rows times out with ten thousand.
  • Migrations that have never been tested rolling back, so a bad deploy cannot actually be undone cleanly and the fix becomes a manual database repair under pressure.

Development process

  1. Model the domain in PHP: tenants, auth, queues, PostgreSQL, Redis — the tenant boundary enforced at the query layer, not left to each controller to remember.
  2. Ship versioned REST APIs, then the admin or product UI that consumes them, so the contract is fixed before the interface that depends on it gets built.
  3. Cover queue failures and retries in the same pass as the happy path, because a job that silently drops on its first failure is worse than no job at all.
  4. Use Laravel's queue and event system deliberately — jobs for anything slow or external, events for anything another part of the system needs to react to — instead of stuffing it all into a controller method.
  5. Write feature tests against the API layer, not just unit tests against isolated classes, so a passing test suite actually means the product works end to end.
  6. Eager-load relationships deliberately and check query counts on pages that list anything, so an N+1 problem is caught in development instead of discovered when real data arrives.
  7. Test that migrations roll back cleanly, not just that they run forward, so a bad deploy has an actual undo instead of a manual repair.

What you get

  • SaaS, ERP and business applications that stay maintainable after the second customer, when the assumptions made for one tenant start meeting a second one that does things differently.
  • Architecture from Tangier that European teams can read and extend, because the domain vocabulary in the code matches the vocabulary the business actually uses.
  • A backend that other developers — mine or the client's own hires — can onboard onto in days, not weeks, because the domain vocabulary in the code matches the business.
  • Fewer 2am pages, because queue failures, tenant leaks and permission bugs were designed against instead of discovered in production.
  • Pages that stay fast as the dataset grows, because query patterns were checked against realistic volumes, not just a seeded dev database.
  • A real rollback path when a deploy goes wrong, instead of a manual database repair done under pressure.

Selected projects

FAQ

Is Laravel suitable for SaaS?

Yes, when tenancy, queues, billing and permissions are designed from the start. I build Laravel SaaS that way by default.

Do you build REST APIs with Laravel?

Yes. Versioned REST APIs with authentication, rate limits, and documentation that frontend and mobile teams can actually use.

Let's Build Your Project →