<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Olusola Ojewunmi</title>
    <description>The latest articles on DEV Community by Olusola Ojewunmi (@ojsholly).</description>
    <link>https://dev.to/ojsholly</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F701639%2F314eb972-51c3-46e8-95cb-77bd28f1478b.png</url>
      <title>DEV Community: Olusola Ojewunmi</title>
      <link>https://dev.to/ojsholly</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ojsholly"/>
    <language>en</language>
    <item>
      <title>The Case Against Rewrites: How I Evolved a 5-Year-Old Backend Codebase (And Why You Probably Shouldn’t Rewrite Yours Either)</title>
      <dc:creator>Olusola Ojewunmi</dc:creator>
      <pubDate>Tue, 10 Mar 2026 10:02:00 +0000</pubDate>
      <link>https://dev.to/ojsholly/the-case-against-rewrites-how-i-evolved-a-5-year-old-backend-codebase-and-why-you-probably-25dl</link>
      <guid>https://dev.to/ojsholly/the-case-against-rewrites-how-i-evolved-a-5-year-old-backend-codebase-and-why-you-probably-25dl</guid>
      <description>&lt;p&gt;&lt;em&gt;A deep dive into modernising legacy systems incrementally without breaking production or losing years of edge-case history.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Most Legacy Systems Don't Fail Because of Bad Code. They Fail Because of Emotional Decisions.
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flxihvgno6oypexigaphv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flxihvgno6oypexigaphv.png" alt=" " width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's start with an uncomfortable truth: when your massive, tangled production backend starts slowing your team down, the code isn't your biggest enemy. Your ego is. And right behind your ego are the twin sirens of software engineering: the Rewrite Temptation and the Microservices Hype.&lt;/p&gt;

&lt;p&gt;When we stare down the barrel of a 5-year-old backend — one that has survived pivoting business models, changing teams, and feature-factory panic — the instinct is always to burn it down and start over. We want a clean slate. We want the architectural purity we didn't have the time or experience to build five years ago. We tell ourselves it will be faster because "this time, we know the domain." We think that handing the entire business logic over to a constellation of managed cloud services, serverless functions, and shiny new vendors will somehow absolve us from having to manage complexity.&lt;/p&gt;

&lt;p&gt;But here is the reality: &lt;strong&gt;production pressure doesn't stop just because engineering wants to refactor.&lt;/strong&gt; If you tie your modernisation strategy strictly to a completely new rewrite or deep vendor lock-in, you are essentially betting the business on an unproven abstraction while starving your current customers of new value.&lt;/p&gt;

&lt;p&gt;We didn't rewrite our 5-year-old backend. We evolved it. We deliberately avoided heavy vendor lock-in. We used AI agents not to write the code for us, but to act as archaeologists, dramatically accelerating our understanding of forgotten modules. We flirted heavily with microservices — and even paused the initiative because the organisational timing was undeniably wrong.&lt;/p&gt;

&lt;p&gt;This isn't theory. This is the battle-tested playbook for incrementally modernising a growing, breathing monolith — without breaking the clients that depend on it.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. What a 5-Year Backend Actually Looks Like
&lt;/h2&gt;

&lt;p&gt;If you've never inherited or maintained a system that has been running and evolving for half a decade, the concept of "technical debt" can sound sterile and abstract. In reality, a 5-year backend is less like a poorly built bridge and more like an old city built on top of its own ruins.&lt;/p&gt;

&lt;p&gt;When this system started half a decade ago, it was built by a small team operating with an early-career-level understanding. The goal was finding product-market fit, not establishing pristine domain-driven design.&lt;/p&gt;

&lt;p&gt;As a result, &lt;strong&gt;pattern drift is everywhere&lt;/strong&gt;. Look closely at the codebase, and you can see exactly when different paradigms were popular. You'll find early controllers that contain 800 lines of raw HTTP requests mingled with database queries and business logic. A year later, there's an attempt at the Repository Pattern — but it was half-abandoned, leaving a mix of raw ORM calls and over-engineered repository wrappers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Accidental complexity&lt;/strong&gt; runs rampant. In the critical paths — like order generation or payment processing — the coupling is terrifyingly tight. Fixing a bug in the notification dispatch system inexplicably breaks the user authentication flow because five years ago, someone needed the user's role and bypassed the boundary to fetch it directly from a deeply nested user relationship graph. There are models with 40 attributes and God Classes that serve as a dumping ground for anything tangentially related to a "User."&lt;/p&gt;

&lt;p&gt;The standards are inconsistent, but the system &lt;em&gt;works&lt;/em&gt;. It makes money. It serves users. You cannot disrespect the code, because the code got you here.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. The Rewrite Temptation
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh5gg5y32jh7iz8ioj7kb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh5gg5y32jh7iz8ioj7kb.png" alt=" " width="800" height="436"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the face of mixed abstraction levels and tightly coupled core domains, the call to rewrite the system from scratch is deafening. A rewrite feels clean. It promises an end to the spaghetti code, the legacy bugs, and the cognitive overhead of parsing a giant monolith.&lt;/p&gt;

&lt;p&gt;But a rewrite is dangerous, and quite frequently, it is a trap.&lt;/p&gt;

&lt;p&gt;The hidden opportunity cost of a rewrite is immense. For every month you spend rebuilding existing functionality in a "cleaner" way, your product stagnates. Competitors move forward. The business grows impatient. But the technical risks are even worse: regression risk and knowledge evaporation.&lt;/p&gt;

&lt;p&gt;That messy, 800-line function you loathe? It contains five years of hard-won edge-case handling. &lt;strong&gt;When you throw away the code, you throw away the business history written into it.&lt;/strong&gt; The correct safety net for a rewrite is not a high code coverage number — a test suite can achieve 100% line coverage and still miss critical edge cases entirely if its assertions are weak. Coverage measures &lt;em&gt;execution&lt;/em&gt;, not &lt;em&gt;correctness&lt;/em&gt;. What you actually need is a comprehensive suite of behaviour-level tests that capture the observable outputs of every critical path. Practically speaking, that suite doesn't exist in most legacy codebases, which means a rewrite almost certainly guarantees that you will reintroduce old bugs.&lt;/p&gt;

&lt;p&gt;Take a firm stance: unless the underlying technology stack is entirely deprecated, unsupported, or fundamentally incapable of scaling to projected usage, you must resist the rewrite. Evolve the system instead. Respect the edge cases.&lt;/p&gt;




&lt;h2&gt;
  
  
  4. The Microservices Attempt That Stalled
&lt;/h2&gt;

&lt;p&gt;We didn't just consider rewriting; we aggressively pursued decomposition into microservices. Splitting these domains into discrete services seemed like the obvious, "senior" thing to do.&lt;/p&gt;

&lt;p&gt;When the idea was first floated, I consulted a mentor with significantly more architectural experience. He explicitly warned against it: microservices weren't necessary for our scale, and we should simply embrace gradual refactoring over time. I didn't take that advice then — though I desperately wish I had.&lt;/p&gt;

&lt;p&gt;And then, we stopped.&lt;/p&gt;

&lt;p&gt;It wasn't a technical failure; it was a reality check. The key driver for the architecture change — yours truly — fell critically ill and took an extended leave of absence. Suddenly, the complex orchestration fell onto a team that was already trying to stay afloat. When I returned, feature backlog pressure from the business was crushing. I needed to ship value, not wire up Kubernetes clusters.&lt;/p&gt;

&lt;p&gt;We realised a critical lesson: &lt;strong&gt;Architecture must survive absence.&lt;/strong&gt; Microservices require organisational readiness and depth — your infrastructure maturity and team structure must match the architecture. Ours didn't yet.&lt;/p&gt;

&lt;p&gt;So we paused the distributed microservices dream. Instead, we shifted our strategy to a &lt;strong&gt;Modular Monolith&lt;/strong&gt;, as I was initially advised by my mentor. The goal was to set and improve our internal boundaries &lt;em&gt;inside&lt;/em&gt; the existing codebase first — and then extract services later, only if and when the team and infrastructure were ready.&lt;/p&gt;




&lt;h2&gt;
  
  
  5. The Incremental Modernisation Framework
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg5vr8ng68sy7ccdp5p01.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg5vr8ng68sy7ccdp5p01.png" alt=" " width="800" height="436"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This approach mirrors what Martin Fowler describes as the &lt;strong&gt;Strangler Fig pattern&lt;/strong&gt; — incrementally replacing legacy behaviour by building the new system around and alongside it, rather than in a single "big bang" replacement. The name comes from the strangler fig tree, which grows around a host tree over time until the host is fully replaced. Applied here, every modernised module is a new ring of growth around the legacy core, with the old code gradually hollowed out and replaced rather than demolished all at once.&lt;/p&gt;

&lt;p&gt;I adopted a highly tactical blueprint built around this philosophy. It is designed to be executed incrementally, meaning feature development never has to fully stop.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: System Mapping Before Touching Code
&lt;/h3&gt;

&lt;p&gt;Before you write a single line of refactoring, you must map the territory. We used dependency tracing to identify the deeply coupled domains — the modules where a change in one place sent shockwaves into three others.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: Behaviour Locking
&lt;/h3&gt;

&lt;p&gt;You cannot safely change what you cannot test. We introduced what Michael Feathers, in his landmark book &lt;em&gt;Working Effectively with Legacy Code&lt;/em&gt;, calls &lt;strong&gt;characterisation tests&lt;/strong&gt; — tests written not to verify correctness, but to document and lock the &lt;em&gt;current, actual behaviour&lt;/em&gt; of legacy code before you move it. We locked the behaviour so that as we started moving walls around, we would instantly know if we broke a load-bearing structure.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: Domain Isolation
&lt;/h3&gt;

&lt;p&gt;With tests in place, we began domain isolation. This didn't mean moving code to a new server; it meant moving it to a clear, isolated namespace within the monolith with strict boundaries. Concretely, this meant:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Namespacing by domain&lt;/strong&gt; (e.g., &lt;code&gt;App\Domains\Orders&lt;/code&gt;, &lt;code&gt;App\Domains\Payments&lt;/code&gt;) rather than by technical layer.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Banning cross-domain Eloquent relationships.&lt;/strong&gt; If the &lt;code&gt;Orders&lt;/code&gt; domain needed user data, it called a dedicated &lt;code&gt;UserReadService&lt;/code&gt; interface — it did not reach across and eager-load a &lt;code&gt;$order-&amp;gt;user-&amp;gt;wallet-&amp;gt;transactions&lt;/code&gt; chain. Violations were caught in code review.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Using internal domain events&lt;/strong&gt; to communicate state changes across module boundaries, laying the groundwork for potential future extraction without tight coupling.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To illustrate how this changed the code, here is how our &lt;code&gt;OrderController&lt;/code&gt; evolved.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Before (V1): The God Controller&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;store&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;CreateOrderRequest&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;JsonResponse&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$order&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;orderService&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;createOrder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Throwable&lt;/span&gt; &lt;span class="nv"&gt;$e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;response&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Error occurred while creating order'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Tight coupling to the Wallet/Ledger domain — reached directly across boundaries&lt;/span&gt;
        &lt;span class="nv"&gt;$charge&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;orderService&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;walletCharge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$order&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$order&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;price&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$order&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nv"&gt;$paymentComplete&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;data_get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$charge&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'status'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nv"&gt;$remainingDebit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;data_get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$charge&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'remaining_debit'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Throwable&lt;/span&gt; &lt;span class="nv"&gt;$e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;response&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Error occurred while charging wallet'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="c1"&gt;// ... matching payment channels and long response logic ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;After (V2): The Delegating Controller &amp;amp; Isolated Service&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;OrderController&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Controller&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;store&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;UnifiedCreateOrderRequest&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;JsonResponse&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// The controller's only job is HTTP input/output.&lt;/span&gt;
            &lt;span class="c1"&gt;// All business orchestration lives in the service layer.&lt;/span&gt;
            &lt;span class="nv"&gt;$result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;unifiedOrderCreationService&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;createOrder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;validated&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
                &lt;span class="nf"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;user&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;response&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;success&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
                &lt;span class="s1"&gt;'order'&lt;/span&gt;   &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;OrderResource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'order'&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt;
                &lt;span class="s1"&gt;'payment'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'payment'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
                &lt;span class="s1"&gt;'pricing'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'pricing'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="s1"&gt;'Order created successfully.'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;OrderCreationException&lt;/span&gt; &lt;span class="nv"&gt;$exception&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// Map domain exceptions to safe, user-facing messages.&lt;/span&gt;
            &lt;span class="c1"&gt;// Raw exception messages are never exposed to API consumers&lt;/span&gt;
            &lt;span class="c1"&gt;// to avoid leaking internal implementation details.&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;response&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Unable to process your order. Please try again.'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;422&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Throwable&lt;/span&gt; &lt;span class="nv"&gt;$exception&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nc"&gt;Log&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Unexpected order creation failure'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
                &lt;span class="s1"&gt;'user_id'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
                &lt;span class="s1"&gt;'error'&lt;/span&gt;   &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$exception&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getMessage&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
            &lt;span class="p"&gt;]);&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;response&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'An unexpected error occurred. Please contact support.'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The payment and pricing logic — previously reached by crossing domain boundaries directly in the controller — now lives entirely within &lt;code&gt;UnifiedOrderCreationService&lt;/code&gt;, which coordinates between &lt;code&gt;App\Domains\Orders&lt;/code&gt; and &lt;code&gt;App\Domains\Payments&lt;/code&gt; through their defined interfaces.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4: Database Evolution Strategy
&lt;/h3&gt;

&lt;p&gt;One of the most practically challenging aspects of this kind of modernisation is the schema itself. We handled this carefully across two modes:&lt;/p&gt;

&lt;p&gt;Where the existing table structure was close enough, we &lt;strong&gt;extended it with new columns via additive migrations&lt;/strong&gt; — never dropping or renaming existing columns before both V1 and V2 consumers were confirmed to be off the old fields. This expand-then-contract approach meant V1 and V2 endpoints could coexist on the same data without a risky simultaneous cutover.&lt;/p&gt;

&lt;p&gt;Where the old schema was fundamentally at odds with the new domain model, we &lt;strong&gt;created new tables and models entirely&lt;/strong&gt;, wrote dual-write logic during the transition period, and deprecated the old table once traffic had fully migrated. This was cleaner than trying to contort an existing structure and avoided the risk of a failed migration taking down V1 in the process.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 5: Opportunistic Refactoring
&lt;/h3&gt;

&lt;p&gt;We implemented the &lt;strong&gt;"Touch → Improve" rule&lt;/strong&gt;. We didn't carve out entire sprints for tech debt. Instead, if an engineer had to touch a massive God Class to add a feature, they were expected to leave it slightly better than they found it — extracting one method, adding one test, removing one dead branch.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 6: Versioned Endpoints Strategy
&lt;/h3&gt;

&lt;p&gt;To avoid breaking consumers — mobile apps, web clients, third-party integrations — we preserved all V1 endpoints exactly as they were. When a domain was fully isolated and tested under the new architecture, we introduced V2 endpoints with the clean interface. Consumers migrated on their own timelines.&lt;/p&gt;




&lt;h2&gt;
  
  
  6. Using AI as a Code Archaeologist
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuwke04lx72ttqfb0b4m7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuwke04lx72ttqfb0b4m7.png" alt=" " width="800" height="446"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;AI is not an architect. It will not magically refactor a coupled monolith into clean domains. What it provides is an unparalleled acceleration in the &lt;em&gt;comprehension&lt;/em&gt; phase — the part that usually takes senior engineers days of careful reading.&lt;/p&gt;

&lt;p&gt;I used AI as a &lt;strong&gt;Code Archaeologist&lt;/strong&gt;. When facing a 1,200-line controller handling dynamic pricing variations that no one had touched in three years, a human engineer would spend most of a day just mapping the variable states and tracing the execution paths. Instead, I would feed the code into an AI agent workflow — using &lt;strong&gt;Windsurf&lt;/strong&gt; and &lt;strong&gt;GitHub Copilot&lt;/strong&gt; for in-editor analysis, and constructing detailed, structured archaeological prompts in &lt;strong&gt;ChatGPT&lt;/strong&gt; and &lt;strong&gt;Gemini&lt;/strong&gt; — asking the agent to trace the data flow, identify unintentional duplication, and document the implicit business rules buried inside conditional logic. It surfaced hidden business rules and flagged dead variable paths in a fraction of the time a full human review would require.&lt;/p&gt;

&lt;p&gt;This heavily assisted my safe refactoring process. But I remained rigorous about validation. AI acts as an incredibly smart pair programmer who completely lacks institutional context. In one case, an agent confidently asserted that a variable was unused — missing an indirect call via PHP's dynamic reflection that would have caused a production failure if I had acted on it blindly. Every AI output was treated as a strong hypothesis to be verified, not a conclusion to be merged.&lt;/p&gt;

&lt;p&gt;The ultimate architectural judgment — what to abstract, what to leave alone, what the business actually needs — must remain in the hands of the senior engineer.&lt;/p&gt;




&lt;h2&gt;
  
  
  7. What the Git History Revealed
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpof6l0cpiuskhfawy7ah.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpof6l0cpiuskhfawy7ah.png" alt=" " width="800" height="446"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you want to understand the true state of your architecture, don't look at the codebase today. Look at the git history.&lt;/p&gt;

&lt;p&gt;In the &lt;strong&gt;Early Stage (Year 1)&lt;/strong&gt;, the commits were enormous: &lt;code&gt;"Added the custom response macros, needed libraries, exception handlers, models, and migrations."&lt;/code&gt; No boundaries. Entire domains were slammed into the monolith in single commits. The git log reads like a construction site — fast, functional, and completely undisciplined.&lt;/p&gt;

&lt;p&gt;In the &lt;strong&gt;Mature Stage (Recent Commits)&lt;/strong&gt;, the inflexion points are unmistakable: &lt;code&gt;"feat: implement ops authentication with role-based access control."&lt;/code&gt; Commit hygiene improved significantly. Class sizes stabilised. Domain-scoped changes stay within their namespaces. You can quantify the maturity of an engineering culture solely through log analysis — and what you find will either give you pride or a very clear improvement agenda.&lt;/p&gt;




&lt;h2&gt;
  
  
  8. Avoiding Vendor Lock-In
&lt;/h2&gt;

&lt;p&gt;One of the great modern traps is coupling your architecture to proprietary services. We deliberately kept business logic portable. We treated cloud providers as commodity compute, not as an extension of our domain logic. Queues, storage, and caching were accessed through abstraction layers rather than vendor-specific SDKs embedded throughout the codebase.&lt;/p&gt;

&lt;p&gt;Modernisation should &lt;em&gt;increase&lt;/em&gt; your portability, not decrease it. If switching your queue provider requires changes in 40 files, your modernisation has made you more fragile, not less.&lt;/p&gt;




&lt;h2&gt;
  
  
  9. Measurable Improvements
&lt;/h2&gt;

&lt;p&gt;The results were concrete and trackable:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Defect rate on order operations dropped significantly.&lt;/strong&gt; Once the new domain boundaries were locked in and characterisation tests were in place, the number of bugs originating from order-related operations dropped dramatically. The few issues that did surface were rapidly isolated — the clear boundaries meant the blast radius of any problem was immediately obvious, and fixes were targeted rather than speculative.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reduced cognitive load.&lt;/strong&gt; I went from being terrified to touch the pricing module to confidently building and shipping new enterprise pricing tiers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Faster onboarding.&lt;/strong&gt; Clearer domain boundaries mean a new engineer can understand a specific module without needing the full five-year context of the system.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Performance gains without rewrite sprints.&lt;/strong&gt; Opportunistic refactoring during feature work yielded significant performance improvements on critical APIs — without a single dedicated "performance sprint."&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  10. Hard Lessons
&lt;/h2&gt;

&lt;p&gt;I will be completely candid: not everything was a success.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Unnecessary refactors.&lt;/strong&gt; I cleaned up legacy corners that had a defect rate of zero and hadn't been modified in three years. I wasted cycles making "ugly" code look pretty. If it works and rarely changes, leave it alone. The Boy Scout Rule applies to code you &lt;em&gt;touch&lt;/em&gt; — not code you stumble past.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Premature abstractions.&lt;/strong&gt; I built generic interfaces for future data models that never materialised. Abstractions are liabilities until they are proven by actual use cases.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AI oversight.&lt;/strong&gt; The reflection call incident above was a real near-miss. AI output requires the same scrutiny as a pull request from a brilliant engineer who has never read the product requirements. Review it accordingly.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  11. Redefining "Modern"
&lt;/h2&gt;

&lt;p&gt;Modern does not equal rewrite.&lt;br&gt;
Modern does not equal distributed microservices.&lt;br&gt;
Modern does not equal the latest buzzword.&lt;/p&gt;

&lt;p&gt;Modern means &lt;strong&gt;Observability&lt;/strong&gt;, &lt;strong&gt;Replaceability&lt;/strong&gt;, and &lt;strong&gt;Clear Boundaries&lt;/strong&gt; — whether those exist across Kubernetes clusters or across namespaces in a well-structured PHP monolith.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Reality Check&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let me be completely clear: our evolved system is not a flawless utopia. We still encounter bugs. We still hit unexpected performance bottlenecks as the user base grows. Code still rots if left unattended.&lt;/p&gt;

&lt;p&gt;The profound difference today is not the &lt;em&gt;absence&lt;/em&gt; of problems — it's our &lt;em&gt;approach&lt;/em&gt; to solving them. When a critical bug surfaces now, we don't panic or threaten to nuke the entire module out of frustration. We write a failing test to lock the behaviour, isolate the boundary, and refactor the specific bottleneck. We have successfully replaced emotional, short-term reactions with disciplined, long-term engineering maturity.&lt;/p&gt;

&lt;p&gt;That, more than any architectural pattern or tooling choice, is what a successful modernisation actually looks like.&lt;/p&gt;




&lt;h2&gt;
  
  
  12. Discussion
&lt;/h2&gt;

&lt;p&gt;Evolution almost always beats ego-driven rewrites. But these questions don't have universal answers — they depend on team, scale, and context. I'd love to hear how other engineers navigate them:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;When is the &lt;em&gt;exact&lt;/em&gt; right moment to finally rewrite instead of evolve?&lt;/li&gt;
&lt;li&gt;When is an organisational structure truly ready for microservices?&lt;/li&gt;
&lt;li&gt;What does your git history say about your engineering team's growth?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Drop your experiences below. The hardest lessons are the ones we shouldn't have to learn in isolation.&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>discuss</category>
      <category>laravel</category>
      <category>ai</category>
      <category>refactorit</category>
    </item>
    <item>
      <title>How to Vibe Code Your MVP in Weeks Using AI-Assisted Development</title>
      <dc:creator>Olusola Ojewunmi</dc:creator>
      <pubDate>Wed, 21 Jan 2026 10:11:12 +0000</pubDate>
      <link>https://dev.to/ojsholly/how-to-vibe-code-your-mvp-in-weeks-using-ai-assisted-development-1b1l</link>
      <guid>https://dev.to/ojsholly/how-to-vibe-code-your-mvp-in-weeks-using-ai-assisted-development-1b1l</guid>
      <description>&lt;p&gt;We need to stop pretending that "doing it right" means taking three months to ship a CRUD app.&lt;/p&gt;

&lt;p&gt;In the startup world, over-engineering isn't a badge of honour. It is often just procrastination disguised as quality. We hide behind "best practices," clean architecture, and perfect test coverage because it feels safe. It feels professional. But while we are debating folder structures, the market is moving on.&lt;/p&gt;

&lt;p&gt;If you are treating your pre-revenue MVP like it’s a legacy enterprise system, you aren't being thorough—you are bleeding opportunity.&lt;/p&gt;

&lt;p&gt;I recently shipped a production-ready Laravel MVP in about 45 hours of active coding time, all while working a full-time job as a Senior Engineering Lead. No sabbatical. No 40-hour blocks. Just nights, weekends, and an AI-assisted workflow that turns "stolen moments" into shipped features.&lt;/p&gt;

&lt;p&gt;This isn't a guide on how to be lazy. It’s a guide on how to stop hiding.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Call That Started It All
&lt;/h2&gt;

&lt;p&gt;It was a regular Tuesday afternoon when my screen lit up with a Google Meet invite from the CEO. The CTO was already in the call when I joined. No agenda, no pre-read—just an invite titled "Quick Sync."&lt;/p&gt;

&lt;p&gt;The CEO cut straight to it. He recounted something he'd heard about recently—a frustrating experience that had surfaced at a place he frequents. Someone close to the situation had dealt with a friction point that, frankly, shouldn't exist in 2025. The specifics don't matter here, but what struck me was the immediacy. This wasn't abstract product ideation. This was a real problem, freshly articulated, with tangible urgency behind it.&lt;/p&gt;

&lt;p&gt;And then the constraint that reframes everything: &lt;em&gt;a prospective first client was already in motion&lt;/em&gt;. They faced the same friction, the same gap. The window to deliver something meaningful—while the pain was still visceral—was narrow.&lt;/p&gt;

&lt;p&gt;The call ended with a direct question: "How fast can we move on this?"&lt;/p&gt;

&lt;p&gt;I gave an honest answer: "A couple of weeks, if we stay focused."&lt;/p&gt;

&lt;p&gt;But there was a catch. I wasn't doing this with a clear calendar. I work full-time as a Senior Backend Developer and Engineering Team Lead, and I have my own personal projects in flight. I didn't have 40 hours of open air; I had nights, weekends, and stolen moments.&lt;/p&gt;

&lt;p&gt;That was when I started "cooking."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;This guide walks you through exactly how I did it&lt;/strong&gt;—the AI-assisted workflow, the collaboration patterns, and the lessons learned shipping a production Laravel MVP in approximately &lt;strong&gt;40–50 hours of focused coding&lt;/strong&gt;, spread across 6 weeks.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;[!NOTE]&lt;br&gt;
&lt;strong&gt;Quick disclaimer&lt;/strong&gt;: This isn't a Windsurf promotion. Windsurf is my editor of choice, but any capable AI-powered environment—Cursor, VS Code with Copilot, or similar—would work. The workflow is tool-agnostic. The principles transfer.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  What You'll Learn
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;How to use AI for &lt;strong&gt;architectural scaffolding&lt;/strong&gt; before writing code&lt;/li&gt;
&lt;li&gt;Setting up an effective &lt;strong&gt;AI-assisted development environment&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;iterative workflow&lt;/strong&gt; that actually works under pressure&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Collaboration patterns&lt;/strong&gt; for distributed teams&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Post-launch maintenance&lt;/strong&gt; realities and how AI helps (and doesn't)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Time investment benchmarks&lt;/strong&gt; from a real project&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Reality Check: What Vibe Coding Is NOT
&lt;/h2&gt;

&lt;p&gt;Before we go further, let’s draw a hard line. "Vibe Coding" is not an excuse for sloppy engineering.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;It is NOT&lt;/strong&gt; pasting prompts and hoping for the best.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It is NOT&lt;/strong&gt; shipping code you don't understand.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It is NOT&lt;/strong&gt; bypassing security or architectural standards.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Vibe coding is &lt;strong&gt;context-aware acceleration&lt;/strong&gt;. It is using AI to handle the implementation details (the "how") so you can focus entirely on the architectural intent (the "what" and "why"). If you can't read the code the AI generates, you shouldn't ship it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;Before you start vibe coding your MVP, ensure you have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A clear problem statement (alignment &amp;gt; speed)&lt;/li&gt;
&lt;li&gt;Laravel development environment (I use &lt;a href="https://herd.laravel.com/" rel="noopener noreferrer"&gt;Laravel Herd&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;An AI-powered code editor (Windsurf, Cursor, or VS Code with Copilot)&lt;/li&gt;
&lt;li&gt;Access to high-reasoning LLMs for architectural thinking (I utilized Claude Sonnet 4.5, Google Gemini 3 Pro, and GPT-5 across my workflows)&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Step 1: Architect Before You Code
&lt;/h2&gt;

&lt;p&gt;Don't jump into your editor immediately. Use AI to think through architecture first.&lt;/p&gt;

&lt;h3&gt;
  
  
  What I Did
&lt;/h3&gt;

&lt;p&gt;With urgency as the backdrop, I needed to move fast but deliberately. The first phase involved getting an initial architecture in place—something I could think through and validate before committing to code.&lt;/p&gt;

&lt;p&gt;I opened &lt;strong&gt;Google Anti Gravity&lt;/strong&gt; and described the problem space. Not the solution—the problem. The constraints. The edge cases I could anticipate. This was my first time using it for this kind of architectural thinking, and it proved invaluable for exploring different approaches before writing a single line of code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Prompt pattern:
"I'm building a system that needs to [core requirement].
The key constraints are [list constraints].
What architecture patterns would you recommend?
What edge cases should I consider before I start?"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Why This Works
&lt;/h3&gt;

&lt;p&gt;AI excels at brainstorming and pattern matching. Before you've written a single line, you can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Validate data relationships&lt;/li&gt;
&lt;li&gt;Identify potential architectural decisions&lt;/li&gt;
&lt;li&gt;Surface edge cases you'd otherwise discover mid-implementation&lt;/li&gt;
&lt;li&gt;Get a mental model of the system's shape&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Time invested&lt;/strong&gt;: 1–2 hours&lt;br&gt;
&lt;strong&gt;Time saved&lt;/strong&gt;: Potentially days of refactoring&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 2: Set Up Your AI-Assisted Environment
&lt;/h2&gt;

&lt;p&gt;Once architecture is clear, switch to your code editor.&lt;/p&gt;

&lt;h3&gt;
  
  
  My Transition
&lt;/h3&gt;

&lt;p&gt;As the project evolved and iteration became heavier, I switched from Google AI Studio to &lt;strong&gt;Windsurf&lt;/strong&gt;. The shift wasn't about capability—it was about workflow fit. Windsurf better suited how I wanted to work contextually, maintaining awareness of the codebase as I moved through features and fixes. I was simply more familiar with its patterns.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Stack That Worked
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Google AI Studio (Gemini Advanced)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Initial architectural scaffolding and design validation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Windsurf&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Primary editor with contextual AI assistance&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Laravel Boost MCP&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Laravel-specific code generation and framework guidance&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Herd MCP&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Local development environment orchestration&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Tailwind CSS&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Utility-first styling for rapid UI development&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Livewire&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Reactive components without leaving PHP&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  MCP Configuration Note
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;[!WARNING]&lt;br&gt;
&lt;strong&gt;Laravel Boost MCP and Herd MCP are not compatible out-of-the-box with Windsurf's editor and plugins.&lt;/strong&gt; If you're using these MCP servers, you'll need to configure them carefully. I've written a detailed guide on fixing this: &lt;a href="https://dev.to/ojsholly/fixing-laravel-boost-in-windsurf-a-global-mcp-setup-guide-5126"&gt;Fixing Laravel Boost in Windsurf: A Global MCP Setup Guide&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Step 3: Iterative Development (The Vibe Coding Loop)
&lt;/h2&gt;

&lt;p&gt;Here's where AI-assisted development truly shines.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Core Workflow
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌─────────────────────────────────────────────┐
│  0. Create a solid implementation plan      │
│  1. Describe what you want to build         │
│  2. AI generates initial implementation     │
│  3. You review, refine, correct             │
│  4. Test immediately                        │
│  5. Repeat                                  │
└─────────────────────────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Non-Negotiable: The Implementation Plan
&lt;/h3&gt;

&lt;p&gt;Before writing any code, create a &lt;strong&gt;comprehensive implementation plan&lt;/strong&gt; from start to finish. This isn't optional—it's the foundation that makes AI-assisted development work.&lt;/p&gt;

&lt;p&gt;Use AI to help you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Map out the entire MVP scope&lt;/li&gt;
&lt;li&gt;Identify dependencies and sequencing&lt;/li&gt;
&lt;li&gt;Surface tradeoffs early (e.g., "Do we need this feature for v1?")&lt;/li&gt;
&lt;li&gt;Document decisions so you don't relitigate them later&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Fine-tune this plan iteratively with AI until it feels solid. The time invested here pays dividends when you're deep in implementation and need to make quick decisions.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyf0pkube9a2ohvg7jojn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyf0pkube9a2ohvg7jojn.png" alt="My actual implementation plan in Windsurf (Redacted for Client Privacy)" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  The Key Mindset Shift
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;AI-assisted ≠ AI-driven&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This distinction shaped everything. AI wasn't writing the application for me—it was helping me think faster. When I had an idea, I could validate it quickly. When I needed to scaffold a component, I could generate a starting point and refine it. When I hit a wall, I could explore alternatives without losing momentum.&lt;/p&gt;

&lt;p&gt;The human remains the architect. The AI accelerates execution.&lt;/p&gt;

&lt;h3&gt;
  
  
  Practical Tips from the Trenches
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Stay contextual&lt;/strong&gt;: Keep related files open. AI needs context to be useful.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Correct early&lt;/strong&gt;: When AI misunderstands, fix immediately. Don't let errors compound.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Commit frequently&lt;/strong&gt;: Small, atomic commits. You'll thank yourself during debugging.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Test as you go&lt;/strong&gt;: Don't wait until "it's done." Test components in isolation.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Step 4: Collaboration Patterns That Scale
&lt;/h2&gt;

&lt;p&gt;Sustained pressure demands distributed ownership. Building under pressure is rarely solo, and this project was no exception.&lt;/p&gt;

&lt;h3&gt;
  
  
  How We Worked
&lt;/h3&gt;

&lt;p&gt;The CTO and I logged significant hours on Google Meet—not merely discussing requirements, but actively testing together in real-time. He would surface something that felt off, I would push a fix, and we'd validate immediately. The rhythm became instinctive.&lt;/p&gt;

&lt;p&gt;Periodically, the CEO and company director joined these sessions. There's irreplaceable value in fresh perspective. Someone who hasn't been immersed in the same context for hours will identify friction you've internalized as normal.&lt;/p&gt;

&lt;h3&gt;
  
  
  Responsibility Partitioning
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Role&lt;/th&gt;
&lt;th&gt;Ownership&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;CTO&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Server provisioning, Bitbucket pipeline configuration, deployment orchestration, production stability&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Developer (me)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Business logic, user flows, edge case handling, bug fixes&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;This separation enabled &lt;strong&gt;genuine parallelism&lt;/strong&gt;. While I resolved a validation defect, he configured SSL termination. While he debugged a pipeline failure, I refined a notification flow. Neither of us waited on the other.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Feedback Loop
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;test → feedback → fix → repeat
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The collaboration wasn't ceremonial—it was operational. We weren't scheduling reviews to satisfy process. We were building together, in real-time, with shared accountability for the outcome.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 5: Maintenance (Where Reality Asserts Itself)
&lt;/h2&gt;

&lt;p&gt;Shipping the MVP marked a milestone, but seasoned engineers understand: it's the beginning, not the conclusion.&lt;/p&gt;

&lt;h3&gt;
  
  
  What to Expect Post-Launch
&lt;/h3&gt;

&lt;p&gt;Production users possess an uncanny ability to surface truths that no test suite anticipates:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Edge cases you never modeled&lt;/strong&gt; will surface&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Workflows you assumed were self-evident&lt;/strong&gt; won't be&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Data patterns&lt;/strong&gt; will defy your assumptions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In the weeks following initial deployment, defects emerged. Requirements evolved. The system that felt complete revealed its gaps under authentic load.&lt;/p&gt;

&lt;h3&gt;
  
  
  How AI Helps (and Doesn't)
&lt;/h3&gt;

&lt;p&gt;AI tooling continued to accelerate the work—suggesting fixes, helping trace through unfamiliar code paths, reducing the friction of context-switching.&lt;/p&gt;

&lt;p&gt;But &lt;strong&gt;judgment, prioritization, and accountability remained unambiguously human responsibilities&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;No AI can articulate &lt;em&gt;why&lt;/em&gt; something matters to the business. It can help remediate a defect, but it cannot determine whether that defect is critical or cosmetic.&lt;/p&gt;

&lt;h3&gt;
  
  
  A Real Refactoring Story: When Your Data Model Fights Back
&lt;/h3&gt;

&lt;p&gt;I'll be honest here: this one was on me.&lt;/p&gt;

&lt;p&gt;During the initial build, I was moving fast with AI-generated code and database structures. I reviewed them, approved them, and moved on. The schema looked reasonable. The relationships made sense at first glance. But I failed to catch a fundamental modeling issue—a key domain entity was set up as a standalone table when it should have been a specialized extension of an existing user-context relationship.&lt;/p&gt;

&lt;p&gt;The problem didn't surface until I was testing a related feature, hit a bug, and started digging deeper. That's when the friction became obvious: duplicate data paths, inconsistent relationships, and unnecessary complexity rippling across multiple features.&lt;/p&gt;

&lt;p&gt;Here's where AI became genuinely valuable as a thinking partner. I broke down the issue, proposed the remodeling approach, and asked: &lt;em&gt;"What are the ripple effects? And is this worth the effort now, or should we live with it?"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The response was unambiguous: do it now. The reasoning was sound—migrating data and updating relationships would only get harder as the dataset grew and more features depended on the flawed structure. Better to pay the cost early than compound it later.&lt;/p&gt;

&lt;p&gt;The fix required significant refactoring:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Extend an existing pivot table&lt;/strong&gt; with domain-specific columns&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Write a data migration&lt;/strong&gt; to move existing records&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Update every component, service, and notification&lt;/strong&gt; that touched the old model&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Refactor related feature relationships&lt;/strong&gt; to use the consolidated structure&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Update all the profile and management components&lt;/strong&gt; to use the new approach.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;The Critical "Senior" Moment&lt;/strong&gt;&lt;br&gt;
During this refactor, Windsurf suggested a "quick fix" that involved adding a JSON column to patch the data structure without migration. It would have worked instantly. It would have saved me 3 hours that night.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;I rejected it immediately.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Why? Because I know that JSON patches on core relational data turn into query nightmares six months later. An AI optimizes for &lt;em&gt;solving the current error&lt;/em&gt;. A senior engineer optimises for future maintainability. I forced the AI to generate the harder, proper migration path. Speed matters, but not at the cost of structural integrity.&lt;/p&gt;

&lt;p&gt;This was spread across multiple pull requests. AI accelerated each step—generating migration scaffolds, updating model relationships, tracing through affected components. However, the decision to restructure came from living with the original design and recognising where it broke down.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lesson&lt;/strong&gt;: Your first data model &lt;em&gt;might&lt;/em&gt; be wrong—and that's okay. Design for flexibility and possible refactoring down the road. Don't rush into rigid schema decisions just to feel "done." The cost of early flexibility is low; the cost of late rigidity is high.&lt;/p&gt;




&lt;h2&gt;
  
  
  Time Investment Benchmarks
&lt;/h2&gt;

&lt;p&gt;Transparency matters. Here's what this MVP actually took, derived from the project's commit history:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Active development days&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;8 distinct days&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Total elapsed time&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;~6 weeks&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Estimated coding hours&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;40–50 hours&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Total commits&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;107&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Context is critical here. I didn't take six weeks off to build this. I hold a full-time role as a Senior Backend Engineer and Team Lead, plus I actively maintain other personal software projects. This MVP was built entirely in the margins—early mornings, late nights, and weekends.&lt;/p&gt;

&lt;p&gt;This is where the true value of AI-assisted development lies. It wasn't just about generating code faster; it was about &lt;strong&gt;instant context recovery&lt;/strong&gt;. When I only had 45 minutes on a Tuesday night, I didn't spend 20 minutes remembering where I left off. Windsurf and the implementation plan allowed me to dive straight into the "flow state," execute a feature, and sign off.&lt;/p&gt;

&lt;p&gt;AI tooling didn't eliminate the work—it &lt;strong&gt;compressed&lt;/strong&gt; it. Efforts that might have consumed weeks under traditional workflows occurred in focused sessions, with rapid iteration and immediate validation.&lt;/p&gt;




&lt;h2&gt;
  
  
  Key Lessons for Your MVP
&lt;/h2&gt;

&lt;p&gt;Reflecting on this project, several principles crystallised:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Speed Is a Form of Clarity
&lt;/h3&gt;

&lt;p&gt;Let’s be uncomfortable for a second: &lt;strong&gt;Over-engineering is usually disguised fear in a lot of cases.&lt;/strong&gt; We build complex architectures for simple problems because we are afraid of tech debt. But for an MVP, the only debt that kills you is &lt;em&gt;apathy&lt;/em&gt;—building something nobody uses.&lt;/p&gt;

&lt;p&gt;In this project, I deliberately skipped standard abstractions I would mandate in my day job. It felt "wrong" at first. But that speed revealed the product's actual shape faster than any architecture diagram could. Code can be refactored; time cannot be refunded.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. AI Accelerates Momentum, Not Accountability
&lt;/h3&gt;

&lt;p&gt;These tools are force multipliers—they amplify velocity when direction is clear. But they do not own outcomes. Ownership remains yours.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Real Users Reveal Truths No Prompt Can Anticipate
&lt;/h3&gt;

&lt;p&gt;Test suites are constrained by your imagination. Users will discover paths you never considered. Architect for iteration, not premature perfection.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Collaboration Eclipses Solo Brilliance
&lt;/h3&gt;

&lt;p&gt;The tight feedback loop with the CTO—real-time testing, immediate remediation—delivered more value than any individual AI tool. Human collaboration remains the ultimate accelerator.&lt;/p&gt;




&lt;h2&gt;
  
  
  Quick Reference: The Vibe Coding Workflow
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                  PHASE 1: PREPARATION
                  [Problem Definition]
                           │
                           ▼
              [AI Architectural Brainstorm]
                           │
                           ▼
      [Implementation Plan] ───→ [Environment Setup]
                                        │
                                        │
      PHASE 2: VIBE CODING LOOP         │
            ┌───────────────────────────┘
            │
            ▼
      [Iterative Dev Loop] &amp;lt;──────────────┐
            │                             │
            ▼                             │
      {Working Feature?} ──── NO ─────────┤
            │                             │
           YES                            │
            │                             │
            ▼                             │
      [Test with Stakeholders]            │
            │                             │
            ▼                             │
        {Feedback?} ───────── YES ────────┘
            │
            NO (Approved)
            │
            ▼
       [ 🚀 SHIP IT ] ───→ [Maintenance &amp;amp; Iteration]

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Technical Stack Reference
&lt;/h2&gt;

&lt;p&gt;For those interested in the underlying architecture:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Layer&lt;/th&gt;
&lt;th&gt;Technology&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Framework&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Laravel&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Frontend&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Livewire for reactive components&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Styling&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Tailwind CSS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Architecture&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Multi-tenancy (implementation details intentionally omitted)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Final Thoughts: You Are Out of Excuses
&lt;/h2&gt;

&lt;p&gt;This project proved one uncomfortable truth: The barrier to entry has collapsed.&lt;/p&gt;

&lt;p&gt;The "I don't have time" excuse is gone. The "I need a team" excuse is gone. The tools I used (Windsurf, MCP, Claude, Gemini) didn't just write code; they removed the friction that usually kills momentum. They allowed me to be an Architect for 45 hours straight, rather than spending 20 of those hours fighting syntax.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;You have a choice.&lt;/strong&gt;&lt;br&gt;
You can keep polishing your boilerplate, feeling busy, and protecting your ego with "clean code" that nobody uses.&lt;br&gt;
Or you can open your editor tonight, scope it down, and ship something that might actually break.&lt;/p&gt;

&lt;p&gt;If you are a senior dev, these tools make you a 10x architect. If you are a junior, they are the best partner you will ever have.&lt;/p&gt;

&lt;p&gt;But they won't click the "Deploy" button for you.&lt;/p&gt;

&lt;p&gt;The window is open. Start cooking.&lt;/p&gt;

&lt;h2&gt;
  
  
  Further Reading
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://laravel.com/docs" rel="noopener noreferrer"&gt;Laravel Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://livewire.laravel.com/docs" rel="noopener noreferrer"&gt;Livewire Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://tailwindcss.com/docs" rel="noopener noreferrer"&gt;Tailwind CSS Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/ojsholly/fixing-laravel-boost-in-windsurf-a-global-mcp-setup-guide-5126"&gt;Fixing Laravel Boost in Windsurf: A Global MCP Setup Guide&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Have you built something under similar pressure? I'd love to hear about your experience with AI-assisted development. What worked? What didn't? Drop a comment below.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>laravel</category>
      <category>ai</category>
      <category>tutorial</category>
      <category>productivity</category>
    </item>
    <item>
      <title>What Building a Simple E-commerce Cart Taught Me About Senior Laravel Engineering</title>
      <dc:creator>Olusola Ojewunmi</dc:creator>
      <pubDate>Mon, 12 Jan 2026 07:40:08 +0000</pubDate>
      <link>https://dev.to/ojsholly/what-building-a-simple-e-commerce-cart-taught-me-about-senior-laravel-engineering-30m6</link>
      <guid>https://dev.to/ojsholly/what-building-a-simple-e-commerce-cart-taught-me-about-senior-laravel-engineering-30m6</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; I built a production-grade e-commerce cart in 14 hours to explore Laravel Volt. This article focuses on the engineering decisions that matter: concurrency handling, partial failure recovery, data snapshotting, and query optimization. &lt;a href="https://github.com/Ojsholly/laravel-ecommerce-cart" rel="noopener noreferrer"&gt;Full source code available on GitHub&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  🎯 Context
&lt;/h2&gt;

&lt;p&gt;Over the holiday break, I carved out a focused window to build something I'd been meaning to explore: a production-grade e-commerce cart using Laravel Volt. The entire project—from first commit to final polish—took roughly &lt;strong&gt;14 hours&lt;/strong&gt; spread across December 29-30, 2025.&lt;/p&gt;

&lt;p&gt;This wasn't a side project for fun. It was part of a technical assessment where I was given flexibility to choose my stack. I opted to try Laravel Volt for the first time, knowing it would be a learning experience. Ultimately, the team was looking for a different implementation approach, and we parted ways amicably. But the exercise itself was valuable—it forced me to think deeply about correctness, concurrency, and the kind of engineering judgment that separates junior implementations from senior ones.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;📦 The complete source code is available here:&lt;/strong&gt; &lt;strong&gt;&lt;a href="https://github.com/Ojsholly/laravel-ecommerce-cart" rel="noopener noreferrer"&gt;https://github.com/Ojsholly/laravel-ecommerce-cart&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This article isn't about the UI or feature completeness. It's about the backend decisions that matter when you're building systems that handle real money, real inventory, and real user expectations.&lt;/p&gt;




&lt;h2&gt;
  
  
  🛠️ Tech Stack
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Layer&lt;/th&gt;
&lt;th&gt;Technology&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Framework&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Laravel 12&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Language&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;PHP 8.4&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Frontend&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Livewire 3 + Volt (first time using Volt)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Styling&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Tailwind CSS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Database (Dev)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;PostgreSQL&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Database (Test)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;SQLite (in-memory)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Background Jobs&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Laravel Queues &amp;amp; Scheduler&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Testing&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Pest&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Version Control&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;GitHub&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The choice of Volt was deliberate—I wanted to see how it felt to build reactive components without writing explicit Livewire classes. The learning curve was steeper than expected, but it paid off in reduced boilerplate.&lt;/p&gt;




&lt;h2&gt;
  
  
  🎯 The Problems That Matter
&lt;/h2&gt;

&lt;p&gt;When you're building an e-commerce cart, the interesting problems aren't in the UI. They're in the edge cases:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;⚡ What happens when two users try to buy the last item simultaneously?&lt;/li&gt;
&lt;li&gt;🛒 How do you handle partial checkout when some items go out of stock mid-session?&lt;/li&gt;
&lt;li&gt;📧 How do you avoid spamming admins with low-stock notifications?&lt;/li&gt;
&lt;li&gt;📸 How do you ensure historical order data remains accurate even if products change?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These are the problems that junior engineers often miss—and the ones that cause production incidents.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. ⚡ Concurrency &amp;amp; Row-Level Locking
&lt;/h2&gt;

&lt;p&gt;The most critical part of checkout is stock validation. If you don't lock rows properly, you'll oversell inventory.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Problem: Race Conditions
&lt;/h3&gt;

&lt;p&gt;Here's the naive approach (don't do this):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ❌ Race condition: two requests can both see stock = 1&lt;/span&gt;
&lt;span class="nv"&gt;$product&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Product&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$productId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$product&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;stock_quantity&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="nv"&gt;$quantity&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$product&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;decrement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'stock_quantity'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$quantity&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The problem:&lt;/strong&gt; Between checking stock and decrementing it, another request can sneak in. Both requests see &lt;code&gt;stock_quantity = 1&lt;/code&gt;, both proceed, and you've just sold two units of an item you only had one of.&lt;/p&gt;

&lt;h4&gt;
  
  
  Visual Breakdown of the Race Condition
&lt;/h4&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Timeline&lt;/th&gt;
&lt;th&gt;User A&lt;/th&gt;
&lt;th&gt;User B&lt;/th&gt;
&lt;th&gt;Database Stock&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;T1&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Read stock → sees &lt;code&gt;1&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;1&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;T2&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;Read stock → sees &lt;code&gt;1&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;&lt;code&gt;1&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;T3&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Check: &lt;code&gt;1 &amp;gt;= 1&lt;/code&gt; ✅&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;1&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;T4&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;Check: &lt;code&gt;1 &amp;gt;= 1&lt;/code&gt; ✅&lt;/td&gt;
&lt;td&gt;&lt;code&gt;1&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;T5&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Decrement → &lt;code&gt;stock = 0&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;0&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;T6&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;Decrement → &lt;code&gt;stock = -1&lt;/code&gt; ❌&lt;/td&gt;
&lt;td&gt;&lt;code&gt;-1&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Result:&lt;/strong&gt; Both purchases succeed, but inventory is now negative. You've oversold.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Solution: Pessimistic Locking
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ✅ Lock the row for the duration of the transaction&lt;/span&gt;
&lt;span class="nv"&gt;$product&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Product&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;lockForUpdate&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$productId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$product&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nv"&gt;$product&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;hasStock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$quantity&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$product&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;decrement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'stock_quantity'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$quantity&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is wrapped in a database transaction, so the lock is held until the transaction commits. No other request can read or modify that row until we're done.&lt;/p&gt;

&lt;h3&gt;
  
  
  Full Checkout Implementation
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;processCheckout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Cart&lt;/span&gt; &lt;span class="nv"&gt;$cart&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;Order&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;DB&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;transaction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$cart&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$availableItems&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$unavailableItems&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;validateStock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$cart&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$availableItems&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;isEmpty&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;InsufficientStockException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'All items are out of stock.'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="nv"&gt;$order&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Order&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="cm"&gt;/* ... */&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

        &lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$availableItems&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nv"&gt;$item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;createOrderItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$order&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$item&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;decrementStock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$item&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$item&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;quantity&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;removeAvailableItemsFromCart&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$cart&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$availableItems&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$order&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;validateStock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Cart&lt;/span&gt; &lt;span class="nv"&gt;$cart&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;array&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$availableItems&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;collect&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nv"&gt;$unavailableItems&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;collect&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$cart&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;items&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nv"&gt;$item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$product&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Product&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;lockForUpdate&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$item&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;product_id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$product&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nv"&gt;$product&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;hasStock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$item&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;quantity&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nv"&gt;$availableItems&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$item&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nv"&gt;$unavailableItems&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$item&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$availableItems&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$unavailableItems&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;💡 Key Takeaway:&lt;/strong&gt; Without row-level locking, you'll have angry customers who paid for items you don't have. With it, you have a system that behaves correctly under load.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  2. 🛒 Handling Partial Failure Gracefully
&lt;/h2&gt;

&lt;p&gt;Here's a scenario that trips up a lot of implementations:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;User adds 5 items to their cart&lt;/li&gt;
&lt;li&gt;Another user buys one of those items, depleting stock&lt;/li&gt;
&lt;li&gt;First user tries to checkout&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;What should happen?&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Approach&lt;/th&gt;
&lt;th&gt;Junior Implementation ❌&lt;/th&gt;
&lt;th&gt;Senior Implementation ✅&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Error Handling&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Fail entire checkout with generic error&lt;/td&gt;
&lt;td&gt;Identify which items are available&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Cart Management&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Delete all items (losing user intent)&lt;/td&gt;
&lt;td&gt;Process only available items&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Stock Handling&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Proceed anyway and oversell&lt;/td&gt;
&lt;td&gt;Leave unavailable items in cart&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;User Feedback&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Confusing error message&lt;/td&gt;
&lt;td&gt;Clear feedback on what was purchased&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Implementation
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Separate available from unavailable items&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$availableItems&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$unavailableItems&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;validateStock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$cart&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Only process available items&lt;/span&gt;
&lt;span class="nv"&gt;$pricing&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;priceCalculationService&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;calculateOrderPricing&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$availableItems&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Remove only what was purchased&lt;/span&gt;
&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;removeAvailableItemsFromCart&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$cart&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$availableItems&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The key insight:&lt;/strong&gt; Preserve user intent. If they wanted 5 items and only 4 are available, sell them 4 and keep the 5th in their cart. Don't make them start over.&lt;/p&gt;

&lt;h3&gt;
  
  
  Frontend Handling
&lt;/h3&gt;

&lt;p&gt;On the frontend, unavailable items are clearly distinguished:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ Visual indicators (opacity, grayscale images)&lt;/li&gt;
&lt;li&gt;✅ "Out of Stock" badges&lt;/li&gt;
&lt;li&gt;✅ Excluded from pricing calculations&lt;/li&gt;
&lt;li&gt;✅ Checkout button disabled if all items are unavailable&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;💡 Key Takeaway:&lt;/strong&gt; This isn't just good UX—it's correct behavior. Partial failure is a reality in distributed systems. Handle it gracefully.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  3. 📸 Snapshotting Data for Historical Accuracy
&lt;/h2&gt;

&lt;p&gt;Here's a mistake I see often: storing only foreign keys in order items.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ❌ What happens if the product is deleted or its price changes?&lt;/span&gt;
&lt;span class="nc"&gt;OrderItem&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
    &lt;span class="s1"&gt;'order_id'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$order&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'product_id'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$product&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'quantity'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$quantity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Six months later, the product is deleted or repriced. Now your order history is broken. You can't show customers what they actually paid for.&lt;/p&gt;

&lt;p&gt;The fix: snapshot the product data at the time of purchase.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ✅ Preserve historical accuracy&lt;/span&gt;
&lt;span class="nc"&gt;OrderItem&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
    &lt;span class="s1"&gt;'order_id'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$order&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'product_id'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$product&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'quantity'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$quantity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'price_snapshot'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$product&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;price&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'product_snapshot'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$product&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;toSnapshot&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;toSnapshot()&lt;/code&gt; method captures everything needed to reconstruct the order:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;toSnapshot&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;array&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="s1"&gt;'id'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'uuid'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;uuid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'name'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'description'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'price'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;price&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'images'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;images&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'primary_image'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;primary_image&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;💡 Key Takeaway:&lt;/strong&gt; Orders are legal documents. They need to be immutable and accurate, even if the underlying product data changes or disappears.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  4. 📧 Background Jobs &amp;amp; Avoiding Notification Spam
&lt;/h2&gt;

&lt;p&gt;Low-stock notifications are a common feature. The naive implementation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ❌ Sends a notification every time stock decrements below threshold&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$product&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;stock_quantity&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="nv"&gt;$lowStockThreshold&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;SendLowStockNotification&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$product&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The problem: if stock is at 5 and the threshold is 10, you'll send a notification on every sale until stock hits 0. That's 5 emails for the same issue.&lt;/p&gt;

&lt;p&gt;The fix: only notify when crossing the threshold.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;decrementStock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Product&lt;/span&gt; &lt;span class="nv"&gt;$product&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nv"&gt;$quantity&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$lowStockThreshold&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;config&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'cart.low_stock_threshold'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nv"&gt;$stockBeforeDecrement&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$product&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;stock_quantity&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="nv"&gt;$product&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;decrement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'stock_quantity'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$quantity&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nv"&gt;$stockAfterDecrement&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$product&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;fresh&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;stock_quantity&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// Only notify when crossing from above to below threshold&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$stockBeforeDecrement&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$lowStockThreshold&lt;/span&gt; 
        &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nv"&gt;$stockAfterDecrement&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="nv"&gt;$lowStockThreshold&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;SendLowStockNotification&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$product&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;💡 Key Takeaway:&lt;/strong&gt; Notification fatigue is real. Admins will ignore your alerts if you spam them. Send one notification per threshold crossing, not one per sale.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  5. 📊 Daily Sales Reports &amp;amp; Query Optimization
&lt;/h2&gt;

&lt;p&gt;Another common feature: daily sales reports sent via email. The challenge is aggregating data efficiently without N+1 queries.&lt;/p&gt;

&lt;p&gt;Here's the job structure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;handle&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$stats&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;calculateStats&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nv"&gt;$recentOrders&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getRecentOrders&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nv"&gt;$topProducts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getTopSellingProducts&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="nc"&gt;Mail&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$adminEmail&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;DailySalesReport&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$stats&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$recentOrders&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$topProducts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;date&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The interesting part is &lt;code&gt;getTopSellingProducts()&lt;/code&gt;. You need to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Aggregate sales by product&lt;/li&gt;
&lt;li&gt;Fetch product snapshots (since products might have changed)&lt;/li&gt;
&lt;li&gt;Avoid N+1 queries&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Here's how I did it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;getTopSellingProducts&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;Collection&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Single query to get aggregated sales data&lt;/span&gt;
    &lt;span class="nv"&gt;$topProductsData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Order&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;completed&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;whereDate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'orders.created_at'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;date&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nb"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'order_items'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'orders.id'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'='&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'order_items.order_id'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;selectRaw&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'
            order_items.product_id, 
            sum(order_items.quantity) as total_quantity, 
            sum(order_items.quantity * order_items.price_snapshot) as total_sales
        '&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;groupBy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'order_items.product_id'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;orderByDesc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'total_quantity'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// Single query to fetch order items (which contain snapshots)&lt;/span&gt;
    &lt;span class="nv"&gt;$orderItemsByProductId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;OrderItem&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;whereIn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'product_id'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$topProductsData&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;pluck&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'product_id'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;whereHas&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'order'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$q&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$q&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;completed&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;whereDate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'created_at'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;date&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;keyBy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'product_id'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Map aggregated data to snapshots&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$topProductsData&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$orderItemsByProductId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$orderItem&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$orderItemsByProductId&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$item&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;product_id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nv"&gt;$snapshot&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$orderItem&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;product_snapshot&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="s1"&gt;'name'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$snapshot&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'name'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="s1"&gt;'Unknown Product'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'quantity'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$item&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;total_quantity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'revenue'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;number_format&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;float&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;$item&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;total_sales&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;💡 Key Takeaway:&lt;/strong&gt; N+1 queries kill performance. Two queries for any number of products is better than N+1 queries for N products.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  6. 🏗️ Separation of Concerns
&lt;/h2&gt;

&lt;p&gt;One pattern I stuck to throughout: &lt;strong&gt;keep business logic out of controllers and Livewire components&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Components should be thin:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;placeOrder&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$checkoutService&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;app&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;CheckoutService&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nv"&gt;$order&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$checkoutService&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;processCheckout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;cart&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'cart-updated'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'notify'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'Order placed successfully!'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'success'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;redirect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'orders.show'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$order&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;navigate&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;\Exception&lt;/span&gt; &lt;span class="nv"&gt;$e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'notify'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;$e&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getMessage&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'error'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All the complexity lives in &lt;code&gt;CheckoutService&lt;/code&gt;. This makes testing easier, logic reusable, and the codebase more maintainable.&lt;/p&gt;




&lt;h2&gt;
  
  
  💭 What I Learned About Volt
&lt;/h2&gt;

&lt;p&gt;This was my first time using Laravel Volt, and it was a mixed experience.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What I liked:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Less boilerplate than traditional Livewire classes&lt;/li&gt;
&lt;li&gt;Faster iteration for simple components&lt;/li&gt;
&lt;li&gt;Good for prototyping&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;What I found challenging:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Computed properties require specific naming conventions (&lt;code&gt;getXProperty()&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Debugging is harder without explicit class files&lt;/li&gt;
&lt;li&gt;IDE support is weaker than traditional Livewire&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Would I use it again?&lt;/strong&gt; For rapid prototyping, yes. For a large production app, I'd probably stick with traditional Livewire classes for better tooling and discoverability.&lt;/p&gt;




&lt;h2&gt;
  
  
  🎓 Reflections
&lt;/h2&gt;

&lt;p&gt;Building this cart in 14 hours was a forcing function. I couldn't afford to bikeshed or over-engineer. I had to focus on correctness first, polish second.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The things that mattered:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ Transactions and locking&lt;/li&gt;
&lt;li&gt;✅ Handling partial failure gracefully&lt;/li&gt;
&lt;li&gt;✅ Snapshotting data for historical accuracy&lt;/li&gt;
&lt;li&gt;✅ Avoiding notification spam&lt;/li&gt;
&lt;li&gt;✅ Query optimization&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The things that didn't matter (in this timeframe):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;⏸️ Perfect UI polish&lt;/li&gt;
&lt;li&gt;⏸️ Comprehensive admin dashboards&lt;/li&gt;
&lt;li&gt;⏸️ Payment gateway integration&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;💡 Key Takeaway:&lt;/strong&gt; This is the kind of prioritization that defines senior engineering: knowing what to build, what to defer, and what to skip entirely.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  🎯 Final Thoughts
&lt;/h2&gt;

&lt;p&gt;The team ultimately went with a different tech stack, which is fine. Not every project is a fit, and that's okay. But the exercise itself was valuable—it forced me to think through problems I don't encounter in my day-to-day work, and it gave me a chance to explore Volt in a real-world context.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The repo is open source. Star it if you find the locking logic useful:&lt;/strong&gt; &lt;strong&gt;&lt;a href="https://github.com/Ojsholly/laravel-ecommerce-cart" rel="noopener noreferrer"&gt;https://github.com/Ojsholly/laravel-ecommerce-cart&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The codebase includes:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ 123 passing tests (Pest)&lt;/li&gt;
&lt;li&gt;✅ Full checkout flow with concurrency handling&lt;/li&gt;
&lt;li&gt;✅ Background jobs for notifications and reports&lt;/li&gt;
&lt;li&gt;✅ Partial checkout support&lt;/li&gt;
&lt;li&gt;✅ Product snapshotting&lt;/li&gt;
&lt;li&gt;✅ Query optimization&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It's not perfect, but it's correct. And in production systems, &lt;strong&gt;correctness is what matters&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  🔗 Links &amp;amp; Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;📦 GitHub Repository:&lt;/strong&gt; &lt;a href="https://github.com/Ojsholly/laravel-ecommerce-cart" rel="noopener noreferrer"&gt;https://github.com/Ojsholly/laravel-ecommerce-cart&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;⭐ Star the repo&lt;/strong&gt; if you found the locking logic or partial failure handling useful&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;💬 Questions?&lt;/strong&gt; Feel free to reach out—I'm always happy to discuss Laravel architecture and engineering tradeoffs&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Thanks for reading! If this helped you think differently about e-commerce cart implementation, consider sharing it with your team.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>laravel</category>
      <category>architecture</category>
      <category>php</category>
      <category>performance</category>
    </item>
    <item>
      <title>Fixing Laravel Boost in Windsurf: A Global MCP Setup Guide</title>
      <dc:creator>Olusola Ojewunmi</dc:creator>
      <pubDate>Mon, 22 Dec 2025 12:14:54 +0000</pubDate>
      <link>https://dev.to/ojsholly/fixing-laravel-boost-in-windsurf-a-global-mcp-setup-guide-5126</link>
      <guid>https://dev.to/ojsholly/fixing-laravel-boost-in-windsurf-a-global-mcp-setup-guide-5126</guid>
      <description>&lt;h1&gt;
  
  
  Fixing Laravel Boost in Windsurf: A Global MCP Setup Guide
&lt;/h1&gt;

&lt;p&gt;If you’ve tried adding Laravel Boost as an MCP server in Windsurf and hit &lt;strong&gt;“cannot initialize server”&lt;/strong&gt;, you’re not alone.&lt;/p&gt;

&lt;p&gt;The most common cause is simple:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Windsurf often starts MCP servers from a &lt;strong&gt;working directory that is NOT your Laravel project root&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;So a “normal” MCP command like &lt;code&gt;php artisan boost:mcp&lt;/code&gt; fails because &lt;code&gt;artisan&lt;/code&gt; isn’t in the current directory (or it runs with the wrong PHP).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In other words: this is usually not a Boost problem — it’s a &lt;strong&gt;Windsurf MCP launch context&lt;/strong&gt; problem.&lt;/p&gt;

&lt;p&gt;When Laravel announced &lt;strong&gt;Boost&lt;/strong&gt;, I was thrilled.&lt;br&gt;
I rushed over to the docs expecting to see a simple “Use this with Windsurf” section.&lt;br&gt;
But as I kept scrolling… that smile gradually dropped from my face.&lt;br&gt;
I saw Cursor, Junie, and a bunch of familiar integrations — but not Windsurf.&lt;br&gt;
So I kept searching until I landed on a setup that’s boringly reliable.&lt;/p&gt;

&lt;p&gt;This tutorial shows a robust approach that works across &lt;em&gt;all&lt;/em&gt; your Laravel projects:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Install Boost per project (required)&lt;/li&gt;
&lt;li&gt;Add a Windsurf MCP server that calls a &lt;strong&gt;wrapper script&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;The wrapper script finds the correct Laravel project and runs &lt;code&gt;php artisan boost:mcp&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you keep multiple Laravel projects open at the same time, you’ll also see how to configure &lt;strong&gt;per-project MCP entries&lt;/strong&gt; so each project stays isolated.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; While we’re configuring Windsurf “globally” so it can work with any project, &lt;strong&gt;Laravel Boost itself must still be installed locally in every Laravel project&lt;/strong&gt; via Composer. This wrapper simply helps Windsurf find the correct local installation.&lt;/p&gt;

&lt;p&gt;Bonus:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add &lt;strong&gt;Herd MCP&lt;/strong&gt; to Windsurf too (handy if you use Herd for local sites/PHP)&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  The target architecture (what we’re building)
&lt;/h2&gt;

&lt;p&gt;Windsurf can launch MCP servers from arbitrary directories, so the reliable design is to put an adaptive layer in front of Boost:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Windsurf (any cwd)
  -&amp;gt; wrapper script (stable entrypoint)
       -&amp;gt; find Laravel project root (locate artisan)
       -&amp;gt; choose correct PHP (prefer Herd / isolated PHP)
       -&amp;gt; exec: php artisan boost:mcp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once you understand that flow, every config choice in this guide makes sense.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;You have &lt;strong&gt;Windsurf&lt;/strong&gt; installed&lt;/li&gt;
&lt;li&gt;You have &lt;strong&gt;PHP&lt;/strong&gt; installed (Herd/Homebrew/Linux packages/Windows PHP)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Python 3&lt;/strong&gt; is recommended (only needed for the optional auto-detection fallback; without it the script falls back to &lt;code&gt;$PWD&lt;/code&gt; / &lt;code&gt;SITE_PATH&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;You have at least one Laravel project&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Install Boost in each Laravel project (required)
&lt;/h3&gt;

&lt;p&gt;In every Laravel project you want Boost to work with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;composer require laravel/boost &lt;span class="nt"&gt;--dev&lt;/span&gt;
php artisan boost:install
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then verify inside that project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;php artisan boost:mcp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If this fails, fix your Laravel app boot/dependencies first. MCP depends on the app being able to boot.&lt;/p&gt;




&lt;h2&gt;
  
  
  Understanding Windsurf MCP config files
&lt;/h2&gt;

&lt;p&gt;Windsurf follows the Claude Desktop MCP config schema.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Windsurf Editor:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;macOS/Linux: &lt;strong&gt;&lt;code&gt;~/.codeium/mcp_config.json&lt;/code&gt;&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Windows: &lt;strong&gt;&lt;code&gt;C:\Users\&amp;lt;you&amp;gt;\.codeium\mcp_config.json&lt;/code&gt;&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;Windsurf JetBrains plugin (PhpStorm/IntelliJ):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;macOS/Linux: &lt;strong&gt;&lt;code&gt;~/.codeium/windsurf/mcp_config.json&lt;/code&gt;&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Windows: &lt;strong&gt;&lt;code&gt;C:\Users\&amp;lt;you&amp;gt;\.codeium\windsurf\mcp_config.json&lt;/code&gt;&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Warning:&lt;/strong&gt; If you edit the wrong file, you’ll keep seeing &lt;strong&gt;“cannot initialise server”&lt;/strong&gt; even though the JSON looks correct.&lt;/p&gt;

&lt;p&gt;Quick way to confirm you’re editing the right file:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Temporarily point the &lt;code&gt;command&lt;/code&gt; to a definitely-invalid path (or rename your wrapper script).&lt;/li&gt;
&lt;li&gt;Refresh MCP servers in Windsurf.&lt;/li&gt;
&lt;li&gt;If the error message doesn’t change, you’re editing the wrong config file.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For the rest of this guide:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If you’re using the &lt;strong&gt;Windsurf Editor&lt;/strong&gt;, edit &lt;code&gt;~/.codeium/mcp_config.json&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;If you’re using &lt;strong&gt;Windsurf inside PhpStorm&lt;/strong&gt;, edit &lt;code&gt;~/.codeium/windsurf/mcp_config.json&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Tip: You can include multiple servers (Boost, Herd, etc.) inside the same &lt;code&gt;mcpServers&lt;/code&gt; object.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Fix: Use a Wrapper Script
&lt;/h2&gt;

&lt;p&gt;Instead of configuring &lt;code&gt;laravel-boost&lt;/code&gt; as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"php"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"args"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"artisan"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"boost:mcp"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;…we configure it to run a wrapper script. The wrapper script:&lt;/p&gt;

&lt;p&gt;Phase 1 — Locate the Laravel project root&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Search upward for &lt;code&gt;artisan&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Fall back to other hints like &lt;code&gt;SITE_PATH&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;(Optional) Use Windsurf trackers to guess active workspaces (&lt;strong&gt;requires Python 3&lt;/strong&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Phase 2 — Choose the right PHP&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Prefer &lt;strong&gt;Herd’s per-site PHP&lt;/strong&gt; when available (so isolated sites use their own PHP)&lt;/li&gt;
&lt;li&gt;Fall back to a usable &lt;code&gt;php&lt;/code&gt; binary&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Phase 3 — Launch MCP&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;exec herd php artisan boost:mcp&lt;/code&gt; (preferred)&lt;/li&gt;
&lt;li&gt;or &lt;code&gt;exec &amp;lt;php_bin&amp;gt; artisan boost:mcp&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Using the right PHP version per project (Herd + alternatives)
&lt;/h2&gt;

&lt;p&gt;One subtle issue when you have lots of Laravel projects is PHP version drift: one project might require PHP 8.1 while another needs PHP 8.3.&lt;/p&gt;

&lt;h3&gt;
  
  
  If you use Laravel Herd (recommended)
&lt;/h3&gt;

&lt;p&gt;Herd supports &lt;strong&gt;per-site PHP versions&lt;/strong&gt; via “isolation”. Once a project is isolated, Herd can automatically run the correct PHP when you execute commands from that project.&lt;/p&gt;

&lt;p&gt;Set a PHP version for a project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; /path/to/your-project
herd isolate 8.3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Confirm what PHP Herd will use for that project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; /path/to/your-project
herd which-php
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That’s why the wrapper script in this tutorial prefers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;herd php artisan boost:mcp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  If you’re not using Herd
&lt;/h3&gt;

&lt;p&gt;You still have options:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;asdf / mise / phpenv&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;These can select PHP based on the current directory (e.g. &lt;code&gt;.tool-versions&lt;/code&gt; / &lt;code&gt;.mise.toml&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;The key is ensuring the tool’s “shims” are available to Windsurf (non-interactive process).&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;Docker / Laravel Sail&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You can run Boost using the project’s containerized PHP instead of your host PHP.&lt;/li&gt;
&lt;li&gt;This is heavier, but very consistent across teams.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;One global PHP&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If all your projects support the same PHP version, the simplest option is to keep one PHP installed globally (Herd/Homebrew/etc.).&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;




&lt;h2&gt;
  
  
  macOS / Linux Setup
&lt;/h2&gt;

&lt;h2&gt;
  
  
  Step 1: Create the wrapper script
&lt;/h2&gt;

&lt;p&gt;Create a file at:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;macOS/Linux: &lt;code&gt;~/.codeium/laravel-boost-mcp.sh&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then make it executable:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;chmod&lt;/span&gt; +x ~/.codeium/laravel-boost-mcp.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 2: Add &lt;code&gt;laravel-boost&lt;/code&gt; to Windsurf MCP config
&lt;/h2&gt;

&lt;p&gt;Edit:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Windsurf Editor (macOS/Linux): &lt;code&gt;~/.codeium/mcp_config.json&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Windsurf JetBrains plugin (macOS/Linux): &lt;code&gt;~/.codeium/windsurf/mcp_config.json&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Add (or replace) your &lt;code&gt;laravel-boost&lt;/code&gt; entry:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"mcpServers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"laravel-boost"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/Users/&amp;lt;you&amp;gt;/.codeium/laravel-boost-mcp.sh"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"args"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"${workspaceFolder}"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"disabled"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"env"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Multi-project isolation (recommended)
&lt;/h2&gt;

&lt;p&gt;If you work on multiple Laravel projects in parallel, create &lt;strong&gt;one Boost entry per project&lt;/strong&gt; (same wrapper, different &lt;code&gt;env&lt;/code&gt;), and give each entry a unique server name.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"mcpServers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"laravel-boost-project-name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/Users/&amp;lt;you&amp;gt;/.codeium/laravel-boost-mcp.sh"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"args"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"${workspaceFolder}"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"disabled"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"env"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"LARAVEL_PROJECT_ROOT"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/path/to/project-name"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"laravel-boost-another-project"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/Users/&amp;lt;you&amp;gt;/.codeium/laravel-boost-mcp.sh"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"args"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"${workspaceFolder}"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"disabled"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"env"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"LARAVEL_PROJECT_ROOT"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/path/to/another-project"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Keep your other MCP servers in the same file; just add this entry inside the existing &lt;code&gt;mcpServers&lt;/code&gt; object.&lt;/li&gt;
&lt;li&gt;The path must be the &lt;strong&gt;full absolute path&lt;/strong&gt; to the script.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you’re adding multiple MCP servers, don’t replace the whole file—just add &lt;code&gt;laravel-boost&lt;/code&gt; inside the existing &lt;code&gt;mcpServers&lt;/code&gt; object.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: Restart/refresh MCP servers in Windsurf
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Open Windsurf settings&lt;/li&gt;
&lt;li&gt;Go to MCP / Tools&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;refresh&lt;/strong&gt; (or restart Windsurf)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 4: Quick sanity check
&lt;/h2&gt;

&lt;p&gt;Run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;~/.codeium/laravel-boost-mcp.sh &lt;span class="nt"&gt;--check&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;what directory it decided is the Laravel project root (&lt;code&gt;artisan_dir&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;which php binary it found as a fallback (&lt;code&gt;php_bin&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;whether Herd CLI is available (&lt;code&gt;herd_bin&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;what Herd would use as the PHP for that project (&lt;code&gt;herd_which_php&lt;/code&gt;) if available&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If this output looks correct but Windsurf still can’t start the server, jump to the Troubleshooting section below.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 5: Test the wrapper in real-world scenarios
&lt;/h2&gt;

&lt;p&gt;This wrapper is designed to behave well even when Windsurf starts it from a “random” working directory.&lt;/p&gt;

&lt;h3&gt;
  
  
  Scenario A: Not a Laravel project (expected: skipped)
&lt;/h3&gt;

&lt;p&gt;From any folder that is not a Laravel app:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;~/.codeium/laravel-boost-mcp.sh &lt;span class="nt"&gt;--check&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Expected output includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;skipped (no Laravel project detected; artisan not found)&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Scenario B: Laravel project without Boost installed (expected: skipped)
&lt;/h3&gt;

&lt;p&gt;Inside a Laravel project that does NOT have Boost:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; /path/to/laravel-project
~/.codeium/laravel-boost-mcp.sh &lt;span class="nt"&gt;--check&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Expected output includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;skipped (Boost not installed in this project)&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Scenario C: Laravel project with Boost + Herd per-site PHP (expected: Herd isolated PHP)
&lt;/h3&gt;

&lt;p&gt;Inside a Laravel project with Boost installed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; /path/to/laravel-project
composer require laravel/boost &lt;span class="nt"&gt;--dev&lt;/span&gt;
herd isolate 8.3
herd which-php
~/.codeium/laravel-boost-mcp.sh &lt;span class="nt"&gt;--check&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Expected output includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;herd_bin: ...&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;herd_which_php: ...&lt;/code&gt; (matching your isolated version)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then, to confirm it can actually start, run the wrapper (this is a long-running process; stop with Ctrl+C):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;~/.codeium/laravel-boost-mcp.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Final macOS/Linux wrapper script (multi-project safe)
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;Save as: &lt;code&gt;~/.codeium/laravel-boost-mcp.sh&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;
&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-euo&lt;/span&gt; pipefail

find_artisan_dir&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="nb"&gt;local dir&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="k"&gt;while &lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
    if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$dir&lt;/span&gt;&lt;span class="s2"&gt;/artisan"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
      &lt;/span&gt;&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s2"&gt;"%s"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$dir&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
      &lt;span class="k"&gt;return &lt;/span&gt;0
    &lt;span class="k"&gt;fi

    &lt;/span&gt;&lt;span class="nb"&gt;local &lt;/span&gt;parent
    &lt;span class="nv"&gt;parent&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;dirname&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$dir&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$parent&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$dir&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
      return &lt;/span&gt;1
    &lt;span class="k"&gt;fi
    &lt;/span&gt;&lt;span class="nb"&gt;dir&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$parent&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="k"&gt;done&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

find_php&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="nb"&gt;command&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; php &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;/dev/null 2&amp;gt;&amp;amp;1&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;command&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; php
    &lt;span class="k"&gt;return &lt;/span&gt;0
  &lt;span class="k"&gt;fi

  &lt;/span&gt;&lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;candidates&lt;/span&gt;&lt;span class="o"&gt;=(&lt;/span&gt;
    &lt;span class="s2"&gt;"/opt/homebrew/bin/php"&lt;/span&gt;
    &lt;span class="s2"&gt;"/usr/local/bin/php"&lt;/span&gt;
    &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/Library/Application Support/Herd/bin/php"&lt;/span&gt;
  &lt;span class="o"&gt;)&lt;/span&gt;

  &lt;span class="nb"&gt;local &lt;/span&gt;p
  &lt;span class="k"&gt;for &lt;/span&gt;p &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;candidates&lt;/span&gt;&lt;span class="p"&gt;[@]&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
    if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nt"&gt;-x&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$p&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
      &lt;/span&gt;&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s2"&gt;"%s"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$p&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
      &lt;span class="k"&gt;return &lt;/span&gt;0
    &lt;span class="k"&gt;fi
  done

  return &lt;/span&gt;1
&lt;span class="o"&gt;}&lt;/span&gt;

get_windsurf_active_paths&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;py&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nt"&gt;-x&lt;/span&gt; &lt;span class="s2"&gt;"/usr/bin/python3"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nv"&gt;py&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"/usr/bin/python3"&lt;/span&gt;
  &lt;span class="k"&gt;elif &lt;/span&gt;&lt;span class="nb"&gt;command&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; python3 &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;/dev/null 2&amp;gt;&amp;amp;1&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nv"&gt;py&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;command&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; python3&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="k"&gt;else
    return &lt;/span&gt;0
  &lt;span class="k"&gt;fi&lt;/span&gt;

  &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$py&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; - &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="no"&gt;PY&lt;/span&gt;&lt;span class="sh"&gt;'
from pathlib import Path
import re

base = Path.home() / '.codeium' / 'windsurf' / 'code_tracker' / 'active'
if not base.exists():
    raise SystemExit(0)

paths = []
for f in base.rglob('*_mcp.json'):
    try:
        b = f.read_bytes()
    except Exception:
        continue

    for m in re.finditer(br'file:///[^&lt;/span&gt;&lt;span class="se"&gt;\x&lt;/span&gt;&lt;span class="sh"&gt;00&lt;/span&gt;&lt;span class="se"&gt;\s&lt;/span&gt;&lt;span class="sh"&gt;"]+', b):
        u = m.group(0).decode('utf-8', 'ignore')
        if u.startswith('file:///'):
            paths.append(u[7:])

seen = set()
for p in paths:
    if p in seen:
        continue
    seen.add(p)
    print(p)
&lt;/span&gt;&lt;span class="no"&gt;PY
&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;

collect_candidate_start_dirs&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;candidates&lt;/span&gt;&lt;span class="o"&gt;=()&lt;/span&gt;

  candidates+&lt;span class="o"&gt;=(&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$PWD&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;

  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;SITE_PATH&lt;/span&gt;&lt;span class="k"&gt;:-}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;candidates+&lt;span class="o"&gt;=(&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SITE_PATH&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;fi

  &lt;/span&gt;&lt;span class="nb"&gt;local &lt;/span&gt;p
  &lt;span class="k"&gt;while &lt;/span&gt;&lt;span class="nv"&gt;IFS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;read&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; p&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$p&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; candidates+&lt;span class="o"&gt;=(&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$p&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;done&lt;/span&gt; &amp;lt; &amp;lt;&lt;span class="o"&gt;(&lt;/span&gt;get_windsurf_active_paths 2&amp;gt;/dev/null &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;

  &lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s2"&gt;"%s&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;candidates&lt;/span&gt;&lt;span class="p"&gt;[@]&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'NF &amp;amp;&amp;amp; !seen[$0]++'&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="nv"&gt;start_dir&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PWD&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nv"&gt;artisan_dir&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;

&lt;span class="nv"&gt;check_mode&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0
&lt;span class="nv"&gt;root_arg&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;1&lt;/span&gt;&lt;span class="k"&gt;:-}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"--check"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
  &lt;/span&gt;&lt;span class="nv"&gt;check_mode&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1
  &lt;span class="nb"&gt;shift
&lt;/span&gt;&lt;span class="k"&gt;fi

if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;1&lt;/span&gt;&lt;span class="k"&gt;:-}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
  &lt;/span&gt;&lt;span class="nv"&gt;root_arg&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="nb"&gt;shift
&lt;/span&gt;&lt;span class="k"&gt;fi

if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;root_arg&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;root_arg&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\$&lt;/span&gt;&lt;span class="s2"&gt;{workspaceFolder}"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;root_arg&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;workspaceFolder&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;root_arg&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\$&lt;/span&gt;&lt;span class="s2"&gt;{workspaceRoot}"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;root_arg&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;workspaceRoot&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
  if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;root_arg&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nv"&gt;artisan_dir&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;find_artisan_dir &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;root_arg&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; 2&amp;gt;/dev/null &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="k"&gt;fi
fi

if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nt"&gt;-z&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;artisan_dir&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;LARAVEL_PROJECT_ROOT&lt;/span&gt;&lt;span class="k"&gt;:-}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
  if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nv"&gt;artisan_dir&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;find_artisan_dir &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;LARAVEL_PROJECT_ROOT&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; 2&amp;gt;/dev/null&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"laravel-boost MCP: LARAVEL_PROJECT_ROOT set but artisan not found."&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&amp;amp;2
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"- LARAVEL_PROJECT_ROOT: &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;LARAVEL_PROJECT_ROOT&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&amp;amp;2
    &lt;span class="nb"&gt;exit &lt;/span&gt;1
  &lt;span class="k"&gt;fi
fi

if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nt"&gt;-z&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;artisan_dir&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
  while &lt;/span&gt;&lt;span class="nv"&gt;IFS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;read&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; candidate&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
    if &lt;/span&gt;&lt;span class="nv"&gt;artisan_dir&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;find_artisan_dir &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$candidate&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; 2&amp;gt;/dev/null&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
      &lt;/span&gt;&lt;span class="nb"&gt;break
    &lt;/span&gt;&lt;span class="k"&gt;fi
  done&lt;/span&gt; &amp;lt; &amp;lt;&lt;span class="o"&gt;(&lt;/span&gt;collect_candidate_start_dirs&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;fi

if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nt"&gt;-z&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$artisan_dir&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
  &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"laravel-boost MCP: skipped (no Laravel project detected; artisan not found)."&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&amp;amp;2
  &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"- starting directory: &lt;/span&gt;&lt;span class="nv"&gt;$start_dir&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&amp;amp;2
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;SITE_PATH&lt;/span&gt;&lt;span class="k"&gt;:-}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"- SITE_PATH: &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;SITE_PATH&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&amp;amp;2
  &lt;span class="k"&gt;fi
  &lt;/span&gt;&lt;span class="nb"&gt;exit &lt;/span&gt;0
&lt;span class="k"&gt;fi

if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;artisan_dir&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/vendor/laravel/boost"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
  &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"laravel-boost MCP: skipped (Boost not installed in this project)."&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&amp;amp;2
  &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"- artisan_dir: &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;artisan_dir&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&amp;amp;2
  &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Tip: run 'composer require laravel/boost --dev' in this project."&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&amp;amp;2
  &lt;span class="nb"&gt;exit &lt;/span&gt;0
&lt;span class="k"&gt;fi

&lt;/span&gt;&lt;span class="nv"&gt;php_bin&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nv"&gt;php_bin&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;find_php&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
  &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"laravel-boost MCP: php binary not found in PATH or common locations."&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&amp;amp;2
  &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Ensure PHP is installed and available to your IDE (Herd/Homebrew)."&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&amp;amp;2
  &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="k"&gt;fi

if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;check_mode&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"1"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
  &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"laravel-boost MCP wrapper check"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&amp;amp;2
  &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"- starting directory: &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;start_dir&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&amp;amp;2
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;root_arg&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"- root_arg: &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;root_arg&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&amp;amp;2
  &lt;span class="k"&gt;fi
  if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;LARAVEL_PROJECT_ROOT&lt;/span&gt;&lt;span class="k"&gt;:-}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"- LARAVEL_PROJECT_ROOT: &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;LARAVEL_PROJECT_ROOT&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&amp;amp;2
  &lt;span class="k"&gt;fi
  if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;SITE_PATH&lt;/span&gt;&lt;span class="k"&gt;:-}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"- SITE_PATH: &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;SITE_PATH&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&amp;amp;2
  &lt;span class="k"&gt;fi
  &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"- artisan_dir: &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;artisan_dir&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&amp;amp;2
  &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"- php_bin: &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;php_bin&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&amp;amp;2
  &lt;span class="nv"&gt;herd_bin&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="nb"&gt;command&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; herd &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;/dev/null 2&amp;gt;&amp;amp;1&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nv"&gt;herd_bin&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;command&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; herd&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="k"&gt;fi
  if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;herd_bin&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"- herd_bin: &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;herd_bin&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&amp;amp;2
    &lt;span class="nv"&gt;herd_php&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;artisan_dir&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; herd which-php 2&amp;gt;/dev/null &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;herd_php&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
      &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"- herd_which_php: &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;herd_php&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&amp;amp;2
    &lt;span class="k"&gt;fi
  else
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"- herd_bin: &amp;lt;not found&amp;gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&amp;amp;2
  &lt;span class="k"&gt;fi
  &lt;/span&gt;&lt;span class="nb"&gt;exit &lt;/span&gt;0
&lt;span class="k"&gt;fi

&lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$artisan_dir&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="nb"&gt;command&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; herd &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;/dev/null 2&amp;gt;&amp;amp;1&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
  &lt;/span&gt;&lt;span class="nb"&gt;exec &lt;/span&gt;herd php artisan boost:mcp
&lt;span class="k"&gt;fi

&lt;/span&gt;&lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$php_bin&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; artisan boost:mcp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Add Herd MCP to Windsurf (macOS, Linux, Windows notes)
&lt;/h2&gt;

&lt;p&gt;If you use &lt;strong&gt;Laravel Herd&lt;/strong&gt;, you can also add &lt;strong&gt;Herd’s MCP server&lt;/strong&gt; to Windsurf.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Herd MCP does
&lt;/h2&gt;

&lt;p&gt;Herd MCP provides tools around Herd’s local dev environment (sites, PHP versions, etc.).&lt;/p&gt;

&lt;h2&gt;
  
  
  Important OS note
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;macOS:&lt;/strong&gt; Fully supported.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Windows:&lt;/strong&gt; Fully supported.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Linux:&lt;/strong&gt; Herd is not typically available, so you can skip this section.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 1: Create a Herd wrapper script
&lt;/h2&gt;

&lt;p&gt;Create a file at:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;macOS: &lt;code&gt;~/.codeium/herd-mcp.sh&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Make it executable:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;chmod&lt;/span&gt; +x ~/.codeium/herd-mcp.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 2: Add the &lt;code&gt;herd&lt;/code&gt; server in Windsurf MCP config
&lt;/h2&gt;

&lt;p&gt;Edit:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;macOS: &lt;code&gt;~/.codeium/windsurf/mcp_config.json&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Add (or update) this entry inside &lt;code&gt;mcpServers&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"mcpServers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"herd"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"bash"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"args"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"/Users/&amp;lt;you&amp;gt;/.codeium/herd-mcp.sh"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"${workspaceFolder}"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"disabled"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"env"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Why run it with &lt;code&gt;bash&lt;/code&gt;?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;On macOS, some security/xattr quirks can cause direct execution errors.&lt;/li&gt;
&lt;li&gt;Running via &lt;code&gt;bash &amp;lt;script&amp;gt;&lt;/code&gt; tends to be more reliable inside GUI apps.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Windows Herd MCP (no wrapper required)
&lt;/h3&gt;

&lt;p&gt;On Windows, Herd installs its MCP PHAR under:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;%USERPROFILE%\.config\herd\bin\herd-mcp.phar&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can add it directly in your MCP config (Windsurf Editor or the JetBrains plugin config file, depending on what you use):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"mcpServers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"herd"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"php"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"args"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"%USERPROFILE%/.config/herd/bin/herd-mcp.phar"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"env"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"SITE_PATH"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"YOUR-SITE-PATH"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 3: Restart/refresh MCP servers
&lt;/h2&gt;

&lt;p&gt;In Windsurf MCP settings, click &lt;strong&gt;Refresh&lt;/strong&gt; (or restart Windsurf).&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4: Sanity check
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;~/.codeium/herd-mcp.sh &lt;span class="nt"&gt;--check&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;detected project root (exported as &lt;code&gt;SITE_PATH&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;php binary used&lt;/li&gt;
&lt;li&gt;Herd MCP phar path&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Final macOS Herd wrapper script (multi-project safe)
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;Save as: &lt;code&gt;~/.codeium/herd-mcp.sh&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;

&lt;span class="c"&gt;# A robust wrapper to start Herd MCP in Windsurf.&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;# Why this exists:&lt;/span&gt;
&lt;span class="c"&gt;# - Windsurf may launch MCP servers from a directory that is NOT your project.&lt;/span&gt;
&lt;span class="c"&gt;# - Herd MCP benefits from knowing the project root via SITE_PATH.&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;# What this script does:&lt;/span&gt;
&lt;span class="c"&gt;# 1) Finds a Laravel project root by searching upwards for an `artisan` file.&lt;/span&gt;
&lt;span class="c"&gt;# 2) Falls back to SITE_PATH if provided.&lt;/span&gt;
&lt;span class="c"&gt;# 3) Optionally uses Windsurf trackers to guess your active workspace.&lt;/span&gt;
&lt;span class="c"&gt;# 4) Finds a usable PHP binary (PATH first, then common locations like Herd).&lt;/span&gt;
&lt;span class="c"&gt;# 5) Runs Herd's MCP PHAR with SITE_PATH exported.&lt;/span&gt;

&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-euo&lt;/span&gt; pipefail

find_artisan_dir&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="nb"&gt;local dir&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="k"&gt;while &lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
    if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$dir&lt;/span&gt;&lt;span class="s2"&gt;/artisan"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
      &lt;/span&gt;&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s2"&gt;"%s"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$dir&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
      &lt;span class="k"&gt;return &lt;/span&gt;0
    &lt;span class="k"&gt;fi

    &lt;/span&gt;&lt;span class="nb"&gt;local &lt;/span&gt;parent
    &lt;span class="nv"&gt;parent&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;dirname&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$dir&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$parent&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$dir&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
      return &lt;/span&gt;1
    &lt;span class="k"&gt;fi
    &lt;/span&gt;&lt;span class="nb"&gt;dir&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$parent&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="k"&gt;done&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

find_php&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="nb"&gt;command&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; php &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;/dev/null 2&amp;gt;&amp;amp;1&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;command&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; php
    &lt;span class="k"&gt;return &lt;/span&gt;0
  &lt;span class="k"&gt;fi

  &lt;/span&gt;&lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;candidates&lt;/span&gt;&lt;span class="o"&gt;=(&lt;/span&gt;
    &lt;span class="s2"&gt;"/opt/homebrew/bin/php"&lt;/span&gt;
    &lt;span class="s2"&gt;"/usr/local/bin/php"&lt;/span&gt;
    &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/Library/Application Support/Herd/bin/php"&lt;/span&gt;
    &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/Library/Application Support/Herd/bin/php82"&lt;/span&gt;
  &lt;span class="o"&gt;)&lt;/span&gt;

  &lt;span class="nb"&gt;local &lt;/span&gt;p
  &lt;span class="k"&gt;for &lt;/span&gt;p &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;candidates&lt;/span&gt;&lt;span class="p"&gt;[@]&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
    if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nt"&gt;-x&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$p&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
      &lt;/span&gt;&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s2"&gt;"%s"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$p&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
      &lt;span class="k"&gt;return &lt;/span&gt;0
    &lt;span class="k"&gt;fi
  done

  return &lt;/span&gt;1
&lt;span class="o"&gt;}&lt;/span&gt;

get_windsurf_active_paths&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;py&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nt"&gt;-x&lt;/span&gt; &lt;span class="s2"&gt;"/usr/bin/python3"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nv"&gt;py&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"/usr/bin/python3"&lt;/span&gt;
  &lt;span class="k"&gt;elif &lt;/span&gt;&lt;span class="nb"&gt;command&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; python3 &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;/dev/null 2&amp;gt;&amp;amp;1&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nv"&gt;py&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;command&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; python3&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="k"&gt;else
    return &lt;/span&gt;0
  &lt;span class="k"&gt;fi&lt;/span&gt;

  &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$py&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; - &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="no"&gt;PY&lt;/span&gt;&lt;span class="sh"&gt;'
from pathlib import Path
import re

base = Path.home() / '.codeium' / 'windsurf' / 'code_tracker' / 'active'
if not base.exists():
    raise SystemExit(0)

paths = []
for f in base.rglob('*_mcp.json'):
    try:
        b = f.read_bytes()
    except Exception:
        continue

    for m in re.finditer(br'file:///[&lt;/span&gt;&lt;span class="se"&gt;\x&lt;/span&gt;&lt;span class="sh"&gt;21-&lt;/span&gt;&lt;span class="se"&gt;\x&lt;/span&gt;&lt;span class="sh"&gt;7E]+', b):
        u = m.group(0).decode('utf-8', 'ignore')
        if u.startswith('file:///'):
            paths.append(u[7:])

seen = set()
for p in paths:
    if p in seen:
        continue
    seen.add(p)
    print(p)
&lt;/span&gt;&lt;span class="no"&gt;PY
&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;

find_herd_mcp_phar&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;candidates&lt;/span&gt;&lt;span class="o"&gt;=(&lt;/span&gt;
    &lt;span class="s2"&gt;"/Applications/Herd.app/Contents/Resources/herd-mcp.phar"&lt;/span&gt;
  &lt;span class="o"&gt;)&lt;/span&gt;

  &lt;span class="nb"&gt;local &lt;/span&gt;p
  &lt;span class="k"&gt;for &lt;/span&gt;p &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;candidates&lt;/span&gt;&lt;span class="p"&gt;[@]&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
    if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$p&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
      &lt;/span&gt;&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s2"&gt;"%s"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$p&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
      &lt;span class="k"&gt;return &lt;/span&gt;0
    &lt;span class="k"&gt;fi
  done

  return &lt;/span&gt;1
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="nv"&gt;start_dir&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PWD&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nv"&gt;project_dir&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;

&lt;span class="nv"&gt;check_mode&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0
&lt;span class="nv"&gt;root_arg&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;1&lt;/span&gt;&lt;span class="k"&gt;:-}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"--check"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
  &lt;/span&gt;&lt;span class="nv"&gt;check_mode&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1
  &lt;span class="nb"&gt;shift
&lt;/span&gt;&lt;span class="k"&gt;fi

if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;1&lt;/span&gt;&lt;span class="k"&gt;:-}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
  &lt;/span&gt;&lt;span class="nv"&gt;root_arg&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="nb"&gt;shift
&lt;/span&gt;&lt;span class="k"&gt;fi

if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;root_arg&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;root_arg&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\$&lt;/span&gt;&lt;span class="s2"&gt;{workspaceFolder}"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;root_arg&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;workspaceFolder&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;root_arg&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\$&lt;/span&gt;&lt;span class="s2"&gt;{workspaceRoot}"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;root_arg&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;workspaceRoot&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
  if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;root_arg&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nv"&gt;project_dir&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;find_artisan_dir &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;root_arg&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; 2&amp;gt;/dev/null &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="k"&gt;fi
fi

if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nt"&gt;-z&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;project_dir&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;LARAVEL_PROJECT_ROOT&lt;/span&gt;&lt;span class="k"&gt;:-}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
  if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nv"&gt;project_dir&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;find_artisan_dir &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;LARAVEL_PROJECT_ROOT&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; 2&amp;gt;/dev/null&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"herd MCP: LARAVEL_PROJECT_ROOT set but artisan not found."&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&amp;amp;2
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"- LARAVEL_PROJECT_ROOT: &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;LARAVEL_PROJECT_ROOT&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&amp;amp;2
    &lt;span class="nb"&gt;exit &lt;/span&gt;1
  &lt;span class="k"&gt;fi
fi

if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nt"&gt;-z&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;project_dir&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
  while &lt;/span&gt;&lt;span class="nv"&gt;IFS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;read&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; candidate&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
    if &lt;/span&gt;&lt;span class="nv"&gt;project_dir&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;find_artisan_dir &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$candidate&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; 2&amp;gt;/dev/null&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
      &lt;/span&gt;&lt;span class="nb"&gt;break
    &lt;/span&gt;&lt;span class="k"&gt;fi
  done&lt;/span&gt; &amp;lt; &amp;lt;&lt;span class="o"&gt;(&lt;/span&gt;collect_candidate_start_dirs&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;fi

if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nt"&gt;-z&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$project_dir&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
  &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"herd MCP: could not find artisan."&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&amp;amp;2
  &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"- starting directory: &lt;/span&gt;&lt;span class="nv"&gt;$start_dir&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&amp;amp;2
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;SITE_PATH&lt;/span&gt;&lt;span class="k"&gt;:-}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"- SITE_PATH: &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;SITE_PATH&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&amp;amp;2
  &lt;span class="k"&gt;fi
  &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Tip: Open a Laravel project folder (or set SITE_PATH to the project root) and try again."&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&amp;amp;2
  &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="k"&gt;fi

&lt;/span&gt;&lt;span class="nv"&gt;php_bin&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nv"&gt;php_bin&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;find_php&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
  &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"herd MCP: php binary not found in PATH or common locations."&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&amp;amp;2
  &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="k"&gt;fi

&lt;/span&gt;&lt;span class="nv"&gt;herd_phar&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nv"&gt;herd_phar&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;find_herd_mcp_phar&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
  &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"herd MCP: herd-mcp.phar not found."&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&amp;amp;2
  &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Tip: Ensure Laravel Herd is installed, or update find_herd_mcp_phar() with your Herd path."&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&amp;amp;2
  &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="k"&gt;fi

if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;check_mode&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"1"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
  &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"herd MCP wrapper check"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&amp;amp;2
  &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"- starting directory: &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;start_dir&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&amp;amp;2
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;root_arg&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"- root_arg: &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;root_arg&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&amp;amp;2
  &lt;span class="k"&gt;fi
  if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;LARAVEL_PROJECT_ROOT&lt;/span&gt;&lt;span class="k"&gt;:-}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"- LARAVEL_PROJECT_ROOT: &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;LARAVEL_PROJECT_ROOT&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&amp;amp;2
  &lt;span class="k"&gt;fi
  if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;SITE_PATH&lt;/span&gt;&lt;span class="k"&gt;:-}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"- SITE_PATH (env): &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;SITE_PATH&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&amp;amp;2
  &lt;span class="k"&gt;fi
  &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"- SITE_PATH (resolved): &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;project_dir&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&amp;amp;2
  &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"- php_bin: &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;php_bin&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&amp;amp;2
  &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"- herd_phar: &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;herd_phar&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&amp;amp;2
  &lt;span class="nb"&gt;exit &lt;/span&gt;0
&lt;span class="k"&gt;fi

&lt;/span&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;SITE_PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;project_dir&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$php_bin&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$herd_phar&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Combined &lt;code&gt;mcp_config.json&lt;/code&gt; example (Boost + Herd)
&lt;/h2&gt;

&lt;p&gt;This is how your MCP config can look with both servers configured (same schema, different file location depending on Windsurf Editor vs JetBrains plugin):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"mcpServers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"laravel-boost"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/Users/&amp;lt;you&amp;gt;/.codeium/laravel-boost-mcp.sh"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"args"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"${workspaceFolder}"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"disabled"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"env"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"herd"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"bash"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"args"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"/Users/&amp;lt;you&amp;gt;/.codeium/herd-mcp.sh"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"${workspaceFolder}"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"disabled"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"env"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Windows Setup (PowerShell)
&lt;/h2&gt;

&lt;p&gt;Windsurf on Windows can also launch MCP servers from an unexpected working directory. The fix is the same idea: use a wrapper.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: Create the wrapper script
&lt;/h2&gt;

&lt;p&gt;Create:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;C:\Users\&amp;lt;you&amp;gt;\.codeium\laravel-boost-mcp.ps1&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 2: Configure Windsurf MCP to run the script
&lt;/h2&gt;

&lt;p&gt;Edit:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;C:\Users\&amp;lt;you&amp;gt;\.codeium\windsurf\mcp_config.json&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Add:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"mcpServers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"laravel-boost"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"powershell.exe"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"args"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"-NoProfile"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"-ExecutionPolicy"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"Bypass"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"-File"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"C:&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;Users&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;you&amp;gt;&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;.codeium&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;laravel-boost-mcp.ps1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"${workspaceFolder}"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 3: Restart/refresh MCP servers in Windsurf
&lt;/h2&gt;

&lt;p&gt;Refresh MCP servers (or restart Windsurf).&lt;/p&gt;




&lt;h2&gt;
  
  
  Final Windows PowerShell wrapper script (well-commented)
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;Save as: &lt;code&gt;C:\Users\&amp;lt;you&amp;gt;\.codeium\laravel-boost-mcp.ps1&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="cm"&gt;&amp;lt;#
A robust wrapper to start Laravel Boost MCP in Windsurf on Windows.

Why this exists:
- Windsurf may start MCP servers from a non-project directory.
- `php artisan boost:mcp` fails if it can’t find `artisan`.

What this script does:
1) Finds a Laravel project root by walking up directories looking for `artisan`.
2) Falls back to SITE_PATH if provided.
3) Finds a usable php binary.
4) Runs `php artisan boost:mcp` inside the detected project.

Usage:
- Check mode:
  .\laravel-boost-mcp.ps1 --check
#&amp;gt;&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="kr"&gt;param&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Parameter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ValueFromRemainingArguments&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;$true&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[]]&lt;/span&gt;&lt;span class="nv"&gt;$Args&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="kr"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;Find-ArtisanDir&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;$StartDir&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nv"&gt;$dir&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Resolve-Path&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$StartDir&lt;/span&gt;&lt;span class="w"&gt;

  &lt;/span&gt;&lt;span class="kr"&gt;while&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;$true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="kr"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Test-Path&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Join-Path&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$dir&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'artisan'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="kr"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$dir&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

    &lt;/span&gt;&lt;span class="nv"&gt;$parent&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Split-Path&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$dir&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Parent&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="kr"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$parent&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-eq&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$dir&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="kr"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="bp"&gt;$null&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

    &lt;/span&gt;&lt;span class="nv"&gt;$dir&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$parent&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="kr"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;Find-Php&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="c"&gt;# Prefer php on PATH&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nv"&gt;$php&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Get-Command&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;php&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-ErrorAction&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;SilentlyContinue&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="kr"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$php&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$php&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Source&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

  &lt;/span&gt;&lt;span class="c"&gt;# Common candidates&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nv"&gt;$candidates&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;@(&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"C:\\Program Files\\PHP\\php.exe"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"C:\\PHP\\php.exe"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;

  &lt;/span&gt;&lt;span class="kr"&gt;foreach&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$c&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$candidates&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="kr"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Test-Path&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$c&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$c&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

  &lt;/span&gt;&lt;span class="kr"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="bp"&gt;$null&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="nv"&gt;$startDir&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Get-Location&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nv"&gt;$artisanDir&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="bp"&gt;$null&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nv"&gt;$checkMode&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="bp"&gt;$false&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nv"&gt;$rootArg&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="bp"&gt;$null&lt;/span&gt;&lt;span class="w"&gt;

 &lt;/span&gt;&lt;span class="c"&gt;# Support: --check [&amp;lt;workspaceFolder&amp;gt;]&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="kr"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$Args&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Count&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-gt&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-and&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$Args&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-eq&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'--check'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nv"&gt;$checkMode&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="bp"&gt;$true&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="kr"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$Args&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Count&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-gt&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
     &lt;/span&gt;&lt;span class="nv"&gt;$Args&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$Args&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$Args&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Count&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;else&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
     &lt;/span&gt;&lt;span class="nv"&gt;$Args&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;@()&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

 &lt;/span&gt;&lt;span class="kr"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$Args&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Count&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-gt&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nv"&gt;$rootArg&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$Args&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

 &lt;/span&gt;&lt;span class="c"&gt;# 1) If Windsurf passed the project path explicitly (via ${workspaceFolder}), use it first&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="kr"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$rootArg&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-and&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$rootArg&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-ne&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'${workspaceFolder}'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-and&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Test-Path&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$rootArg&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nv"&gt;$artisanDir&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Find-ArtisanDir&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$rootArg&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

 &lt;/span&gt;&lt;span class="c"&gt;# 2) Try from current directory&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="kr"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-not&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$artisanDir&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nv"&gt;$artisanDir&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Find-ArtisanDir&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$startDir&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

 &lt;/span&gt;&lt;span class="c"&gt;# 3) Fallback to SITE_PATH if provided&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="kr"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-not&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$artisanDir&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-and&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$&lt;/span&gt;&lt;span class="nn"&gt;env&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;SITE_PATH&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nv"&gt;$artisanDir&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Find-ArtisanDir&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$&lt;/span&gt;&lt;span class="nn"&gt;env&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;SITE_PATH&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="kr"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-not&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$artisanDir&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;Write-Error&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"laravel-boost MCP: could not find artisan. Starting directory: &lt;/span&gt;&lt;span class="nv"&gt;$startDir&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="kr"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$&lt;/span&gt;&lt;span class="nn"&gt;env&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;SITE_PATH&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Write-Error&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"SITE_PATH: &lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nv"&gt;$&lt;/span&gt;&lt;span class="nn"&gt;env&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;SITE_PATH&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="kr"&gt;exit&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="nv"&gt;$phpBin&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Find-Php&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nx"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-not&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$phpBin&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;Write-Error&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"laravel-boost MCP: php binary not found. Ensure PHP is installed and available in PATH."&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="kr"&gt;exit&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

 &lt;/span&gt;&lt;span class="kr"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$checkMode&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="n"&gt;Write-Host&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"laravel-boost MCP wrapper check"&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="n"&gt;Write-Host&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"- starting directory: &lt;/span&gt;&lt;span class="nv"&gt;$startDir&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="kr"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$rootArg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Write-Host&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"- root_arg: &lt;/span&gt;&lt;span class="nv"&gt;$rootArg&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="kr"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$&lt;/span&gt;&lt;span class="nn"&gt;env&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;SITE_PATH&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Write-Host&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"- SITE_PATH: &lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nv"&gt;$&lt;/span&gt;&lt;span class="nn"&gt;env&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;SITE_PATH&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="n"&gt;Write-Host&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"- artisan_dir: &lt;/span&gt;&lt;span class="nv"&gt;$artisanDir&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="n"&gt;Write-Host&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"- php_bin: &lt;/span&gt;&lt;span class="nv"&gt;$phpBin&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="kr"&gt;exit&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="n"&gt;Set-Location&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$artisanDir&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$phpBin&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;artisan&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;boost:mcp&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Troubleshooting
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Symptom&lt;/th&gt;
&lt;th&gt;Likely cause&lt;/th&gt;
&lt;th&gt;Fix&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Could not open input file: ./artisan&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;MCP server started outside project root&lt;/td&gt;
&lt;td&gt;Use the wrapper script (it finds &lt;code&gt;artisan&lt;/code&gt; by walking up directories)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;“Cannot initialize server” never changes&lt;/td&gt;
&lt;td&gt;Edited the wrong MCP config file&lt;/td&gt;
&lt;td&gt;Verify the config path; use the temporary “invalid command” test above&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MCP starts, but Boost tools are missing&lt;/td&gt;
&lt;td&gt;Boost not installed in that project&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;composer require laravel/boost --dev&lt;/code&gt; in the target project&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;php artisan boost:mcp&lt;/code&gt; fails in terminal&lt;/td&gt;
&lt;td&gt;Laravel app can’t boot&lt;/td&gt;
&lt;td&gt;Fix the underlying Laravel error first (env/config/extensions)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;Wrong PHP version / missing extensions&lt;/td&gt;
&lt;td&gt;Windsurf uses PATH PHP, not project PHP&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;Wrong project info returned (multi-project)&lt;/td&gt;
&lt;td&gt;MCP server reused across projects, or wrapper guessed the wrong root&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;Auto-detection doesn’t work&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;python3&lt;/code&gt; not available&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Quick checks
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Run &lt;code&gt;~/.codeium/laravel-boost-mcp.sh --check&lt;/code&gt; and confirm the resolved &lt;code&gt;artisan_dir&lt;/code&gt; is the correct project.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  - If you use Herd, confirm &lt;code&gt;herd which-php&lt;/code&gt; matches what you expect in that directory.
&lt;/h2&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Windsurf MCP servers can fail if started from the wrong working directory.&lt;/li&gt;
&lt;li&gt;A wrapper script that finds &lt;code&gt;artisan&lt;/code&gt; and a working &lt;code&gt;php&lt;/code&gt; makes Boost reliable.&lt;/li&gt;
&lt;li&gt;Configure Windsurf to run the wrapper instead of &lt;code&gt;php artisan boost:mcp&lt;/code&gt; directly.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What this teaches us about AI tooling (meta takeaway)
&lt;/h2&gt;

&lt;p&gt;This isn’t unique to Windsurf or Boost.&lt;br&gt;
Any AI tool that shells out to a project-specific CLI will eventually hit the same class of issues:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The tool assumes &lt;code&gt;cwd&lt;/code&gt; is the project root.&lt;/li&gt;
&lt;li&gt;The tool assumes the runtime (PHP, Node, Python, etc.) matches the project.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The durable solution is the same pattern you used here: create an adaptive entrypoint that can &lt;strong&gt;find the project&lt;/strong&gt;, &lt;strong&gt;select the right runtime&lt;/strong&gt;, and &lt;strong&gt;print diagnostics&lt;/strong&gt; when things go wrong.&lt;/p&gt;

&lt;p&gt;If this helped, consider sharing your wrapper suggestions and or improvements back to the community.&lt;/p&gt;

</description>
      <category>laravel</category>
      <category>windsurf</category>
      <category>php</category>
      <category>mcp</category>
    </item>
    <item>
      <title>How to Build Applications That Survive the Real World</title>
      <dc:creator>Olusola Ojewunmi</dc:creator>
      <pubDate>Mon, 15 Dec 2025 09:12:11 +0000</pubDate>
      <link>https://dev.to/ojsholly/how-to-build-applications-that-survive-the-real-world-omg</link>
      <guid>https://dev.to/ojsholly/how-to-build-applications-that-survive-the-real-world-omg</guid>
      <description>&lt;h1&gt;
  
  
  A Practical Guide to Production-Ready Engineering (Using Laravel for Illustrative Examples)
&lt;/h1&gt;

&lt;p&gt;Most applications don't fail because the code is wrong. They fail because the supporting guardrails—the unsexy, invisible, operational foundations—were never built.&lt;/p&gt;

&lt;p&gt;I've seen it happen more times than I'd like to admit: a team ships a beautiful feature, users love it, and then—without warning—everything falls apart. Not because of a bug in the login flow, but because nobody thought about what happens when the database fills up, or when a third-party API changes its response format overnight.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"Production ready" is not an engineering badge, it's a survival strategy.&lt;/strong&gt; It means your application can survive the chaos of the real world: traffic spikes, disk failures, malicious actors, and the inevitable 3 AM emergency.&lt;/p&gt;

&lt;p&gt;This article is a practical checklist of the non-functional requirements that separate hobby projects from production-grade systems. While the concepts apply to any language or framework, I've included &lt;strong&gt;Laravel&lt;/strong&gt; examples to show how to implement them effectively.&lt;/p&gt;




&lt;h2&gt;
  
  
  TL;DR: The 10 Production Essentials
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;✅ Logging &amp;amp; Error Monitoring&lt;/li&gt;
&lt;li&gt;✅ Backups &amp;amp; Disaster Recovery&lt;/li&gt;
&lt;li&gt;✅ Queue Management &amp;amp; Job Failures&lt;/li&gt;
&lt;li&gt;✅ Caching Strategy&lt;/li&gt;
&lt;li&gt;✅ Security Hygiene&lt;/li&gt;
&lt;li&gt;✅ Encrypting Sensitive Data&lt;/li&gt;
&lt;li&gt;✅ Environment Configuration &amp;amp; Secrets&lt;/li&gt;
&lt;li&gt;✅ Monitoring &amp;amp; Observability&lt;/li&gt;
&lt;li&gt;✅ Automated Testing&lt;/li&gt;
&lt;li&gt;✅ Deployment &amp;amp; CI/CD&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  1. Logging &amp;amp; Error Monitoring
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The Principle
&lt;/h3&gt;

&lt;p&gt;Running a production application without proper logging is like flying a plane without instruments. You can't troubleshoot what you can't see, and you can't prevent what you can't detect.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvd7hhs2piusqd30bbdy5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvd7hhs2piusqd30bbdy5.png" alt=" " width="800" height="339"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Logging is your application's black box recorder. When a user says &lt;em&gt;"it's broken,"&lt;/em&gt; your logs should tell you &lt;strong&gt;exactly why&lt;/strong&gt;, without you needing to reproduce it blindly.&lt;/p&gt;

&lt;h3&gt;
  
  
  Laravel Implementation Example
&lt;/h3&gt;

&lt;p&gt;Laravel makes structured logging straightforward. You have two main approaches:&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Option A: Contextual Manual Logging&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;Don't just log text; log context.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Illuminate\Support\Facades\Log&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$payment&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;PaymentGateway&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;charge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$order&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Exception&lt;/span&gt; &lt;span class="nv"&gt;$e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;Log&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Payment processing failed'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="s1"&gt;'order_id'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$order&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'amount'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$order&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;total&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'error'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$e&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getMessage&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="s1"&gt;'user_id'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="p"&gt;]);&lt;/span&gt;

    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nv"&gt;$e&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  &lt;strong&gt;Option B: Clean Global Error Handling (Laravel 11+)&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;Define global exception reporting rules in &lt;code&gt;bootstrap/app.php&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;// bootstrap/app.php&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Illuminate\Foundation\Configuration\Exceptions&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Application&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;configure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;basePath&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;dirname&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;__DIR__&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;withExceptions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Exceptions&lt;/span&gt; &lt;span class="nv"&gt;$exceptions&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$exceptions&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;report&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;PaymentFailedException&lt;/span&gt; &lt;span class="nv"&gt;$e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nc"&gt;Log&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;critical&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Payment Failure detected'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
                &lt;span class="s1"&gt;'details'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$e&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getMessage&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
            &lt;span class="p"&gt;]);&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Pro Tip:&lt;/strong&gt; Don't rely on logs alone. Consider:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Sentry&lt;/li&gt;
&lt;li&gt;Honeybadger&lt;/li&gt;
&lt;li&gt;Rollbar&lt;/li&gt;
&lt;li&gt;Laravel Nightwatch&lt;/li&gt;
&lt;li&gt;Laravel Pulse &lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  2. Backups &amp;amp; Disaster Recovery
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The Principle
&lt;/h3&gt;

&lt;p&gt;Backups are not a "nice to have"—they are the difference between a recoverable mistake and a company-ending event.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;An untested backup is not a backup; it is a false sense of security.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Hard drives fail. Developers delete the wrong table. Hackers encrypt data. If you cannot restore your system within one hour, you do &lt;strong&gt;not&lt;/strong&gt; have a disaster recovery plan.&lt;/p&gt;

&lt;h3&gt;
  
  
  Laravel Implementation Example
&lt;/h3&gt;

&lt;p&gt;Use &lt;code&gt;spatie/laravel-backup&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;// app/Console/Kernel.php&lt;/span&gt;
&lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;schedule&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Schedule&lt;/span&gt; &lt;span class="nv"&gt;$schedule&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$schedule&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;command&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'backup:run'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;daily&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;at&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'01:00'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nv"&gt;$schedule&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;command&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'backup:monitor'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;daily&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;at&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'03:00'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Quarterly drill:&lt;/strong&gt; Restore your system from scratch. If you haven't tested your restore… assume it's broken.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. Queue Management &amp;amp; Job Failures
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The Principle
&lt;/h3&gt;

&lt;p&gt;Queues handle emails, reports, and long-running tasks. When misconfigured, they become silent saboteurs.&lt;/p&gt;

&lt;p&gt;Common failure modes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Workers die — jobs pile up&lt;/li&gt;
&lt;li&gt;Third-party outage — retry storms&lt;/li&gt;
&lt;li&gt;Slow jobs — queue starvation&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Laravel Implementation Example
&lt;/h3&gt;

&lt;p&gt;Use &lt;strong&gt;Laravel Horizon&lt;/strong&gt; for Redis queues.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Illuminate\Contracts\Queue\ShouldQueue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Illuminate\Support\Facades\Log&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Illuminate\Support\Facades\Notification&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Throwable&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ProcessOrderNotification&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;ShouldQueue&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nv"&gt;$tries&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nv"&gt;$backoff&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;900&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt; &lt;span class="c1"&gt;// 1m, 5m, 15m&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;failed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Throwable&lt;/span&gt; &lt;span class="nv"&gt;$exception&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;Log&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Order notification failed permanently'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="s1"&gt;'order_id'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'error'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$exception&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getMessage&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="p"&gt;]);&lt;/span&gt;

        &lt;span class="nc"&gt;Notification&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'slack'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'SLACK_WEBHOOK'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
            &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;notify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;JobFailedNotification&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$exception&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  4. Caching Strategy
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The Principle
&lt;/h3&gt;

&lt;p&gt;Caching is not optional — it is a &lt;strong&gt;scalability prerequisite&lt;/strong&gt;. Without it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Databases melt&lt;/li&gt;
&lt;li&gt;Response times degrade&lt;/li&gt;
&lt;li&gt;Infrastructure costs explode&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Laravel Implementation Example
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$stats&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Cache&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;remember&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'dashboard-stats'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3600&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="s1"&gt;'total_revenue'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Order&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'total'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="s1"&gt;'active_users'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'active'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nb"&gt;count&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Invalidate when data changes&lt;/span&gt;
&lt;span class="nc"&gt;Cache&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;forget&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'dashboard-stats'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A slow system is a broken system.&lt;/p&gt;




&lt;h2&gt;
  
  
  5. Security Hygiene
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The Principle
&lt;/h3&gt;

&lt;p&gt;Security is a mindset. Most breaches come from basic oversights:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Unvalidated input&lt;/li&gt;
&lt;li&gt;Exposed &lt;code&gt;.env&lt;/code&gt; files&lt;/li&gt;
&lt;li&gt;Unsafe SQL&lt;/li&gt;
&lt;li&gt;Mass assignment mistakes&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Laravel Implementation Example
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;// SAFE&lt;/span&gt;
&lt;span class="nv"&gt;$users&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;DB&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s1"&gt;'SELECT * FROM users WHERE email = :email'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'email'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// UNSAFE&lt;/span&gt;
&lt;span class="nv"&gt;$users&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;DB&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s2"&gt;"SELECT * FROM users WHERE email = '&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;'"&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  6. Encrypting Sensitive Data
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The Principle
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fltdt0z2rcm42h9hoc26e.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fltdt0z2rcm42h9hoc26e.png" alt=" " width="800" height="446"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Some data is so sensitive that even if your database leaks, attackers should learn nothing.&lt;/p&gt;

&lt;p&gt;Examples:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;BVN&lt;/li&gt;
&lt;li&gt;NIN&lt;/li&gt;
&lt;li&gt;Passport numbers&lt;/li&gt;
&lt;li&gt;Bank account numbers&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Laravel Implementation Example
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserProfile&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Model&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="nv"&gt;$casts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="s1"&gt;'bvn'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'encrypted'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'passport_number'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'encrypted'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'bank_details'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'encrypted:array'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Laravel handles encryption and decryption automatically.&lt;/p&gt;




&lt;h2&gt;
  
  
  7. Environment Configuration &amp;amp; Secrets
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The Principle
&lt;/h3&gt;

&lt;p&gt;Leaking credentials is one of the fastest ways to destroy a business.&lt;/p&gt;

&lt;p&gt;Never:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Commit &lt;code&gt;.env&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Hardcode secrets&lt;/li&gt;
&lt;li&gt;Share production keys in Slack/WhatsApp&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Laravel Implementation Example
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;php artisan config:cache
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Enforce required variables:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;boot&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;app&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'production'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nf"&gt;env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'STRIPE_SECRET'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nf"&gt;env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'DB_PASSWORD'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Critical environment variables are missing!'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  8. Monitoring &amp;amp; Observability
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The Principle
&lt;/h3&gt;

&lt;p&gt;Monitoring is your system's heartbeat. Without it, failures remain invisible until customers complain.&lt;/p&gt;

&lt;p&gt;I learned this the hard way.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftsiuw9x193dizi2fq9jx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftsiuw9x193dizi2fq9jx.png" alt=" " width="800" height="446"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  A Real Incident: Ignoring Alerts Has a Price
&lt;/h3&gt;

&lt;p&gt;I set up daily Honeybadger health checks for our Laravel API. Every midnight, I received a "200 OK" email. It became routine — background noise.&lt;/p&gt;

&lt;p&gt;One night at 3 AM, I woke up, saw multiple Honeybadger emails, assumed they were noise, and went back to sleep.&lt;/p&gt;

&lt;p&gt;A couple of minutes after 8 AM, during our morning standup, the CEO joined the call and dropped a statement developers everywhere dread hearing:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;"The entire app is down."&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Our server had crashed hours earlier, presumably very late during the previous night. We scrambled to migrate the database and redeploy under pressure.&lt;/p&gt;

&lt;p&gt;Only later did I re-check the ignored emails—they were warnings that the app was &lt;strong&gt;unreachable&lt;/strong&gt;, not success notifications.&lt;/p&gt;




&lt;h3&gt;
  
  
  Two lessons carved into my memory:
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Silence is not safety.&lt;/strong&gt; Daily health checks matter—make them impossible to ignore.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Alerts require action.&lt;/strong&gt; If an alert doesn't require action, delete it.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Laravel Implementation Example
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;// routes/web.php&lt;/span&gt;
&lt;span class="nc"&gt;Route&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'/health'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Spatie\Health\Health&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;check&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Connect &lt;code&gt;/health&lt;/code&gt; to an uptime monitor (UptimeRobot, Oh Dear).&lt;/p&gt;




&lt;h2&gt;
  
  
  9. Automated Testing
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The Principle
&lt;/h3&gt;

&lt;p&gt;Untested code is legacy code the moment you write it. Tests are not about perfection—they're about &lt;strong&gt;confidence&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;You cannot deploy with confidence if you don't know what will break.&lt;/p&gt;

&lt;p&gt;Common excuses (and why they're wrong):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;"We don't have time"&lt;/em&gt; — You'll spend 10x more time debugging production&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;"Tests slow us down"&lt;/em&gt; — Manual testing is slower and less reliable&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;"Our code is too complex to test"&lt;/em&gt; — That's a design problem, not a testing problem&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Laravel Implementation Example
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Feature Tests (Test Critical Paths)&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;// tests/Feature/OrderFlowTest.php&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;OrderFlowTest&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;TestCase&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;test_user_can_complete_checkout&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;factory&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="nv"&gt;$product&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Product&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;factory&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;'price'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

        &lt;span class="nv"&gt;$response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;actingAs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'/orders'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
                &lt;span class="s1"&gt;'product_id'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$product&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="s1"&gt;'quantity'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;]);&lt;/span&gt;

        &lt;span class="nv"&gt;$response&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;assertStatus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;201&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;assertDatabaseHas&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'orders'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="s1"&gt;'user_id'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'total'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;]);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;test_checkout_fails_with_insufficient_stock&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$product&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Product&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;factory&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;'stock'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

        &lt;span class="nv"&gt;$response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;actingAs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;factory&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
            &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'/orders'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
                &lt;span class="s1"&gt;'product_id'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$product&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="s1"&gt;'quantity'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;]);&lt;/span&gt;

        &lt;span class="nv"&gt;$response&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;assertStatus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;422&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nv"&gt;$response&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;assertJsonValidationErrors&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'quantity'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;What to Test (Priority Order)&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Critical business flows&lt;/strong&gt; — Checkout, payments, authentication&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Data integrity&lt;/strong&gt; — Money calculations, user permissions, data validation&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Third-party integrations&lt;/strong&gt; — API failures, timeouts, fallback behavior&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Edge cases&lt;/strong&gt; — Empty carts, expired sessions, invalid input&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Run Tests in CI/CD&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# GitHub Actions&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Run Tests&lt;/span&gt;
  &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;php artisan test --parallel&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The Golden Rule:&lt;/strong&gt; If it breaks in production, write a test for it.&lt;/p&gt;




&lt;h2&gt;
  
  
  10. Deployment &amp;amp; CI/CD
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The Principle
&lt;/h3&gt;

&lt;p&gt;Manual deployments fail. CI/CD ensures safe, predictable, repeatable releases.&lt;/p&gt;

&lt;p&gt;If your deployment involves SSH + &lt;code&gt;git pull&lt;/code&gt;, you're one command away from an outage.&lt;/p&gt;

&lt;h3&gt;
  
  
  Laravel Implementation Example
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# GitHub Actions Snippet&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deploy to Production&lt;/span&gt;
  &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
    &lt;span class="s"&gt;ssh user@server 'cd /var/www/app &amp;amp;&amp;amp; git pull origin main'&lt;/span&gt;
    &lt;span class="s"&gt;ssh user@server 'cd /var/www/app &amp;amp;&amp;amp; composer install --no-dev'&lt;/span&gt;
    &lt;span class="s"&gt;ssh user@server 'cd /var/www/app &amp;amp;&amp;amp; php artisan migrate --force'&lt;/span&gt;
    &lt;span class="s"&gt;ssh user@server 'cd /var/www/app &amp;amp;&amp;amp; php artisan config:cache'&lt;/span&gt;
    &lt;span class="s"&gt;ssh user@server 'cd /var/www/app &amp;amp;&amp;amp; php artisan queue:restart'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Summary: Building for the Real World
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1dbbiwnbffn41xj1rrzg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1dbbiwnbffn41xj1rrzg.png" alt=" " width="800" height="339"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Production readiness isn't about adding complexity—it's about adding &lt;strong&gt;reliability&lt;/strong&gt;. Every item on this checklist exists because someone, somewhere, learned a painful lesson the hard way.&lt;/p&gt;

&lt;p&gt;Modern frameworks like Laravel have made production readiness easier than ever.&lt;/p&gt;

&lt;p&gt;With a few packages and the right discipline, you can build systems that are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Resilient&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Observable&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Secure&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Maintainable&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Professional&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  A Practical Adoption Path
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Week 1:&lt;/strong&gt; Logging &amp;amp; Monitoring&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Week 2:&lt;/strong&gt; Backups&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Week 3:&lt;/strong&gt; Queue Management&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Week 4:&lt;/strong&gt; Security &amp;amp; Encryption&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Week 5:&lt;/strong&gt; Health Checks&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Week 6:&lt;/strong&gt; Automated Testing&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Week 7:&lt;/strong&gt; CI/CD Pipeline&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The Real Cost of Neglect
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;No backups — permanent data loss&lt;/li&gt;
&lt;li&gt;No monitoring — silent failures&lt;/li&gt;
&lt;li&gt;No encryption — possible breach of confidential user data and lawsuits&lt;/li&gt;
&lt;li&gt;No queue handling — stuck orders&lt;/li&gt;
&lt;li&gt;No testing — production bugs and regressions&lt;/li&gt;
&lt;li&gt;No CI/CD — broken deployments&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The time you invest in production readiness pays dividends every single day your application runs. It's the difference between a side project and a sustainable business. Between a stressful on-call rotation and peaceful weekends. Between "it works" and "it works reliably."&lt;/p&gt;

&lt;h3&gt;
  
  
  Your Application Deserves Better
&lt;/h3&gt;

&lt;p&gt;If you're building something that matters—something people depend on, something that generates revenue, something that stores user data—you owe it to yourself and your users to build it right.&lt;/p&gt;

&lt;p&gt;Start today. Your future self will thank you.&lt;/p&gt;




&lt;p&gt;💬 &lt;strong&gt;Discussion Questions:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Which of these 10 items do you struggle with most?&lt;/li&gt;
&lt;li&gt;Have you had a production incident that taught you a hard lesson?&lt;/li&gt;
&lt;li&gt;What's your #1 production-readiness tip?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Drop your thoughts in the comments—I read and respond to every one.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Found this helpful? Follow me for more practical guides on building production-grade applications. Have questions about implementing any of these practices? Ask below!&lt;/em&gt;&lt;/p&gt;

</description>
      <category>laravel</category>
      <category>devops</category>
      <category>webdev</category>
      <category>backend</category>
    </item>
    <item>
      <title>AI-Assisted Engineering: A Senior Developer’s Framework for Speed, Quality, and Sound Technical Judgment - Part 2</title>
      <dc:creator>Olusola Ojewunmi</dc:creator>
      <pubDate>Mon, 01 Dec 2025 07:36:45 +0000</pubDate>
      <link>https://dev.to/ojsholly/ai-assisted-engineering-a-senior-developers-framework-for-speed-quality-and-sound-technical-168o</link>
      <guid>https://dev.to/ojsholly/ai-assisted-engineering-a-senior-developers-framework-for-speed-quality-and-sound-technical-168o</guid>
      <description>&lt;p&gt;&lt;strong&gt;Executive Overview&lt;/strong&gt;&lt;br&gt;
In &lt;a href="https://dev.to/ojsholly/ai-assisted-engineering-a-senior-developers-framework-for-speed-quality-and-sound-technical-15nh"&gt;Part 1&lt;/a&gt;, we covered the “thinking layer”— how AI improves architectural reasoning. Now we shift to the &lt;strong&gt;execution layer&lt;/strong&gt;: how AI improves daily coding, testing, refactoring, debugging, and delivery.&lt;/p&gt;

&lt;p&gt;Over the last few months of production work, this workflow consistently delivered:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✔️ &lt;strong&gt;~70% time savings&lt;/strong&gt; on code scaffolding&lt;/li&gt;
&lt;li&gt;✔️ &lt;strong&gt;~78% faster&lt;/strong&gt; test generation&lt;/li&gt;
&lt;li&gt;✔️ &lt;strong&gt;~67% faster&lt;/strong&gt; debugging cycles&lt;/li&gt;
&lt;li&gt;✔️ &lt;strong&gt;More consistent&lt;/strong&gt; Laravel architecture&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is the practical playbook behind those numbers.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. AI-Accelerated Code Scaffolding
&lt;/h2&gt;

&lt;p&gt;AI excels at generating structured, consistent scaffolds—if you provide a strong, detailed and precise specification.&lt;/p&gt;

&lt;h3&gt;
  
  
  Service Classes (83% Faster)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Traditional:&lt;/strong&gt; 60–75 minutes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AI-assisted:&lt;/strong&gt; 10–15 minutes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;My Prompt Strategy:&lt;/strong&gt;&lt;br&gt;
I use &lt;strong&gt;Windsurf&lt;/strong&gt; combined with &lt;strong&gt;Laravel Boost&lt;/strong&gt; (to enforce modern Laravel patterns) and provide a prompt like this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Create a &lt;code&gt;SeasonalDiscountService&lt;/code&gt; that retrieves active discount rules from config/discounts.php.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Requirements:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Methods: &lt;code&gt;getDiscountForCategory(string $category)&lt;/code&gt;, &lt;code&gt;getActiveCampaigns()&lt;/code&gt;, &lt;code&gt;calculateImpact()&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Use dependency injection and full type hints.&lt;/li&gt;
&lt;li&gt;Handle expired campaigns gracefully with defaults.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Constraint:&lt;/strong&gt; Return a strictly typed &lt;code&gt;DiscountDTO&lt;/code&gt;, never a raw array."&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;The Result:&lt;/strong&gt; A production-ready service class with proper namespaces, docblocks, and error handling. I step in only to validate the business logic.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. AI-Enhanced Refactoring
&lt;/h2&gt;

&lt;p&gt;Refactoring legacy code is safer with AI — but requires synchronization.&lt;/p&gt;

&lt;h3&gt;
  
  
  The “Explain → Verify → Execute” Loop
&lt;/h3&gt;

&lt;p&gt;When dealing with complex, "spaghetti" legacy logic, I never jump straight to refactoring. I use a synchronization step first:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Explain:&lt;/strong&gt; I highlight the complex method in Windsurf or sometimes the whole flow of execution of the feature, and ask:
&amp;gt; &lt;em&gt;"Explain the logic of the method or feature flow step-by-step."&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Verify &amp;amp; Correct:&lt;/strong&gt; I review the AI's explanation. If it misinterprets a variable or edge case, or omits some part of the logic, I correct it explicitly in the chat.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Execute:&lt;/strong&gt; Only once I have confirmed the AI's mental model matches the existing implementation, do I issue the refactor command, while adding constraints where necessary.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  The "Reverse Code Review"
&lt;/h3&gt;

&lt;p&gt;Sometimes I flip the script. I will manually refactor the AI's code (or existing legacy code) to apply a specific design pattern or personal preference, and then I ask the AI to review &lt;em&gt;my&lt;/em&gt; work.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Prompt:&lt;/strong&gt;&lt;br&gt;
"I have refactored your logic to use the &lt;code&gt;match&lt;/code&gt; expression instead of the &lt;code&gt;switch&lt;/code&gt; statement. Review my changes for regressions, missed edge cases, or logic errors. Then, merge the best parts of my refactor with your original error handling."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;The Result:&lt;/strong&gt; The AI acts as a safety net. It often catches subtle bugs I introduced (like missing a null check during my refactor) and upgrades the final code by combining my architectural structure with its robust boilerplate.&lt;/p&gt;

&lt;h3&gt;
  
  
  The "Hardcoded to Config" Move
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Prompt:&lt;/strong&gt;&lt;br&gt;
"Extract all hardcoded rate limits, API timeouts, and retry thresholds in this controller into &lt;code&gt;config/discounts.php&lt;/code&gt;. Make them environment-variable configurable."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Real stats:&lt;/strong&gt; I recently moved 20+ hardcoded values in 20 minutes. A task that usually takes around 1 hour of tedious copy-pasting, which is also prone to errors of omission sometimes.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fds7orhpkxwuj8smk5969.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fds7orhpkxwuj8smk5969.png" alt=" " width="800" height="336"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  3. Testing: A Force Multiplier
&lt;/h2&gt;

&lt;p&gt;Testing is usually the first thing to slip when deadlines loom. AI reverses that trend when utilised properly.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Traditional:&lt;/strong&gt; 120 minutes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AI-assisted:&lt;/strong&gt; 25 minutes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Time saved:&lt;/strong&gt; ~79%&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Generating the Suite
&lt;/h3&gt;

&lt;p&gt;I don't write tests from scratch anymore. I ask AI to generate the skeleton and the standard cases:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Create unit tests for &lt;code&gt;LoyaltyPointsCalculator&lt;/code&gt; covering:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Gold Tier:&lt;/strong&gt; Earns 2x points on all orders.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Silver Tier:&lt;/strong&gt; Earns 1.5x points only on weekends (mock the date).&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Standard Tier:&lt;/strong&gt; Earns 1x points.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Edge Case:&lt;/strong&gt; Orders over $1,000 get a flat 500-point bonus regardless of tier."&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  The "Blind Spot" Check
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"What edge cases am I missing for this points calculation?"&lt;/em&gt;&lt;br&gt;
&lt;strong&gt;AI Result:&lt;/strong&gt; "You missed: Refunded orders (negative points), zero-dollar orders, and floating point precision errors on multiplier math."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Running Tests With AI
&lt;/h3&gt;

&lt;p&gt;I also use AI to run my test suite.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;It &lt;strong&gt;picks up failed test cases&lt;/strong&gt; automatically.&lt;/li&gt;
&lt;li&gt;It proceeds to &lt;strong&gt;investigate the cause&lt;/strong&gt; of the failures and &lt;strong&gt;recommends multiple solutions&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;After &lt;strong&gt;my review and decision&lt;/strong&gt;, it implements the fixes and reruns the suite.&lt;/li&gt;
&lt;li&gt;It &lt;strong&gt;automatically runs the test suite&lt;/strong&gt; after significant refactoring to ensure no regressions.&lt;/li&gt;
&lt;li&gt;It can also suggest additional test cases for &lt;strong&gt;improved test coverage&lt;/strong&gt; for the codebase. &lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  4. Documentation: The Silent Productivity Killer
&lt;/h2&gt;

&lt;p&gt;Documentation is the task every engineer hates, but every team needs. AI turns this from a chore into a breeze.&lt;/p&gt;

&lt;p&gt;AI allows me to effectively document code and applications, making handovers and onboarding new devs effortless compared to manual effort.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;My Documentation Workflow:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Code Comments:&lt;/strong&gt; "Add strict DocBlocks to this service class explaining the business rules for the discount calculation."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Onboarding:&lt;/strong&gt; "Read this repository and generate a &lt;code&gt;CONTRIBUTING.md&lt;/code&gt; file that explains how to set up the environment, run migrations, and seed the database."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;API Docs:&lt;/strong&gt; "Generate Swagger/OpenAPI definitions for these 3 new endpoints."&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Result:&lt;/strong&gt; I consolidated 31 fragmented documentation files into 1 comprehensive README in 30 minutes, saving ~2 hours of manual work.&lt;/p&gt;




&lt;h2&gt;
  
  
  5. Infrastructure &amp;amp; Deployment: The DevOps Accelerator
&lt;/h2&gt;

&lt;p&gt;I am a Backend Engineer, not a full-time DevOps specialist. AI helps bridge that gap by generating, explaining, and debugging deployment scripts and also build errors that I would otherwise spend hours Googling.&lt;/p&gt;

&lt;h3&gt;
  
  
  Writing &amp;amp; Learning
&lt;/h3&gt;

&lt;p&gt;Instead of copying opaque Bash scripts from StackOverflow, I now use AI to build and &lt;strong&gt;teach me&lt;/strong&gt; the infrastructure.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Prompt:&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Write a GitHub Actions workflow to deploy this Laravel app to a DigitalOcean Droplet via SSH.&lt;br&gt;
&lt;strong&gt;Requirements:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Run tests before deploying.&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;rsync&lt;/code&gt; for zero-downtime file transfer.&lt;/li&gt;
&lt;li&gt;Reload PHP-FPM after transfer.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Explain:&lt;/strong&gt; Add comments explaining what every flag in the &lt;code&gt;rsync&lt;/code&gt; command actually does."&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;The Result:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; A working CI/CD pipeline.&lt;/li&gt;
&lt;li&gt; A clear explanation of why we use &lt;code&gt;rsync -avz&lt;/code&gt; (archive mode, verbose, compressed).&lt;/li&gt;
&lt;li&gt; I learn &lt;em&gt;while&lt;/em&gt; I build, rather than just copy-pasting.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I apply this same logic to &lt;strong&gt;Dockerfiles&lt;/strong&gt; (optimising layers), &lt;strong&gt;Nginx configs&lt;/strong&gt;, and &lt;strong&gt;Makefiles&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  6. Debugging: Ranked Root Cause Analysis
&lt;/h2&gt;

&lt;p&gt;Stop staring at stack traces. Use AI to structure your diagnostic thinking.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;My Debugging Workflow (15-20 mins):&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Input:&lt;/strong&gt; Error message + Code Context + Recent Changes.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Prompt:&lt;/strong&gt;
&amp;gt; "Based on this error, provide 3 likely root causes &lt;strong&gt;ranked by probability&lt;/strong&gt;. For each, suggest a verification step."&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Action:&lt;/strong&gt; I test the "70% likely" cause first.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  7. The Data: Real World Time Savings
&lt;/h2&gt;

&lt;p&gt;Let's look at the actual numbers from 12 months of production work.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Task Type&lt;/th&gt;
&lt;th&gt;Traditional Time&lt;/th&gt;
&lt;th&gt;With AI&lt;/th&gt;
&lt;th&gt;Time Saved&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Service Class&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;60 min&lt;/td&gt;
&lt;td&gt;10 min&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;83%&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Test Suite&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;120 min&lt;/td&gt;
&lt;td&gt;25 min&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;79%&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Documentation&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;120 min&lt;/td&gt;
&lt;td&gt;30 min&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;75%&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Debugging&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;60 min&lt;/td&gt;
&lt;td&gt;20 min&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;67%&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Refactoring&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;90 min&lt;/td&gt;
&lt;td&gt;30 min&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;67%&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;The Takeaway:&lt;/strong&gt; This results in a &lt;strong&gt;25-30% net productivity gain&lt;/strong&gt; per engineer. For a team of 5, that’s the equivalent of adding a 6th engineer without the headcount.&lt;/p&gt;




&lt;h2&gt;
  
  
  8. My Multi-AI Prompt Strategy
&lt;/h2&gt;

&lt;p&gt;The secret to high-quality output is a two-step process:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;The "Thinking" Step (ChatGPT/Gemini):&lt;/strong&gt; I dump my messy requirements here. "I need to implement a new rewards tier..." I ask the AI to structure this into a technical spec.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;The "Execution" Step (Windsurf + Laravel Boost):&lt;/strong&gt; I feed the refined, structured spec into Windsurf. It executes with full codebase awareness and Laravel-specific context.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;The Rule:&lt;/strong&gt; Invest 5 minutes in prompt design to save 30 minutes of cleanup.&lt;/p&gt;

&lt;h3&gt;
  
  
  The "No Assumptions" Clause
&lt;/h3&gt;

&lt;p&gt;I always explicitly instruct Windsurf:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;"Do not make assumptions. If any part of this requirement is ambiguous or open to interpretation, ask clarifying questions before you generate any code."&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This simple instruction prevents the AI from confidently guessing wrong and forces a clarification loop that saves hours of rewriting later.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Seniority Gap: Asking for Features vs. Asking for Architecture
&lt;/h3&gt;

&lt;p&gt;One of the clearest indicators of seniority isn't just the code you write—it's the prompt you write.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The "Junior" Prompt:&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Build a registration form with first name, last name, email, phone number, and address."&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The Result:&lt;/strong&gt; Happy-path code. It works, but it’s fragile. It likely misses validation and lacks atomicity.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;The "Senior" Prompt:&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Create a user registration endpoint.&lt;br&gt;
&lt;strong&gt;Technical Constraints:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Validation:&lt;/strong&gt; Enforce strict typing. DNS/RFC validation on production.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Data Integrity:&lt;/strong&gt; Wrap the user creation and profile setup in a database transaction to ensure efficient rollback.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Performance:&lt;/strong&gt; Email uniqueness check via optimized indexed query.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security:&lt;/strong&gt; Sanitise inputs to prevent XSS and mass-assignment."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Result:&lt;/strong&gt; Enterprise-grade code that is secure, atomic, and environment-aware.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  9. Guardrails: Where I Draw the Line
&lt;/h2&gt;

&lt;p&gt;AI speeds up execution, but &lt;strong&gt;I own the quality.&lt;/strong&gt; Here is my safety checklist:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Security:&lt;/strong&gt; I never blindly trust AI with auth logic or input validation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;N+1 Queries:&lt;/strong&gt; AI loves to write inefficient loops. I always review database interactions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Business Logic:&lt;/strong&gt; AI can write the &lt;em&gt;code&lt;/em&gt;, but only I know if the Loyalty logic matches the Marketing team's request.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Secrets:&lt;/strong&gt; Never paste API keys or real customer data into the prompt.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Closing Thoughts: The Future of Hybrid Engineering
&lt;/h2&gt;

&lt;p&gt;Teams that master structured AI workflows will ship faster, maintain higher quality, and onboard engineers quickly. Hybrid AI-assisted engineering isn’t the future—it’s the present.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Meta Note:&lt;/strong&gt; In true "practice what you preach" fashion, this article’s structure was refined using the same AI-assisted workflow described above: structured prompts, multi-AI refinement, and final human review.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;(Now, if only I could prompt the AI to explain to my PM why this "5-minute fix" will actually take three days... ☕)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Which part of your engineering workflow would you delegate to AI first — and what’s stopping you from doing it today?&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;About the Author&lt;/strong&gt;&lt;br&gt;
Senior Backend Engineer specialising in Laravel, distributed systems, and backend architecture. Focused on scalable systems and hybrid AI-assisted engineering workflows.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Read Part 1:&lt;/strong&gt; &lt;a href="https://dev.to/ojsholly/ai-assisted-engineering-a-senior-developers-framework-for-speed-quality-and-sound-technical-15nh"&gt;Decision-Making, Architecture, and Problem Solving&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>productivity</category>
      <category>softwaredevelopment</category>
      <category>promptengineering</category>
    </item>
    <item>
      <title>AI-Assisted Engineering: A Senior Developer’s Framework for Speed, Quality, and Sound Technical Judgment</title>
      <dc:creator>Olusola Ojewunmi</dc:creator>
      <pubDate>Mon, 24 Nov 2025 06:57:50 +0000</pubDate>
      <link>https://dev.to/ojsholly/ai-assisted-engineering-a-senior-developers-framework-for-speed-quality-and-sound-technical-15nh</link>
      <guid>https://dev.to/ojsholly/ai-assisted-engineering-a-senior-developers-framework-for-speed-quality-and-sound-technical-15nh</guid>
      <description>&lt;p&gt;&lt;strong&gt;Part 1: Decision-Making, Architecture, and Problem Solving&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Executive Summary&lt;/strong&gt;&lt;br&gt;
Modern software engineering has changed forever. The question isn’t &lt;em&gt;whether&lt;/em&gt; to use AI—it’s &lt;em&gt;how&lt;/em&gt; intentionally you use it while preserving engineering judgment.&lt;/p&gt;

&lt;p&gt;As a senior backend engineer working with Laravel and distributed systems, I’ve spent the past couple of months developing a structured AI-assisted workflow. The outcome?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✔️ &lt;strong&gt;70% faster execution&lt;/strong&gt; on delegated tasks&lt;/li&gt;
&lt;li&gt;✔️ &lt;strong&gt;Zero compromise&lt;/strong&gt; on architectural or business logic integrity&lt;/li&gt;
&lt;li&gt;✔️ &lt;strong&gt;Cleaner design decisions&lt;/strong&gt; backed by structured reasoning&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This framework is not about replacing human engineers. It’s about establishing a &lt;strong&gt;hybrid model&lt;/strong&gt; where AI accelerates mechanical execution, and engineers lead architecture, decision-making, and correctness.&lt;/p&gt;

&lt;h3&gt;
  
  
  I Learned This the Hard Way
&lt;/h3&gt;

&lt;p&gt;I wasn’t always this disciplined. When I first started using AI, I treated it like a magic wand. I pushed code to production blindly—without rigorous verification.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;I paid dearly for it.&lt;/strong&gt;&lt;br&gt;
I introduced expensive N+1 queries that choked the DB, pushed logic that defied our business rules, and deployed raw, unverified code that caused regression. I learned that &lt;strong&gt;blind trust is expensive.&lt;/strong&gt; Gradually, I refined my approach from "AI-generated" to "AI-assisted", moving from a passive user to an active architect.&lt;/p&gt;

&lt;p&gt;This article explains the &lt;strong&gt;thinking layer&lt;/strong&gt; of that refined workflow.&lt;/p&gt;




&lt;h2&gt;
  
  
  My Multi-AI Workflow Philosophy
&lt;/h2&gt;

&lt;p&gt;I rely on a three-tier system:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. The Thinking Layer: ChatGPT + Google Gemini
&lt;/h3&gt;

&lt;p&gt;These tools are where my ideas take shape. I use them to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Clarify requirements&lt;/li&gt;
&lt;li&gt;Structure technical thoughts&lt;/li&gt;
&lt;li&gt;Stress-test assumptions&lt;/li&gt;
&lt;li&gt;Explore multiple architectural approaches&lt;/li&gt;
&lt;li&gt;Generate high-signal prompts for execution&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The "Prompt Engineering" Gap:&lt;/strong&gt;&lt;br&gt;
This is often where the gap between junior and senior engineers becomes visible.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A &lt;strong&gt;junior engineer&lt;/strong&gt; might skip this layer and ask the IDE directly: &lt;em&gt;"Build a registration form."&lt;/em&gt; The AI will guess the requirements, usually resulting in fragile, "happy-path" code.&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;senior engineer&lt;/strong&gt; uses this Thinking Layer to generate a &lt;strong&gt;specification&lt;/strong&gt; first. I ask ChatGPT: &lt;em&gt;"Draft the strict technical requirements for a production-grade registration system, considering security, atomicity, and validation."&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The output of this layer:&lt;/strong&gt; ➡️ A clean, structured, context-rich prompt.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. The Execution Layer: Windsurf (Cascade) + Laravel Boost
&lt;/h3&gt;

&lt;p&gt;Once my thinking is refined, I move to &lt;strong&gt;Windsurf&lt;/strong&gt; (or Cursor/Copilot).&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Windsurf:&lt;/strong&gt; Provides full codebase awareness, stateful context, and implementation precision.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Laravel Boost:&lt;/strong&gt; I pair this with specific prompts to enforce modern Laravel patterns, respect service container conventions, and produce clean DTOs.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This pairing ensures speed &lt;strong&gt;AND&lt;/strong&gt; correctness.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. The Review Layer: Copilot + Gemini
&lt;/h3&gt;

&lt;p&gt;These tools are used &lt;em&gt;after&lt;/em&gt; the implementation. I use them as a "second pair of eyes" to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Review PRs:&lt;/strong&gt; Highlight potentially problematic changes on varying levels of complexity.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Simulate:&lt;/strong&gt; Run mental simulations of how the implementation handles race conditions or high load.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Refine:&lt;/strong&gt; Suggest logical improvements or optimizations I might have missed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Automate:&lt;/strong&gt; Generate suitable, descriptive commit messages based on staged files.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This layer acts as my &lt;strong&gt;quality assurance gate&lt;/strong&gt; before I even push the code.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;(And yes—I still write “please” and “thank you” in my prompts. You know… just in case the AI uprising ever happens 😄.)&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  How AI Enhances Architectural Decision-Making
&lt;/h2&gt;

&lt;p&gt;Good architecture requires exploring multiple solutions, assessing trade-offs, and understanding constraints. Here is my 6-step architectural workflow.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Present Requirements with Context
&lt;/h3&gt;

&lt;p&gt;The quality of the architectural output is capped by the quality of your context.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The "Junior" Prompt:&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"How should I build a pricing system?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;Result:&lt;/em&gt; Generic, brittle CRUD advice.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The "Senior" Prompt:&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"I need to redesign our pricing system.&lt;br&gt;
&lt;strong&gt;Constraints:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Current State:&lt;/strong&gt; Database-stored logic that is brittle.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Requirements:&lt;/strong&gt; Role-based pricing, configurable thresholds.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Load:&lt;/strong&gt; 10K+ daily orders; read-heavy.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Stack:&lt;/strong&gt; Team experienced with Laravel Service patterns."&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;Result:&lt;/em&gt; Robust patterns (Pipeline/Strategy) tailored to the stack.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Step 2: Request Multiple Architectural Options
&lt;/h3&gt;

&lt;p&gt;I always ask:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Give me 3 distinct architectural approaches."&lt;br&gt;
AI usually proposes:&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ol&gt;
&lt;li&gt; Pipeline architecture&lt;/li&gt;
&lt;li&gt; Strategy pattern&lt;/li&gt;
&lt;li&gt; Rules engine&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This expands the solution space immediately.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: Ask for a Trade-Off Matrix
&lt;/h3&gt;

&lt;p&gt;Next, I request a comparison.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Compare options by &lt;strong&gt;testability&lt;/strong&gt;, &lt;strong&gt;extensibility&lt;/strong&gt;, &lt;strong&gt;performance&lt;/strong&gt;, &lt;strong&gt;maintainability&lt;/strong&gt;, and &lt;strong&gt;migration complexity&lt;/strong&gt;."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This gives me a clearer, multi-dimensional view.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4: Validate Against Real Constraints
&lt;/h3&gt;

&lt;p&gt;AI doesn’t know your team’s strengths or your deployment risks. That’s where senior judgment comes in. I evaluate:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Can my team maintain this?&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Does this introduce fragile dependencies?&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Is the operational overhead worth it?&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Step 5: Break Into Phases
&lt;/h3&gt;

&lt;p&gt;Once I choose an approach:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Produce a 5-phase implementation plan. Each phase must be independently deployable and testable."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This removes uncertainty and helps me review each phase independently, allowing me to detect architectural drift early.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 6: Make the Final Decision
&lt;/h3&gt;

&lt;p&gt;AI provides insights, not authority. &lt;strong&gt;I decide—and I am accountable.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Complex Problem-Solving: AI as a Debugging Partner
&lt;/h2&gt;

&lt;p&gt;Debugging is where AI shines—not because it “fixes bugs” magically, but because it &lt;strong&gt;structures reasoning&lt;/strong&gt;. My debugging loop now takes 15–20 minutes, not 60–90.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Ranked Diagnostic Workflow
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Provide Context:&lt;/strong&gt; I paste the error, stack trace, relevant code, and recent changes.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;AI Analysis:&lt;/strong&gt;
Based on this error, provide 3 likely root causes &lt;strong&gt;ranked by probability&lt;/strong&gt;. For each, suggest a verification step.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Execution:&lt;/strong&gt; I test the highest-probability cause (e.g., the 70% likely one) first.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Validation:&lt;/strong&gt; I apply the fix and verify.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This prioritization prevents rabbit holes and wasted time.&lt;/p&gt;




&lt;h2&gt;
  
  
  My Acceptance / Rejection Framework
&lt;/h2&gt;

&lt;p&gt;Not all AI output is equal. My mental model for code review:&lt;/p&gt;

&lt;h3&gt;
  
  
  ✅ Immediate Acceptance (Low Risk)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Boilerplate&lt;/li&gt;
&lt;li&gt;DTO scaffolding&lt;/li&gt;
&lt;li&gt;Documentation structure&lt;/li&gt;
&lt;li&gt;Test class skeletons&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  ⚠️ Acceptance After Review (Medium Risk)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Service method logic&lt;/li&gt;
&lt;li&gt;Controllers&lt;/li&gt;
&lt;li&gt;Refactoring suggestions&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Review for:&lt;/em&gt; Business logic correctness and performance.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  ❌ Rejection or Heavy Revision (High Risk)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Complex domain logic&lt;/li&gt;
&lt;li&gt;Authorization/Authentication&lt;/li&gt;
&lt;li&gt;Security-sensitive code&lt;/li&gt;
&lt;li&gt;Database migrations&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Stress-Testing Ideas Before Implementation
&lt;/h2&gt;

&lt;p&gt;One of AI’s hidden superpowers is pre-execution simulation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"What can go wrong?"&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"What failure modes should I expect with this payment design? Consider race conditions and timeouts."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;"Find the Blind Spots"&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"What edge cases am I missing for this coordinate validation?"&lt;br&gt;
&lt;em&gt;AI Result:&lt;/em&gt; Invalid formats, extreme ranges, null/empty cases, injection attempts.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Closing Thoughts: The Hybrid Engineer
&lt;/h2&gt;

&lt;p&gt;The engineers who excel in the next decade won’t be those who avoid AI, nor those who blindly trust it. It will be engineers who combine &lt;strong&gt;clarity, system thinking, and human judgment&lt;/strong&gt; with &lt;strong&gt;AI-assisted speed, structure, and exploration.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AI accelerates execution. You drive decisions.&lt;/strong&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;About the Author&lt;/strong&gt;&lt;br&gt;
Senior Backend Engineer specializing in Laravel, distributed systems, and backend architecture. Focused on scalable systems, clean architecture, and AI-augmented workflows.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🚀 Part 2 Coming Up Next:&lt;/strong&gt; Code Generation, Refactoring, Testing &amp;amp; Delivery Automation.&lt;/p&gt;




</description>
      <category>ai</category>
      <category>productivity</category>
      <category>softwaredevelopment</category>
      <category>promptengineering</category>
    </item>
  </channel>
</rss>
