<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
    <channel>
        <title>Kennedy Mutisya</title>
        <link>mutisyadev.xyz</link>
        <description>Engineering notes by Kennedy Mutisya, CTO and systems engineer in Nairobi</description>
        <lastBuildDate>Sun, 06 Sep 2026 10:22:05 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>Kennedy Mutisya</title>
            <url>mutisyadev.xyz/faviconm.ico</url>
            <link>mutisyadev.xyz</link>
        </image>
        <copyright>All rights reserved 2026</copyright>
        <item>
            <title><![CDATA[The Architecture Review Checklist for Inheriting Legacy Systems]]></title>
            <link>mutisyadev.xyz/articles/architecture-review-checklist-legacy-systems</link>
            <guid>mutisyadev.xyz/articles/architecture-review-checklist-legacy-systems</guid>
            <pubDate>Fri, 03 Jul 2026 00:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>Inheriting a legacy SaaS system as a new engineering leader or principal architect is a bit like being handed the keys to a moving car while someone else is trying to change the tires. You need to keep the business running smoothly and preserve service availability while figuring out what is broken under the hood.</p>
<p>Before making any rash decisions to "rewrite everything" - which is often a career-ending trap - you need a cold, objective assessment of what you actually have.</p>
<p>This checklist is divided into four critical pillars to help you map the land, manage operational risks, and plan your next strategic moves.</p>
<hr>
<h2>1. The Stability &amp; Risk Pillar (Weeks 1-2)</h2>
<p>Your immediate goal is to ensure the system doesn't collapse under your watch. Focus on observability and identifying operational blind spots.</p>
<h3>Telemetry &amp; Observability</h3>
<ul>
<li><strong>Centralized Logging:</strong> Are metrics, logs, and traces centralized in tools like Datadog, New Relic, or Honeycomb, or are engineers still SSH-ing into production servers to read raw log files?</li>
<li><strong>Time-to-Detection (TTD):</strong> Can you accurately pinpoint the root cause of a production failure within 5 minutes?</li>
<li><strong>Alerting Fidelity:</strong> Check the signal-to-noise ratio of existing alerts. If the team is plagued by constant, non-actionable Slack notifications or pager alerts, true emergencies will go unnoticed.</li>
</ul>
<h3>The Deployment Safety Net</h3>
<ul>
<li><strong>CI/CD Pipeline Maturity:</strong> Is there a modern, automated pipeline, or is deployment a manual, stressful event handled by one or two "gatekeeper" engineers?</li>
<li><strong>Rollback Speed:</strong> Can you roll back a bad deployment at the press of a button, or does a rollback require manual database surgery and code cherry-picks?</li>
<li><strong>Pre-production Environments:</strong> Do staging and development environments closely mirror production, or is "testing in production" the default workflow?</li>
</ul>
<h3>Disaster Recovery (DR) Reality Check</h3>
<ul>
<li><strong>The Restore Test:</strong> When was the last time database and file backups were actually restored and tested in an isolated sandbox?</li>
<li><strong>RTO &amp; RPO:</strong> Do you have a verified, SLA-backed Recovery Time Objective (RTO) and Recovery Point Objective (RPO)?</li>
<li><strong>Single Region Vulnerabilities:</strong> Is the infrastructure confined to a single cloud provider zone or region without automatic failover?</li>
</ul>
<h3>Technical Debt &amp; Security Vulnerabilities</h3>
<ul>
<li><strong>Automated Dependency Auditing:</strong> Run tools like Snyk, Dependabot, or Trivy. Legacy systems are notorious for running framework versions that haven't seen a security patch in years.</li>
<li><strong>Secret Management:</strong> Are production secrets, API keys, and certificates securely managed via systems like Vault or AWS Secrets Manager, or are they hardcoded in repositories?</li>
</ul>
<hr>
<h2>2. The Architecture &amp; Scale Pillar (Weeks 3-4)</h2>
<p>Here, you are analyzing how the application handles data, traffic, and state under load.</p>
<h3>Monolith vs. Distributed Footprint</h3>
<ul>
<li><strong>Code Coupling:</strong> Is it a "spaghetti monolith" where changing code in the billing module unexpectedly breaks the reporting engine?</li>
<li><strong>Domain Boundaries:</strong> Are domain boundaries cleanly separated, or is the business logic tightly coupled with database-specific implementations?</li>
</ul>
<h3>Data Integrity &amp; Bottlenecks</h3>
<ul>
<li><strong>Database Single Point of Failure (SPOF):</strong> Is the primary relational database server overwhelmed? Look for missing indexes, bloated tables, and whether the application relies heavily on database-level stored procedures, which are notoriously hard to scale, debug, and version control.</li>
<li><strong>Read/Write Split:</strong> Does the system utilize read replicas to offload reporting and search traffic from the transactional primary?</li>
</ul>
<h3>State Management</h3>
<ul>
<li><strong>Horizontal Scalability:</strong> Does the application scale horizontally, or does it rely on sticky sessions, storing session state on the local server, making true cloud elasticity and autoscaling impossible?</li>
<li><strong>Caching Strategy:</strong> Is caching (Redis, Memcached) implemented as a strategic layer with proper TTLs, or is it used as a band-aid to cover up slow database queries?</li>
</ul>
<h3>API &amp; Integration Contracts</h3>
<ul>
<li><strong>Documentation and Compliance:</strong> Are internal and external APIs versioned and documented using standards like OpenAPI/Swagger, or are you flying blind on what upstream and downstream services expect?</li>
<li><strong>Rate Limiting &amp; Safety:</strong> Is there an API gateway enforcing rate limits to prevent malicious actors or misconfigured integration clients from taking down your API servers?</li>
</ul>
<hr>
<h2>3. The Developer Velocity Pillar (Weeks 5-6)</h2>
<p>A legacy system's worst trait isn't always its bugs; it's how much it slows your team down and destroys developer morale.</p>
<h3>Local Environment Setup</h3>
<ul>
<li><strong>"Time to First Hello World":</strong> Can a newly hired engineer clone the repository and get a working environment running locally, for example using Docker Compose, within a few hours, or does it take weeks of manual configuration and dependency hunting?</li>
<li><strong>Mock Dependencies:</strong> Are external SaaS dependencies, such as Stripe or Twilio, cleanly mocked or sandboxed for local testing?</li>
</ul>
<h3>Test Coverage vs. Test Confidence</h3>
<ul>
<li><strong>Flakiness Index:</strong> Don't just look at the coverage percentage. Are the tests flaky, failing intermittently in CI/CD pipelines?</li>
<li><strong>Execution Time:</strong> Do developers skip running the test suite because it takes 45 minutes to execute? A slow test suite is a bypassed test suite.</li>
</ul>
<h3>Knowledge Silos &amp; Tribal Knowledge</h3>
<ul>
<li><strong>Runbook Availability:</strong> Is the system's runtime architecture documented in an accessible format, or does it live entirely inside the heads of the two longest-tenured engineers?</li>
<li><strong>Bus Factor:</strong> If your lead developer were to leave tomorrow, would the team still be able to deploy and support the system?</li>
</ul>
<hr>
<h2>4. The Financial &amp; Legal Pillar (Ongoing)</h2>
<p>Legacy infrastructure often hides massive, unoptimized cloud spend and compliance liabilities that drain engineering budgets.</p>
<h3>Infrastructure Efficiency</h3>
<ul>
<li><strong>Orphaned Resources:</strong> Are you paying for massive, over-provisioned idle servers, old database snapshots, and unattached storage volumes?</li>
<li><strong>Containerization &amp; Serverless:</strong> Can workloads be migrated to containers or serverless architectures to leverage dynamic scaling and cut cloud spend?</li>
</ul>
<h3>Data Privacy &amp; Compliance</h3>
<ul>
<li><strong>Encryption at Rest &amp; In Transit:</strong> Is customer data encrypted end-to-end, or does it transit plain-text over internal networks?</li>
<li><strong>Regulatory Alignment:</strong> Does the legacy architecture comply with current regulations (GDPR, CCPA, HIPAA, SOC2)? How easy is it to perform a hard delete of user data (Right to be Forgotten)?</li>
</ul>
<h3>Licensing Dead Ends</h3>
<ul>
<li><strong>Commercial Lock-in:</strong> Are you tied to expensive, proprietary legacy database or operating system licenses? Can these be migrated to open-source alternatives like PostgreSQL or Linux?</li>
</ul>
<hr>
<h2>The Assessment Matrix</h2>
<p>To help prioritize your roadmap, run every major component of the inherited system through this quick triage matrix:</p>
<table><thead><tr><th>Architectural Health</th><th>Business Value</th><th>Action Plan</th></tr></thead><tbody><tr><td><strong>High</strong> (Stable / Scalable)</td><td><strong>High</strong> (Core feature)</td><td><strong>Maintain &amp; Optimize</strong> - Leave it alone; it works and provides high ROI.</td></tr><tr><td><strong>Low</strong> (Fragile / Slow)</td><td><strong>High</strong> (Core feature)</td><td><strong>Refactor / Strangler Pattern</strong> - Incrementally isolate and replace this module.</td></tr><tr><td><strong>High</strong> (Stable / Scalable)</td><td><strong>Low</strong> (Rarely used)</td><td><strong>Contain</strong> - Minimize active investment; keep the lights on with minimal resources.</td></tr><tr><td><strong>Low</strong> (Fragile / Slow)</td><td><strong>Low</strong> (Rarely used)</td><td><strong>Decommission</strong> - Sunset the feature entirely to reduce the maintenance surface area.</td></tr></tbody></table>
<hr>
<h2>The Golden Rule of Legacy Transitions</h2>
<p>Avoid the temptation of the <strong>"Big Bang Rewrite."</strong> It is almost always a trap that burns capital, delays feature delivery, and alienates product and sales teams.</p>
<p>Instead, rely on the <strong>Strangler Fig Application</strong> pattern. Chip away at the legacy system by placing new features in microservices or clean, decoupled modules behind an API gateway. Over time, routing is shifted from the legacy core to the new modules, gradually letting the old, unmaintainable code wither away.</p>]]></content:encoded>
            <author>kenmsh@gmail.com (Kennedy Mutisya)</author>
        </item>
        <item>
            <title><![CDATA[Avoiding Memory Leaks When Running Laravel Queue Workers]]></title>
            <link>mutisyadev.xyz/articles/avoiding-memory-leaks-when-running-laravel-queue-workers</link>
            <guid>mutisyadev.xyz/articles/avoiding-memory-leaks-when-running-laravel-queue-workers</guid>
            <pubDate>Mon, 10 Jul 2023 00:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>There is an old debate about whether PHP is suitable for long-running processes. Having run hundreds of workers on large-scale projects, I can say PHP is perfectly capable, as long as you handle memory correctly.</p>
<p>The problem is not the language. It is that references pile up in memory that PHP's garbage collector cannot detect. The process grows, and eventually the server runs out of memory and crashes.</p>
<p>The fix is simple: restart your workers more often.</p>
<h2>Restart Workers with Cron</h2>
<p>With a process manager like Supervisor in place, you can restart workers every hour and let Supervisor bring them back automatically.</p>
<p>Drop this into your crontab:</p>
<pre class="language-bash"><code class="language-bash"><span class="token number">0</span> * * * * forge php /home/forge/laravel.com/artisan queue:restart
</code></pre>
<p>This runs <code>queue:restart</code> every hour. All running workers receive the signal and gracefully exit after finishing whatever job they are currently processing. Supervisor detects the exit and spawns a fresh, clean process.</p>
<h2>Built-in Options: max-jobs and max-time</h2>
<p>If you would rather not use a cron job, Laravel gives you two command-line options that do the same thing:</p>
<pre class="language-bash"><code class="language-bash">php artisan queue:work --max-jobs<span class="token operator">=</span><span class="token number">1000</span> --max-time<span class="token operator">=</span><span class="token number">3600</span>
</code></pre>
<ul>
<li><strong><code>--max-jobs</code></strong>: the worker exits after processing this many jobs, regardless of how long it took.</li>
<li><strong><code>--max-time</code></strong>: the worker exits after this many seconds, regardless of how many jobs it ran.</li>
</ul>
<p>The check happens between jobs, not in the middle of one. So if a job is still running when the limit expires, the worker finishes it first and then exits.</p>
<p>The combination is useful: process up to 1,000 jobs or run for up to an hour, whichever comes first. This caps both memory growth and total runtime in a single command.</p>
<h2>Signal the Worker from Inside a Job</h2>
<p>Sometimes you know a specific job type is memory-heavy: maybe it processes a large file, batches thousands of database records, or loads big datasets into memory. You can tell the worker to quit right after that job finishes, from within the job's <code>handle()</code> method:</p>
<pre class="language-php"><code class="language-php"><span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function-definition function">handle</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">{</span>
    <span class="token comment">// Run the job logic.</span>

    <span class="token function">app</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'queue.worker'</span><span class="token punctuation">)</span><span class="token operator">-&gt;</span><span class="token property">shouldQuit</span> <span class="token operator">=</span> <span class="token number">1</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre>
<p>This sets the <code>shouldQuit</code> flag that the worker checks at the end of each loop iteration. When the flag is true, the worker exits after the current job completes. This gives you fine-grained control: only restart after the jobs that actually need it, rather than on a fixed schedule.</p>
<h2>Using Horizon</h2>
<p>If you are using Laravel Horizon, you can configure <code>maxJobs</code> and <code>maxTime</code> directly in your supervisor configuration instead of passing CLI options:</p>
<pre class="language-php"><code class="language-php"><span class="token string single-quoted-string">'environments'</span> <span class="token operator">=&gt;</span> <span class="token punctuation">[</span>
    <span class="token string single-quoted-string">'production'</span> <span class="token operator">=&gt;</span> <span class="token punctuation">[</span>
        <span class="token string single-quoted-string">'supervisor-1'</span> <span class="token operator">=&gt;</span> <span class="token punctuation">[</span>
            <span class="token comment">// ...</span>
            <span class="token string single-quoted-string">'maxTime'</span> <span class="token operator">=&gt;</span> <span class="token number">3600</span><span class="token punctuation">,</span>
            <span class="token string single-quoted-string">'maxJobs'</span> <span class="token operator">=&gt;</span> <span class="token number">1000</span><span class="token punctuation">,</span>
        <span class="token punctuation">]</span><span class="token punctuation">,</span>
    <span class="token punctuation">]</span><span class="token punctuation">,</span>
<span class="token punctuation">]</span><span class="token punctuation">,</span>
</code></pre>
<p>Horizon handles the rest. It manages the underlying worker processes and restarts them according to your limits.</p>
<h2>The Strategy</h2>
<p>Restarting workers is not a hack. It is the intended escape hatch for a fundamental constraint: any long-running process accumulates memory over time, no matter what language it is written in. The strategy is:</p>
<ol>
<li><strong>Set a reasonable max-time or max-jobs</strong> as a safety net (default for everything).</li>
<li><strong>Use <code>shouldQuit</code> inside specific jobs</strong> that you know are memory-heavy.</li>
<li><strong>Run <code>queue:restart</code> on a cron</strong> as a last-resort backstop if you are not using <code>--max-jobs</code> or <code>--max-time</code>.</li>
</ol>
<p>The three approaches are complementary. Use the combination that matches your workload. A worker processing small, predictable jobs can run for hours. One handling large file uploads or API batch operations should cycle more frequently.</p>
<h2>Key Takeaways</h2>
<ul>
<li><strong>PHP workers can leak memory over time</strong>: not a language flaw, just reality for any long-running process.</li>
<li><strong>Restart often</strong>: hourly restarts with <code>queue:restart</code> via cron keep things clean.</li>
<li><strong><code>--max-jobs</code> and <code>--max-time</code></strong> let you cap worker lifespan without external tools.</li>
<li><strong><code>shouldQuit</code> from inside a job</strong> gives you per-job-type control over restarts.</li>
<li><strong>Horizon has built-in maxJobs/maxTime settings</strong> so you enforce the same rules without extra scripts.</li>
<li><strong>Do not fight the restart</strong>: it is how queue workers stay healthy. Embrace the cycle.</li>
</ul>]]></content:encoded>
            <author>kenmsh@gmail.com (Kennedy Mutisya)</author>
        </item>
        <item>
            <title><![CDATA[Casting JSON Columns to Value Objects with Laravel]]></title>
            <link>mutisyadev.xyz/articles/casting-json-columns-to-value-objects-with-laravel</link>
            <guid>mutisyadev.xyz/articles/casting-json-columns-to-value-objects-with-laravel</guid>
            <pubDate>Thu, 09 Jul 2026 00:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>Have you ever wanted to grab an Eloquent model attribute and have it come back as a rich, typed value object instead of a plain array or <code>stdClass</code>? That is the kind of ergonomics we get with Carbon for dates in Eloquent. JSON columns are the natural answer here, but working with them raw means you are dealing with associative arrays and hoping your keys are spelled right.</p>
<p>Let us walk through a progression, from built-in array casting all the way to a clean, reusable castable value object setup.</p>
<h2>The Problem with Plain JSON Columns</h2>
<p>Eloquent's <code>$casts</code> property gives you a couple of built-in options:</p>
<pre class="language-php"><code class="language-php"><span class="token keyword">protected</span> <span class="token variable">$casts</span> <span class="token operator">=</span> <span class="token punctuation">[</span>
    <span class="token string single-quoted-string">'address'</span> <span class="token operator">=&gt;</span> <span class="token string single-quoted-string">'array'</span><span class="token punctuation">,</span>
<span class="token punctuation">]</span><span class="token punctuation">;</span>
</code></pre>
<p>This auto-serializes to and from JSON. Access it and you get an associative array back. Swap <code>'array'</code> for <code>'object'</code> and you get a <code>stdClass</code> instead. Both work, but neither gives you any type safety, autocomplete, or the ability to add behaviour directly on the data.</p>
<p>If your address value needs to know how to calculate a postage cost or build a map URL, you end up writing helper functions that take the raw array, which scatters that logic across your codebase instead of keeping it where it belongs.</p>
<h2>Custom Casts: First Step</h2>
<p>Laravel's custom casts give us a better foundation. You create a class that implements the <code>CastsAttributes</code> interface and define <code>get()</code> and <code>set()</code> methods:</p>
<pre class="language-php"><code class="language-php"><span class="token keyword">namespace</span> <span class="token package">App<span class="token punctuation">\</span>Casts</span><span class="token punctuation">;</span>

<span class="token keyword">use</span> <span class="token package">App<span class="token punctuation">\</span>Values<span class="token punctuation">\</span>Address</span> <span class="token keyword">as</span> AddressValue<span class="token punctuation">;</span>
<span class="token keyword">use</span> <span class="token package">Illuminate<span class="token punctuation">\</span>Contracts<span class="token punctuation">\</span>Database<span class="token punctuation">\</span>Eloquent<span class="token punctuation">\</span>CastsAttributes</span><span class="token punctuation">;</span>

<span class="token keyword">class</span> <span class="token class-name-definition class-name">Address</span> <span class="token keyword">implements</span> <span class="token class-name">CastsAttributes</span>
<span class="token punctuation">{</span>
    <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function-definition function">get</span><span class="token punctuation">(</span><span class="token variable">$model</span><span class="token punctuation">,</span> <span class="token variable">$key</span><span class="token punctuation">,</span> <span class="token variable">$value</span><span class="token punctuation">,</span> <span class="token variable">$attributes</span><span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token function">is_null</span><span class="token punctuation">(</span><span class="token variable">$value</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
            <span class="token keyword">return</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span>

        <span class="token keyword">return</span> <span class="token keyword">new</span> <span class="token class-name">AddressValue</span><span class="token punctuation">(</span><span class="token function">json_decode</span><span class="token punctuation">(</span><span class="token variable">$value</span><span class="token punctuation">,</span> <span class="token constant boolean">true</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function-definition function">set</span><span class="token punctuation">(</span><span class="token variable">$model</span><span class="token punctuation">,</span> <span class="token variable">$key</span><span class="token punctuation">,</span> <span class="token variable">$value</span><span class="token punctuation">,</span> <span class="token variable">$attributes</span><span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token function">is_null</span><span class="token punctuation">(</span><span class="token variable">$value</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
            <span class="token keyword">return</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span>

        <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token function">is_array</span><span class="token punctuation">(</span><span class="token variable">$value</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
            <span class="token variable">$value</span> <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">AddressValue</span><span class="token punctuation">(</span><span class="token variable">$value</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span>

        <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span> <span class="token variable">$value</span> <span class="token keyword">instanceof</span> <span class="token class-name">AddressValue</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
            <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name class-name-fully-qualified"><span class="token punctuation">\</span>InvalidArgumentException</span><span class="token punctuation">(</span>
                <span class="token string single-quoted-string">'Value must be of type Address, array, or null'</span>
            <span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span>

        <span class="token keyword">return</span> <span class="token function">json_encode</span><span class="token punctuation">(</span><span class="token variable">$value</span><span class="token operator">-&gt;</span><span class="token function">toArray</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre>
<p>Now we cast in the model like this:</p>
<pre class="language-php"><code class="language-php"><span class="token keyword">use</span> <span class="token package">App<span class="token punctuation">\</span>Casts<span class="token punctuation">\</span>Address</span><span class="token punctuation">;</span>

<span class="token keyword">protected</span> <span class="token variable">$casts</span> <span class="token operator">=</span> <span class="token punctuation">[</span>
    <span class="token string single-quoted-string">'address'</span> <span class="token operator">=&gt;</span> <span class="token scope">Address<span class="token punctuation">::</span></span><span class="token keyword">class</span><span class="token punctuation">,</span>
<span class="token punctuation">]</span><span class="token punctuation">;</span>
</code></pre>
<p>That is already a big step up: we get a typed object instead of an array. But we can do better.</p>
<h2>Making the Value Object Castable</h2>
<p>Instead of pointing the model at a separate caster class, Laravel lets a class implement the <code>Castable</code> interface and tell the world how to cast itself:</p>
<pre class="language-php"><code class="language-php"><span class="token keyword">namespace</span> <span class="token package">App<span class="token punctuation">\</span>Values</span><span class="token punctuation">;</span>

<span class="token keyword">use</span> <span class="token package">App<span class="token punctuation">\</span>Casts<span class="token punctuation">\</span>Address</span> <span class="token keyword">as</span> AddressCast<span class="token punctuation">;</span>
<span class="token keyword">use</span> <span class="token package">Illuminate<span class="token punctuation">\</span>Contracts<span class="token punctuation">\</span>Database<span class="token punctuation">\</span>Eloquent<span class="token punctuation">\</span>Castable</span><span class="token punctuation">;</span>

<span class="token keyword">class</span> <span class="token class-name-definition class-name">Address</span> <span class="token keyword">implements</span> <span class="token class-name">Castable</span>
<span class="token punctuation">{</span>
    <span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">function</span> <span class="token function-definition function">castUsing</span><span class="token punctuation">(</span><span class="token keyword type-hint">array</span> <span class="token variable">$arguments</span><span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token keyword">return</span> <span class="token scope">AddressCast<span class="token punctuation">::</span></span><span class="token keyword">class</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre>
<p>Now the model casts directly to the value object class:</p>
<pre class="language-php"><code class="language-php"><span class="token keyword">use</span> <span class="token package">App<span class="token punctuation">\</span>Values<span class="token punctuation">\</span>Address</span><span class="token punctuation">;</span>

<span class="token keyword">protected</span> <span class="token variable">$casts</span> <span class="token operator">=</span> <span class="token punctuation">[</span>
    <span class="token string single-quoted-string">'address'</span> <span class="token operator">=&gt;</span> <span class="token scope">Address<span class="token punctuation">::</span></span><span class="token keyword">class</span><span class="token punctuation">,</span>
<span class="token punctuation">]</span><span class="token punctuation">;</span>
</code></pre>
<p>That feels cleaner. The value object owns its casting logic instead of relying on an external caster that the developer has to remember to link.</p>
<h2>Enter Spatie's DTO Package</h2>
<p>Spatie's Data Transfer Object package (superseded by Laravel Data, but still instructive) gives us typed, validated properties:</p>
<pre class="language-bash"><code class="language-bash"><span class="token function">composer</span> require spatie/data-transfer-object
</code></pre>
<pre class="language-php"><code class="language-php"><span class="token keyword">namespace</span> <span class="token package">App<span class="token punctuation">\</span>Values</span><span class="token punctuation">;</span>

<span class="token keyword">use</span> <span class="token package">App<span class="token punctuation">\</span>Casts<span class="token punctuation">\</span>Address</span> <span class="token keyword">as</span> AddressCast<span class="token punctuation">;</span>
<span class="token keyword">use</span> <span class="token package">Illuminate<span class="token punctuation">\</span>Contracts<span class="token punctuation">\</span>Database<span class="token punctuation">\</span>Eloquent<span class="token punctuation">\</span>Castable</span><span class="token punctuation">;</span>
<span class="token keyword">use</span> <span class="token package">Spatie<span class="token punctuation">\</span>DataTransferObject<span class="token punctuation">\</span>DataTransferObject</span><span class="token punctuation">;</span>

<span class="token keyword">class</span> <span class="token class-name-definition class-name">Address</span> <span class="token keyword">extends</span> <span class="token class-name">DataTransferObject</span> <span class="token keyword">implements</span> <span class="token class-name">Castable</span>
<span class="token punctuation">{</span>
    <span class="token keyword">public</span> <span class="token keyword type-declaration">string</span> <span class="token variable">$street</span><span class="token punctuation">;</span>
    <span class="token keyword">public</span> <span class="token keyword type-declaration">string</span> <span class="token variable">$suburb</span><span class="token punctuation">;</span>
    <span class="token keyword">public</span> <span class="token keyword type-declaration">string</span> <span class="token variable">$state</span><span class="token punctuation">;</span>

    <span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">function</span> <span class="token function-definition function">castUsing</span><span class="token punctuation">(</span><span class="token keyword type-hint">array</span> <span class="token variable">$arguments</span><span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token keyword">return</span> <span class="token scope">AddressCast<span class="token punctuation">::</span></span><span class="token keyword">class</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre>
<p>Now if someone tries to set <code>$address-&gt;street</code> to an integer, an exception is thrown. The structure is enforced, not assumed.</p>
<h2>Making the Caster Reusable</h2>
<p>Look at the caster from earlier. Nothing in it is specific to the <code>Address</code> class except the class name reference. If we have multiple value objects, we would be duplicating the same casting logic for each one. Instead, parameterise it:</p>
<pre class="language-php"><code class="language-php"><span class="token keyword">namespace</span> <span class="token package">App<span class="token punctuation">\</span>Casts</span><span class="token punctuation">;</span>

<span class="token keyword">use</span> <span class="token package">Illuminate<span class="token punctuation">\</span>Contracts<span class="token punctuation">\</span>Database<span class="token punctuation">\</span>Eloquent<span class="token punctuation">\</span>CastsAttributes</span><span class="token punctuation">;</span>

<span class="token keyword">class</span> <span class="token class-name-definition class-name">DataTransferObject</span> <span class="token keyword">implements</span> <span class="token class-name">CastsAttributes</span>
<span class="token punctuation">{</span>
    <span class="token keyword">protected</span> <span class="token keyword type-declaration">string</span> <span class="token variable">$class</span><span class="token punctuation">;</span>

    <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function-definition function">__construct</span><span class="token punctuation">(</span><span class="token keyword type-hint">string</span> <span class="token variable">$class</span><span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token this keyword">$this</span><span class="token operator">-&gt;</span><span class="token keyword">class</span> <span class="token operator">=</span> <span class="token variable">$class</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function-definition function">get</span><span class="token punctuation">(</span><span class="token variable">$model</span><span class="token punctuation">,</span> <span class="token variable">$key</span><span class="token punctuation">,</span> <span class="token variable">$value</span><span class="token punctuation">,</span> <span class="token variable">$attributes</span><span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token function">is_null</span><span class="token punctuation">(</span><span class="token variable">$value</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
            <span class="token keyword">return</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span>

        <span class="token keyword">return</span> <span class="token keyword">new</span> <span class="token this keyword">$this</span><span class="token operator">-&gt;</span><span class="token keyword">class</span><span class="token punctuation">(</span><span class="token function">json_decode</span><span class="token punctuation">(</span><span class="token variable">$value</span><span class="token punctuation">,</span> <span class="token constant boolean">true</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function-definition function">set</span><span class="token punctuation">(</span><span class="token variable">$model</span><span class="token punctuation">,</span> <span class="token variable">$key</span><span class="token punctuation">,</span> <span class="token variable">$value</span><span class="token punctuation">,</span> <span class="token variable">$attributes</span><span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token function">is_null</span><span class="token punctuation">(</span><span class="token variable">$value</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
            <span class="token keyword">return</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span>

        <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token function">is_array</span><span class="token punctuation">(</span><span class="token variable">$value</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
            <span class="token variable">$value</span> <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token this keyword">$this</span><span class="token operator">-&gt;</span><span class="token keyword">class</span><span class="token punctuation">(</span><span class="token variable">$value</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span>

        <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span> <span class="token variable">$value</span> <span class="token keyword">instanceof</span> <span class="token this keyword">$this</span><span class="token operator">-&gt;</span><span class="token keyword">class</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
            <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name class-name-fully-qualified"><span class="token punctuation">\</span>InvalidArgumentException</span><span class="token punctuation">(</span>
                <span class="token string double-quoted-string">"Value must be of type [<span class="token interpolation"><span class="token punctuation">{</span><span class="token this keyword">$this</span><span class="token operator">-&gt;</span><span class="token keyword">class</span><span class="token punctuation">}</span></span>], array, or null"</span>
            <span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span>

        <span class="token keyword">return</span> <span class="token function">json_encode</span><span class="token punctuation">(</span><span class="token variable">$value</span><span class="token operator">-&gt;</span><span class="token function">toArray</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre>
<p>Now your value object's <code>castUsing()</code> becomes:</p>
<pre class="language-php"><code class="language-php"><span class="token keyword">use</span> <span class="token package">App<span class="token punctuation">\</span>Casts<span class="token punctuation">\</span>DataTransferObject</span><span class="token punctuation">;</span>

<span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">function</span> <span class="token function-definition function">castUsing</span><span class="token punctuation">(</span><span class="token keyword type-hint">array</span> <span class="token variable">$arguments</span><span class="token punctuation">)</span>
<span class="token punctuation">{</span>
    <span class="token keyword">return</span> <span class="token keyword">new</span> <span class="token class-name">DataTransferObject</span><span class="token punctuation">(</span><span class="token scope">Address<span class="token punctuation">::</span></span><span class="token keyword">class</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre>
<p>One general-purpose caster, any DTO class.</p>
<h2>An Abstract Castable DTO</h2>
<p>We can go one step further and create an abstract base class that any value object can extend. This eliminates the boilerplate of implementing <code>Castable</code> on every class:</p>
<pre class="language-php"><code class="language-php"><span class="token keyword">namespace</span> <span class="token package">App<span class="token punctuation">\</span>Values</span><span class="token punctuation">;</span>

<span class="token keyword">use</span> <span class="token package">App<span class="token punctuation">\</span>Casts<span class="token punctuation">\</span>DataTransferObject</span> <span class="token keyword">as</span> DataTransferObjectCast<span class="token punctuation">;</span>
<span class="token keyword">use</span> <span class="token package">Illuminate<span class="token punctuation">\</span>Contracts<span class="token punctuation">\</span>Database<span class="token punctuation">\</span>Eloquent<span class="token punctuation">\</span>Castable</span><span class="token punctuation">;</span>
<span class="token keyword">use</span> <span class="token package">Spatie<span class="token punctuation">\</span>DataTransferObject<span class="token punctuation">\</span>DataTransferObject</span><span class="token punctuation">;</span>

<span class="token keyword">abstract</span> <span class="token keyword">class</span> <span class="token class-name-definition class-name">CastableDataTransferObject</span> <span class="token keyword">extends</span> <span class="token class-name">DataTransferObject</span> <span class="token keyword">implements</span> <span class="token class-name">Castable</span>
<span class="token punctuation">{</span>
    <span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">function</span> <span class="token function-definition function">castUsing</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token keyword">return</span> <span class="token keyword">new</span> <span class="token class-name">DataTransferObjectCast</span><span class="token punctuation">(</span><span class="token scope"><span class="token keyword">static</span><span class="token punctuation">::</span></span><span class="token keyword">class</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function-definition function">toJson</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">:</span> <span class="token keyword return-type">string</span>
    <span class="token punctuation">{</span>
        <span class="token keyword">return</span> <span class="token function">json_encode</span><span class="token punctuation">(</span><span class="token this keyword">$this</span><span class="token operator">-&gt;</span><span class="token function">toArray</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">function</span> <span class="token function-definition function">fromJson</span><span class="token punctuation">(</span><span class="token keyword type-hint">string</span> <span class="token variable">$json</span><span class="token punctuation">)</span><span class="token punctuation">:</span> <span class="token keyword return-type">static</span>
    <span class="token punctuation">{</span>
        <span class="token keyword">return</span> <span class="token keyword">new</span> <span class="token keyword">static</span><span class="token punctuation">(</span><span class="token function">json_decode</span><span class="token punctuation">(</span><span class="token variable">$json</span><span class="token punctuation">,</span> <span class="token constant boolean">true</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre>
<p>The <code>toJson()</code> and <code>fromJson()</code> methods keep serialization with the value object. And because they are on the abstract class, every extending value object gets them for free.</p>
<p>Now the <code>Address</code> class is just:</p>
<pre class="language-php"><code class="language-php"><span class="token keyword">namespace</span> <span class="token package">App<span class="token punctuation">\</span>Values</span><span class="token punctuation">;</span>

<span class="token keyword">class</span> <span class="token class-name-definition class-name">Address</span> <span class="token keyword">extends</span> <span class="token class-name">CastableDataTransferObject</span>
<span class="token punctuation">{</span>
    <span class="token keyword">public</span> <span class="token keyword type-declaration">string</span> <span class="token variable">$street</span><span class="token punctuation">;</span>
    <span class="token keyword">public</span> <span class="token keyword type-declaration">string</span> <span class="token variable">$suburb</span><span class="token punctuation">;</span>
    <span class="token keyword">public</span> <span class="token keyword type-declaration">string</span> <span class="token variable">$state</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre>
<h2>The Final Setup</h2>
<p>You end up with three files:</p>
<pre><code>app/Casts/DataTransferObject.php          # The generic caster
app/Values/CastableDataTransferObject.php # The abstract base class
app/Values/Address.php                    # Your actual value object
</code></pre>
<p>And the model casts are as clean as it gets:</p>
<pre class="language-php"><code class="language-php"><span class="token keyword">protected</span> <span class="token variable">$casts</span> <span class="token operator">=</span> <span class="token punctuation">[</span>
    <span class="token string single-quoted-string">'address'</span> <span class="token operator">=&gt;</span> <span class="token scope">Address<span class="token punctuation">::</span></span><span class="token keyword">class</span><span class="token punctuation">,</span>
<span class="token punctuation">]</span><span class="token punctuation">;</span>
</code></pre>
<p>Querying JSON columns works natively:</p>
<pre class="language-php"><code class="language-php"><span class="token variable">$residents</span> <span class="token operator">=</span> <span class="token scope">User<span class="token punctuation">::</span></span><span class="token function">where</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'address-&gt;suburb'</span><span class="token punctuation">,</span> <span class="token string single-quoted-string">'Hill Valley'</span><span class="token punctuation">)</span><span class="token operator">-&gt;</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre>
<p>Creating records accepts either an array or the value object directly:</p>
<pre class="language-php"><code class="language-php"><span class="token scope">User<span class="token punctuation">::</span></span><span class="token function">create</span><span class="token punctuation">(</span><span class="token punctuation">[</span>
    <span class="token string single-quoted-string">'name'</span>    <span class="token operator">=&gt;</span> <span class="token string single-quoted-string">'Emmett Brown'</span><span class="token punctuation">,</span>
    <span class="token string single-quoted-string">'address'</span> <span class="token operator">=&gt;</span> <span class="token punctuation">[</span>
        <span class="token string single-quoted-string">'street'</span> <span class="token operator">=&gt;</span> <span class="token string single-quoted-string">'1640 Riverside Drive'</span><span class="token punctuation">,</span>
        <span class="token string single-quoted-string">'suburb'</span> <span class="token operator">=&gt;</span> <span class="token string single-quoted-string">'Hill Valley'</span><span class="token punctuation">,</span>
        <span class="token string single-quoted-string">'state'</span>  <span class="token operator">=&gt;</span> <span class="token string single-quoted-string">'California'</span><span class="token punctuation">,</span>
    <span class="token punctuation">]</span><span class="token punctuation">,</span>
<span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre>
<p>Once retrieved, <code>$user-&gt;address</code> is a full-fledged value object with the methods you define on it.</p>
<h2>Key Takeaways</h2>
<ul>
<li><strong>JSON columns</strong> are great for nested or optional data, but raw arrays lack safety and expressiveness.</li>
<li><strong>Custom casts</strong> bridge the gap between the database and typed objects.</li>
<li><strong>The <code>Castable</code> interface</strong> lets value objects declare how they should be cast without the model knowing the details.</li>
<li><strong>A single generic caster</strong> serves every DTO in your app. No more one-off caster classes.</li>
<li><strong>An abstract base class</strong> eliminates the boilerplate so new value objects are trivial to create.</li>
</ul>
<p>The result is a clean separation: value objects own their shape, their validation, and their behaviour. Eloquent handles persistence. And your application code talks to rich, meaningful objects instead of loose arrays.</p>]]></content:encoded>
            <author>kenmsh@gmail.com (Kennedy Mutisya)</author>
        </item>
        <item>
            <title><![CDATA[Common Issues That Cause Laravel Queue Workers Not to Restart]]></title>
            <link>mutisyadev.xyz/articles/common-issues-that-cause-laravel-queue-workers-not-to-restart</link>
            <guid>mutisyadev.xyz/articles/common-issues-that-cause-laravel-queue-workers-not-to-restart</guid>
            <pubDate>Tue, 08 Jul 2025 00:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>Restarting queue workers should be a standard part of your deployment script. The command is simple:</p>
<pre class="language-bash"><code class="language-bash">php artisan queue:restart
</code></pre>
<p>It works by writing an <code>illuminate:queue:restart</code> key to your cache store with a timestamp. Workers check this key after finishing each job. If the timestamp is newer than when they last checked, the worker process exits gracefully, and your process manager starts a fresh one.</p>
<p>If you are using Laravel Forge, you can check a worker's uptime. After a successful restart, you will see something like:</p>
<pre><code>worker-:worker-_00   RUNNING   pid 75, uptime 0 days, 00:00:20
</code></pre>
<p>That is a brand new process. If the worker's uptime is much longer than expected after you ran <code>queue:restart</code>, something is preventing the restart. Here are the usual culprits.</p>
<h2>1. The Worker Is Running in Listen Mode</h2>
<p>If you started the worker with <code>queue:listen</code> instead of <code>queue:work</code>, the listener spawns a separate child process for every job. Once the job finishes, the child process is killed and a new one is created for the next job. This means every job already runs with the latest code. The listener has no persistent state to clear, so <code>queue:restart</code> has zero effect on it.</p>
<p>The only way to restart a listener process is manually:</p>
<pre class="language-bash"><code class="language-bash">supervisorctl restart worker-name:*
</code></pre>
<p>If you are relying on <code>queue:restart</code> and wondering why nothing happens, check whether your workers are running in listen mode versus daemon mode.</p>
<h2>2. The Cache Store Is Not Shared or Accessible</h2>
<p>For <code>queue:restart</code> to work, every worker process needs to read from the same cache store where the restart timestamp is written. If your workers are on different machines or different cache configurations, they will not see the key.</p>
<p>A common variant: the <code>queue:restart</code> command writes the key using one cache driver, but the workers read from another. Both sides need to agree on the same <code>CACHE_DRIVER</code> and the same backend (same Redis database, same Memcached server, same filesystem).</p>
<p><strong>File cache driver gotcha:</strong> If you are using the file cache driver, the user running <code>queue:restart</code> must be able to write the cache file, and the user running the worker processes must be able to read it. Permission issues here will silently break the restart:</p>
<pre class="language-bash"><code class="language-bash"><span class="token function">sudo</span> <span class="token function">chown</span> -R forge:forge storage/framework/cache
</code></pre>
<p>This ensures the cache directory is owned by the <code>forge</code> user. If both the deploy script and the workers run as <code>forge</code>, everything works.</p>
<h2>3. The Cache Prefix Changed</h2>
<p>If you change the cache prefix in <code>config/cache.php</code> (or via <code>CACHE_PREFIX</code>), the restart command writes the <code>illuminate:queue:restart</code> key under the new prefix. Existing workers still look for the key under the old prefix. They never see the updated timestamp, so they never restart.</p>
<p>This is a one-time problem. After you change the prefix, you need to restart the workers manually once to pick up the new prefix:</p>
<pre class="language-bash"><code class="language-bash">supervisorctl restart worker-name:*
</code></pre>
<p>After that manual restart, subsequent <code>queue:restart</code> calls work normally because all processes now share the same prefix.</p>
<h2>How <code>queue:restart</code> Actually Works</h2>
<p>When you run <code>queue:restart</code>, Laravel updates the <code>illuminate:queue:restart</code> timestamp in the cache to the current time.</p>
<p>Inside the worker daemon loop, <code>stopIfNecessary()</code> runs after each job and calls <code>queueShouldRestart()</code>:</p>
<pre class="language-php"><code class="language-php"><span class="token keyword">protected</span> <span class="token keyword">function</span> <span class="token function-definition function">stopIfNecessary</span><span class="token punctuation">(</span><span class="token class-name type-declaration">WorkerOptions</span> <span class="token variable">$options</span><span class="token punctuation">,</span> <span class="token variable">$lastRestart</span><span class="token punctuation">)</span>
<span class="token punctuation">{</span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token this keyword">$this</span><span class="token operator">-&gt;</span><span class="token property">shouldQuit</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token this keyword">$this</span><span class="token operator">-&gt;</span><span class="token function">kill</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token this keyword">$this</span><span class="token operator">-&gt;</span><span class="token function">memoryExceeded</span><span class="token punctuation">(</span><span class="token variable">$options</span><span class="token operator">-&gt;</span><span class="token property">memory</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token this keyword">$this</span><span class="token operator">-&gt;</span><span class="token function">stop</span><span class="token punctuation">(</span><span class="token number">12</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span> <span class="token keyword">elseif</span> <span class="token punctuation">(</span><span class="token this keyword">$this</span><span class="token operator">-&gt;</span><span class="token function">queueShouldRestart</span><span class="token punctuation">(</span><span class="token variable">$lastRestart</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token this keyword">$this</span><span class="token operator">-&gt;</span><span class="token function">stop</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre>
<p><code>queueShouldRestart()</code> compares the stored <code>$lastRestart</code> timestamp (captured when the worker started) against the current value of the <code>illuminate:queue:restart</code> cache key. If the cached value is newer, the worker knows a restart was requested and exits.</p>
<p>The worker will not exit in the middle of processing a job. The check only happens between jobs. Once the current job finishes, the worker sees the new timestamp and stops. Supervisor detects the stopped process and starts a replacement.</p>
<h2>Key Takeaways</h2>
<ul>
<li><strong><code>queue:restart</code> only works with <code>queue:work</code> (daemon mode), not <code>queue:listen</code>.</strong></li>
<li><strong>Workers must share the same cache store</strong> as the process running <code>queue:restart</code>.</li>
<li><strong>File cache permissions</strong> can silently prevent restarts. Check ownership of <code>storage/framework/cache</code>.</li>
<li><strong>Changing the cache prefix</strong> requires one manual restart to sync workers.</li>
<li><strong>The worker exits between jobs</strong>, not in the middle of one, so no jobs are lost during a restart.</li>
</ul>]]></content:encoded>
            <author>kenmsh@gmail.com (Kennedy Mutisya)</author>
        </item>
        <item>
            <title><![CDATA[Create a New Laravel 13 Project in 2026 - Step-by-Step with Docker and Composer]]></title>
            <link>mutisyadev.xyz/articles/create-laravel-project-2026</link>
            <guid>mutisyadev.xyz/articles/create-laravel-project-2026</guid>
            <pubDate>Tue, 20 Jan 2026 00:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<h1>Create a New Laravel 13 Project in 2026 - Step-by-Step with Docker and Composer</h1>
<p>If you want to create a Laravel project in 2026, the fastest reliable path is Laravel 13 + Docker + MySQL 8. This guide gives you a clean, repeatable setup for local development and a strong foundation for production.</p>
<p>By the end, you will have:</p>
<ul>
<li>A fresh Laravel 13 app.</li>
<li>A Docker-based local environment.</li>
<li>MySQL 8 wired and tested.</li>
<li>A baseline structure ready for real feature work.</li>
</ul>
<h2>Prerequisites</h2>
<p>Before creating the project, install the following:</p>
<ul>
<li>PHP 8.3+ (CLI).</li>
<li>Composer 2.x.</li>
<li>Docker Desktop.</li>
<li>Git.</li>
</ul>
<p>You can verify quickly:</p>
<pre class="language-bash"><code class="language-bash">php -v
<span class="token function">composer</span> -V
<span class="token function">docker</span> --version
<span class="token function">git</span> --version
</code></pre>
<h2>Step 1: Create the Laravel 13 project</h2>
<p>If you have the Laravel installer, use <code>laravel new</code> first:</p>
<pre class="language-bash"><code class="language-bash">laravel new laravel-starter
</code></pre>
<p>If you are not using the Laravel installer, use Composer as an alternative:</p>
<pre class="language-bash"><code class="language-bash"><span class="token function">composer</span> create-project laravel/laravel laravel-starter
</code></pre>
<p>Move into the project:</p>
<pre class="language-bash"><code class="language-bash"><span class="token builtin class-name">cd</span> laravel-starter
</code></pre>
<p>Confirm the framework version:</p>
<pre class="language-bash"><code class="language-bash">php artisan --version
</code></pre>
<h2>Step 2: Start with Laravel Sail (Docker)</h2>
<p>Laravel Sail gives you an official Docker workflow without manually building every container from scratch.</p>
<p>Install Sail dependencies and publish Docker files:</p>
<pre class="language-bash"><code class="language-bash"><span class="token function">composer</span> require laravel/sail --dev
php artisan sail:install --with<span class="token operator">=</span>mysql,redis,mailpit
</code></pre>
<p>Bring containers up:</p>
<pre class="language-bash"><code class="language-bash">./vendor/bin/sail up -d
</code></pre>
<p>To simplify commands, add an alias:</p>
<pre class="language-bash"><code class="language-bash"><span class="token builtin class-name">alias</span> <span class="token assign-left variable">sail</span><span class="token operator">=</span><span class="token string">'sh $([ -f sail ] &amp;&amp; echo sail || echo vendor/bin/sail)'</span>
</code></pre>
<p>Then use:</p>
<pre class="language-bash"><code class="language-bash">sail artisan about
</code></pre>
<h2>Step 3: Configure <code>.env</code> for MySQL 8</h2>
<p>After Sail install, your <code>.env</code> is mostly ready. Confirm key values:</p>
<pre class="language-ini"><code class="language-ini"><span class="token key attr-name">APP_NAME</span><span class="token punctuation">=</span><span class="token value attr-value">"<span class="token inner-value">Laravel Starter</span>"</span>
<span class="token key attr-name">APP_ENV</span><span class="token punctuation">=</span><span class="token value attr-value">local</span>
<span class="token key attr-name">APP_DEBUG</span><span class="token punctuation">=</span><span class="token value attr-value">true</span>
<span class="token key attr-name">APP_URL</span><span class="token punctuation">=</span><span class="token value attr-value">http://localhost</span>

<span class="token key attr-name">DB_CONNECTION</span><span class="token punctuation">=</span><span class="token value attr-value">mysql</span>
<span class="token key attr-name">DB_HOST</span><span class="token punctuation">=</span><span class="token value attr-value">mysql</span>
<span class="token key attr-name">DB_PORT</span><span class="token punctuation">=</span><span class="token value attr-value">3306</span>
<span class="token key attr-name">DB_DATABASE</span><span class="token punctuation">=</span><span class="token value attr-value">laravel</span>
<span class="token key attr-name">DB_USERNAME</span><span class="token punctuation">=</span><span class="token value attr-value">sail</span>
<span class="token key attr-name">DB_PASSWORD</span><span class="token punctuation">=</span><span class="token value attr-value">password</span>
</code></pre>
<p>Why <code>DB_HOST=mysql</code>? In Docker networking, <code>mysql</code> is the service hostname defined by Sail.</p>
<h2>Step 4: Generate app key and run migrations</h2>
<p>Run initial setup commands inside Sail:</p>
<pre class="language-bash"><code class="language-bash">sail artisan key:generate
sail artisan migrate
</code></pre>
<p>If migrations succeed, your Laravel app is correctly connected to MySQL 8.</p>
<h2>Step 5: Validate your app works end to end</h2>
<p>Open the app:</p>
<pre class="language-text"><code class="language-text">http://localhost
</code></pre>
<p>Run tests:</p>
<pre class="language-bash"><code class="language-bash">sail <span class="token builtin class-name">test</span>
</code></pre>
<p>Check queue worker quickly (optional):</p>
<pre class="language-bash"><code class="language-bash">sail artisan queue:work --once
</code></pre>
<p>At this point, you have a healthy Laravel 13 project baseline.</p>
<h2>Step 6: Create a production-friendly folder structure</h2>
<p>A useful early pattern is to keep business logic out of controllers.</p>
<p>Suggested starter layout:</p>
<pre class="language-text"><code class="language-text">app/
  Actions/
  Services/
  Repositories/
  Http/Controllers/
  Http/Requests/
</code></pre>
<p>Keep it lightweight at first. Add layers only when they help clarity.</p>
<h2>Step 7: Add quality defaults early</h2>
<p>Install tools you will almost certainly need:</p>
<pre class="language-bash"><code class="language-bash">sail <span class="token function">composer</span> require laravel/pint --dev
sail <span class="token function">composer</span> require pestphp/pest --dev
sail artisan pest:install
</code></pre>
<p>Add a basic format-and-test routine:</p>
<pre class="language-bash"><code class="language-bash">sail pint
sail <span class="token builtin class-name">test</span>
</code></pre>
<p>Starting clean prevents style drift as the project grows.</p>
<h2>Step 8: Add your first feature route and controller</h2>
<p>Create a simple health endpoint so you can validate deploys later:</p>
<pre class="language-bash"><code class="language-bash">sail artisan make:controller HealthController
</code></pre>
<p><code>routes/web.php</code>:</p>
<pre class="language-php"><code class="language-php"><span class="token keyword">use</span> <span class="token package">App<span class="token punctuation">\</span>Http<span class="token punctuation">\</span>Controllers<span class="token punctuation">\</span>HealthController</span><span class="token punctuation">;</span>
<span class="token keyword">use</span> <span class="token package">Illuminate<span class="token punctuation">\</span>Support<span class="token punctuation">\</span>Facades<span class="token punctuation">\</span>Route</span><span class="token punctuation">;</span>

<span class="token scope">Route<span class="token punctuation">::</span></span><span class="token function">get</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'/health'</span><span class="token punctuation">,</span> <span class="token scope">HealthController<span class="token punctuation">::</span></span><span class="token keyword">class</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre>
<p><code>app/Http/Controllers/HealthController.php</code>:</p>
<pre class="language-php"><code class="language-php"><span class="token php language-php"><span class="token delimiter important">&lt;?php</span>

<span class="token keyword">namespace</span> <span class="token package">App<span class="token punctuation">\</span>Http<span class="token punctuation">\</span>Controllers</span><span class="token punctuation">;</span>

<span class="token keyword">use</span> <span class="token package">Illuminate<span class="token punctuation">\</span>Http<span class="token punctuation">\</span>Response</span><span class="token punctuation">;</span>

<span class="token keyword">class</span> <span class="token class-name-definition class-name">HealthController</span> <span class="token keyword">extends</span> <span class="token class-name">Controller</span>
<span class="token punctuation">{</span>
    <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function-definition function">__invoke</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">:</span> <span class="token class-name return-type">Response</span>
    <span class="token punctuation">{</span>
        <span class="token keyword">return</span> <span class="token function">response</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'ok'</span><span class="token punctuation">,</span> <span class="token number">200</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</span></code></pre>
<p>Now <code>http://localhost/health</code> should return <code>ok</code>.</p>
<h2>Common setup mistakes (and fast fixes)</h2>
<table><thead><tr><th>Problem</th><th>Cause</th><th>Fix</th></tr></thead><tbody><tr><td><code>SQLSTATE[HY000] [2002] Connection refused</code></td><td>Wrong DB host in Docker</td><td>Use <code>DB_HOST=mysql</code></td></tr><tr><td><code>No application encryption key has been specified</code></td><td>Missing app key</td><td>Run <code>sail artisan key:generate</code></td></tr><tr><td><code>Class "PDO" not found</code></td><td>PHP extension mismatch</td><td>Use Sail PHP container, not host PHP</td></tr><tr><td>App runs, migrations fail</td><td>Containers not fully up</td><td>Wait a few seconds, then retry migrations</td></tr><tr><td>Slow first load</td><td>Cold Docker build</td><td>Normal on first boot; improves after image cache</td></tr></tbody></table>
<h2>Docker-based workflow cheatsheet</h2>
<pre class="language-bash"><code class="language-bash"><span class="token comment"># Start containers</span>
sail up -d

<span class="token comment"># Stop containers</span>
sail down

<span class="token comment"># Run migrations</span>
sail artisan migrate

<span class="token comment"># Run tests</span>
sail <span class="token builtin class-name">test</span>

<span class="token comment"># Run queue worker</span>
sail artisan queue:work

<span class="token comment"># Open shell in app container</span>
sail shell
</code></pre>
<h2>Why this setup works in 2026</h2>
<ul>
<li>Docker keeps developer machines consistent.</li>
<li>Laravel Sail stays close to Laravel defaults.</li>
<li>MySQL 8 gives predictable SQL behavior for modern apps.</li>
<li>Composer-based bootstrap keeps onboarding simple for teams.</li>
</ul>
<p>This gives you a project that is easy to run, easy to hand off, and ready for CI/CD.</p>
<h2>FAQ</h2>
<h3>Should I use Sail or run PHP directly on my machine?</h3>
<p>For solo experiments, either works. For team projects and predictable onboarding, Sail is usually better.</p>
<h3>Do I need Redis on day one?</h3>
<p>Not mandatory, but enabling it early is useful for queues, caching, and later scaling.</p>
<h3>Can I switch from MySQL to PostgreSQL later?</h3>
<p>Yes, but database-specific SQL and indexes may need updates. If you expect a switch, avoid engine-specific behavior early.</p>
<h3>Is this setup production-ready?</h3>
<p>It is production-minded, not production-complete. You still need deployment, monitoring, backups, and security hardening.</p>
<h2>Final takeaway</h2>
<p>If your goal is to create a Laravel project quickly without sacrificing quality, use Laravel 13 with Sail, Docker, and MySQL 8. You will move faster now and avoid painful environment mismatches later.</p>
<p>Next in this series: <a href="/articles/laravel-db-raw-guide-2026">How to Run Raw SQL Queries in Laravel with DB::raw</a> and the upcoming <a href="/articles/laravel-env-explained-2026">Laravel .env guide</a>.</p>]]></content:encoded>
            <author>kenmsh@gmail.com (Kennedy Mutisya)</author>
        </item>
        <item>
            <title><![CDATA[Deploy a Laravel 13 Application: From Shared Hosting to Docker (2026)]]></title>
            <link>mutisyadev.xyz/articles/deploy-laravel-project-2026</link>
            <guid>mutisyadev.xyz/articles/deploy-laravel-project-2026</guid>
            <pubDate>Mon, 20 Apr 2026 00:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<h1>Deploy a Laravel 13 Application: From Shared Hosting to Docker (2026)</h1>
<p>When your Laravel app works locally, deployment is the next real milestone. In this guide, you will deploy Laravel 13 with MySQL 8 using three practical options: shared hosting, VPS with Docker, and Laravel Forge.</p>
<p>You will also get a production checklist so you avoid common mistakes like broken permissions, leaked debug pages, and queue workers not running.</p>
<h2>Deployment options at a glance</h2>
<table><thead><tr><th>Option</th><th>Best for</th><th>Tradeoff</th></tr></thead><tbody><tr><td>Shared hosting</td><td>Lowest cost MVPs</td><td>Less control, limited performance tuning</td></tr><tr><td>VPS + Docker</td><td>Full control and portability</td><td>More DevOps responsibility</td></tr><tr><td>Laravel Forge</td><td>Fastest managed VPS setup</td><td>Added platform cost</td></tr></tbody></table>
<p>If you are new to Laravel setup, read <a href="/articles/create-laravel-project-2026">Create a New Laravel 13 Project in 2026</a> first. For config hardening, pair this with <a href="/articles/laravel-env-explained-2026">Laravel .env Explained</a>.</p>
<h2>Pre-deploy production checklist</h2>
<p>Before any deployment method, confirm these baseline settings:</p>
<pre class="language-ini"><code class="language-ini"><span class="token key attr-name">APP_ENV</span><span class="token punctuation">=</span><span class="token value attr-value">production</span>
<span class="token key attr-name">APP_DEBUG</span><span class="token punctuation">=</span><span class="token value attr-value">false</span>
<span class="token key attr-name">APP_URL</span><span class="token punctuation">=</span><span class="token value attr-value">https://yourdomain.com</span>
<span class="token key attr-name">LOG_CHANNEL</span><span class="token punctuation">=</span><span class="token value attr-value">stack</span>
<span class="token key attr-name">QUEUE_CONNECTION</span><span class="token punctuation">=</span><span class="token value attr-value">database</span>
<span class="token key attr-name">CACHE_STORE</span><span class="token punctuation">=</span><span class="token value attr-value">redis</span>
<span class="token key attr-name">SESSION_DRIVER</span><span class="token punctuation">=</span><span class="token value attr-value">redis</span>
</code></pre>
<p>And run:</p>
<pre class="language-bash"><code class="language-bash">php artisan config:cache
php artisan route:cache
php artisan view:cache
php artisan migrate --force
</code></pre>
<p>Key checks:</p>
<ul>
<li><code>.env</code> is not in git.</li>
<li><code>storage</code> and <code>bootstrap/cache</code> are writable by the web user.</li>
<li>Queue workers and scheduler are configured.</li>
<li>HTTPS is enabled.</li>
</ul>
<h2>Path 1: Deploy on shared hosting</h2>
<p>Shared hosting can run Laravel if you structure files correctly.</p>
<p>Typical flow:</p>
<ol>
<li>Upload project files outside <code>public_html</code>.</li>
<li>Point web root to Laravel <code>public/</code>.</li>
<li>Set production <code>.env</code> values.</li>
<li>Run composer install via SSH (if available).</li>
<li>Run migrations and cache commands.</li>
</ol>
<p>Recommended command set:</p>
<pre class="language-bash"><code class="language-bash"><span class="token function">composer</span> <span class="token function">install</span> --no-dev --optimize-autoloader
php artisan key:generate --force
php artisan migrate --force
php artisan config:cache
php artisan route:cache
php artisan view:cache
</code></pre>
<p>If your host does not provide CLI access, shared hosting becomes painful quickly. In that case, move to Forge or a VPS.</p>
<h2>Path 2: Deploy on VPS with Docker</h2>
<p>This is ideal when you want predictable environments and full control.</p>
<h3>1) Minimal <code>docker-compose.yml</code></h3>
<pre class="language-yaml"><code class="language-yaml"><span class="token key atrule">services</span><span class="token punctuation">:</span>
  <span class="token key atrule">app</span><span class="token punctuation">:</span>
    <span class="token key atrule">image</span><span class="token punctuation">:</span> ghcr.io/your<span class="token punctuation">-</span>org/laravel<span class="token punctuation">-</span>app<span class="token punctuation">:</span>latest
    <span class="token key atrule">env_file</span><span class="token punctuation">:</span>
      <span class="token punctuation">-</span> .env
    <span class="token key atrule">depends_on</span><span class="token punctuation">:</span>
      <span class="token punctuation">-</span> mysql
      <span class="token punctuation">-</span> redis
    <span class="token key atrule">networks</span><span class="token punctuation">:</span>
      <span class="token punctuation">-</span> app<span class="token punctuation">-</span>net

  <span class="token key atrule">nginx</span><span class="token punctuation">:</span>
    <span class="token key atrule">image</span><span class="token punctuation">:</span> nginx<span class="token punctuation">:</span>alpine
    <span class="token key atrule">ports</span><span class="token punctuation">:</span>
      <span class="token punctuation">-</span> <span class="token string">"80:80"</span>
      <span class="token punctuation">-</span> <span class="token string">"443:443"</span>
    <span class="token key atrule">volumes</span><span class="token punctuation">:</span>
      <span class="token punctuation">-</span> ./docker/nginx/default.conf<span class="token punctuation">:</span>/etc/nginx/conf.d/default.conf<span class="token punctuation">:</span>ro
    <span class="token key atrule">depends_on</span><span class="token punctuation">:</span>
      <span class="token punctuation">-</span> app
    <span class="token key atrule">networks</span><span class="token punctuation">:</span>
      <span class="token punctuation">-</span> app<span class="token punctuation">-</span>net

  <span class="token key atrule">mysql</span><span class="token punctuation">:</span>
    <span class="token key atrule">image</span><span class="token punctuation">:</span> mysql<span class="token punctuation">:</span><span class="token number">8</span>
    <span class="token key atrule">environment</span><span class="token punctuation">:</span>
      <span class="token key atrule">MYSQL_DATABASE</span><span class="token punctuation">:</span> laravel
      <span class="token key atrule">MYSQL_USER</span><span class="token punctuation">:</span> laravel
      <span class="token key atrule">MYSQL_PASSWORD</span><span class="token punctuation">:</span> secret
      <span class="token key atrule">MYSQL_ROOT_PASSWORD</span><span class="token punctuation">:</span> root<span class="token punctuation">-</span>secret
    <span class="token key atrule">volumes</span><span class="token punctuation">:</span>
      <span class="token punctuation">-</span> mysql<span class="token punctuation">-</span>data<span class="token punctuation">:</span>/var/lib/mysql
    <span class="token key atrule">networks</span><span class="token punctuation">:</span>
      <span class="token punctuation">-</span> app<span class="token punctuation">-</span>net

  <span class="token key atrule">redis</span><span class="token punctuation">:</span>
    <span class="token key atrule">image</span><span class="token punctuation">:</span> redis<span class="token punctuation">:</span>7<span class="token punctuation">-</span>alpine
    <span class="token key atrule">networks</span><span class="token punctuation">:</span>
      <span class="token punctuation">-</span> app<span class="token punctuation">-</span>net

<span class="token key atrule">networks</span><span class="token punctuation">:</span>
  <span class="token key atrule">app-net</span><span class="token punctuation">:</span>

<span class="token key atrule">volumes</span><span class="token punctuation">:</span>
  <span class="token key atrule">mysql-data</span><span class="token punctuation">:</span>
</code></pre>
<h3>2) Build and release pipeline</h3>
<p>Use CI to build image, then pull on server.</p>
<pre class="language-bash"><code class="language-bash"><span class="token function">docker</span> compose pull
<span class="token function">docker</span> compose up -d --remove-orphans
<span class="token function">docker</span> compose <span class="token builtin class-name">exec</span> app php artisan migrate --force
<span class="token function">docker</span> compose <span class="token builtin class-name">exec</span> app php artisan config:cache
</code></pre>
<h3>3) Queue workers and scheduler</h3>
<p>Run queue workers using Supervisor or an extra container.</p>
<p>Scheduler example (cron on host):</p>
<pre class="language-bash"><code class="language-bash">* * * * * <span class="token builtin class-name">cd</span> /var/www/laravel <span class="token operator">&amp;&amp;</span> <span class="token function">docker</span> compose <span class="token builtin class-name">exec</span> -T app php artisan schedule:run <span class="token operator">&gt;&gt;</span> /dev/null <span class="token operator"><span class="token file-descriptor important">2</span>&gt;</span><span class="token file-descriptor important">&amp;1</span>
</code></pre>
<h2>Path 3: Deploy with Laravel Forge</h2>
<p>Forge is the fastest way to run Laravel on managed VPS infrastructure.</p>
<p>Core setup:</p>
<ol>
<li>Provision server in Forge.</li>
<li>Create a site and connect your Git repo.</li>
<li>Add environment variables in Forge dashboard.</li>
<li>Configure deployment script.</li>
<li>Enable queue worker and scheduler.</li>
</ol>
<p>Sample Forge deploy script:</p>
<pre class="language-bash"><code class="language-bash"><span class="token builtin class-name">cd</span> /home/forge/yourdomain.com

<span class="token function">git</span> pull origin main
<span class="token function">composer</span> <span class="token function">install</span> --no-interaction --prefer-dist --optimize-autoloader --no-dev
php artisan migrate --force
php artisan config:cache
php artisan route:cache
php artisan view:cache
php artisan queue:restart
</code></pre>
<p>For many teams, Forge gives the best speed-to-production balance.</p>
<h2>Zero-downtime deployment basics</h2>
<p>Even simple apps should avoid visible downtime during deploy.</p>
<p>Good practices:</p>
<ul>
<li>Build assets before switch-over.</li>
<li>Run migrations that are backward-compatible first.</li>
<li>Restart workers after code deploy.</li>
<li>Avoid destructive schema changes in a single release step.</li>
</ul>
<p>If using symlink-based release directories, switch symlink only after health checks pass.</p>
<h2>Security and reliability hardening</h2>
<p>Minimum production hardening:</p>
<ul>
<li>Enable firewall (<code>ufw</code>) and only allow required ports.</li>
<li>Disable password SSH login, use SSH keys.</li>
<li>Enforce TLS certificates (LetsEncrypt).</li>
<li>Back up MySQL daily and test restore.</li>
<li>Use separate DB users per environment.</li>
</ul>
<p>Monitor these metrics:</p>
<ul>
<li>5xx error rate</li>
<li>Queue lag</li>
<li>DB CPU and slow queries</li>
<li>Disk usage and backup success</li>
</ul>
<h2>Common deployment mistakes</h2>
<table><thead><tr><th>Mistake</th><th>Symptom</th><th>Fix</th></tr></thead><tbody><tr><td><code>APP_DEBUG=true</code> in prod</td><td>Stack traces visible publicly</td><td>Set false and clear config cache</td></tr><tr><td>Missing write permissions</td><td>Logs/sessions fail</td><td>Fix owner/group and permissions</td></tr><tr><td>Not restarting workers</td><td>Old jobs run stale code</td><td><code>php artisan queue:restart</code></td></tr><tr><td>Running unsafe migrations</td><td>App errors during deploy</td><td>Use backward-compatible migrations</td></tr><tr><td>No health check route</td><td>Hard to verify release</td><td>Add <code>/health</code> endpoint</td></tr></tbody></table>
<h2>Post-deploy verification checklist</h2>
<p>After each deploy:</p>
<ol>
<li>Open <code>/health</code> and main app routes.</li>
<li>Confirm migrations succeeded.</li>
<li>Confirm queue jobs process successfully.</li>
<li>Check logs for exceptions in first 10 minutes.</li>
<li>Confirm scheduler runs at least one cycle.</li>
</ol>
<p>A quick smoke test after each release catches most production incidents early.</p>
<h2>FAQ</h2>
<h3>Which deployment path should I pick in 2026?</h3>
<p>If you want speed and less infrastructure overhead, pick Forge. If you need full control and portability, pick VPS + Docker. Use shared hosting only for small low-risk projects.</p>
<h3>Do I need Docker for Laravel deployment?</h3>
<p>No, but Docker improves consistency across environments and teams.</p>
<h3>Should I run <code>php artisan migrate --force</code> on every deploy?</h3>
<p>Usually yes, as part of your deployment script, as long as your migrations are safe and reviewed.</p>
<h3>How do I avoid downtime during schema changes?</h3>
<p>Use additive, backward-compatible migrations first, deploy code that supports both old and new schema, then clean up in a later release.</p>
<h2>Final takeaway</h2>
<p>Laravel deployment is easiest when you standardize your process. Pick one path, automate it, and verify every release with a checklist. In Laravel 13, consistent environment handling and safe migrations are what separate stable deployments from stressful ones.</p>
<p>Next in this series: <a href="/articles/laravel-db-raw-guide-2026">How to Run Raw SQL Queries in Laravel with DB::raw</a> and the upcoming Laravel Project Lifecycle pillar.</p>]]></content:encoded>
            <author>kenmsh@gmail.com (Kennedy Mutisya)</author>
        </item>
        <item>
            <title><![CDATA[How to Run Raw SQL Queries in Laravel with DB::raw (2026 Guide)]]></title>
            <link>mutisyadev.xyz/articles/laravel-db-raw-guide-2026</link>
            <guid>mutisyadev.xyz/articles/laravel-db-raw-guide-2026</guid>
            <pubDate>Fri, 20 Mar 2026 00:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<h1>How to Run Raw SQL Queries in Laravel with DB::raw (2026 Guide)</h1>
<p>If you are building real Laravel apps, there will be moments when Eloquent alone is not enough. That is where <code>DB::raw</code> and raw query helpers come in. In this guide, you will learn exactly when to use Laravel <code>db raw</code> patterns in Laravel 13 with MySQL 8, how to keep them safe, and how to avoid performance mistakes.</p>
<p>This is part of a broader Laravel project lifecycle series. If you are just getting started, read the upcoming guides on <a href="/articles/create-laravel-project-2026">creating a Laravel project</a>, <a href="/articles/laravel-env-explained-2026">managing <code>.env</code> safely</a>, and <a href="/articles/deploy-laravel-project-2026">deployment patterns</a>.</p>
<h2>What is DB::raw in Laravel?</h2>
<p><code>DB::raw</code> lets you pass a raw SQL expression into the query builder when Laravel does not provide a clean fluent method for what you need.</p>
<p>Use it when you need:</p>
<ul>
<li>SQL expressions like <code>CASE</code>, <code>COALESCE</code>, or arithmetic operations.</li>
<li>Database-specific functions in MySQL 8.</li>
<li>Advanced ordering and reporting queries.</li>
</ul>
<p>Avoid it when a regular query builder or Eloquent method can express the same query clearly.</p>
<h2>DB::raw vs selectRaw vs whereRaw vs Eloquent</h2>
<p>Quick rule of thumb:</p>
<table><thead><tr><th>Use case</th><th>Best option</th></tr></thead><tbody><tr><td>Standard CRUD and relationships</td><td>Eloquent</td></tr><tr><td>Normal filters and joins</td><td>Query Builder</td></tr><tr><td>Computed columns (<code>CASE</code>, <code>SUM(price * qty)</code>)</td><td><code>selectRaw</code> or <code>DB::raw</code></td></tr><tr><td>Custom SQL predicates</td><td><code>whereRaw</code> / <code>havingRaw</code> with bindings</td></tr><tr><td>Complex reports with grouping</td><td><code>selectRaw</code> + <code>groupByRaw</code></td></tr></tbody></table>
<p>In practice, prefer <code>selectRaw</code>, <code>whereRaw</code>, and friends over directly scattering <code>DB::raw</code> everywhere. They are easier to read and support parameter bindings in a predictable way.</p>
<h2>Example 1: Aggregate totals with aliases</h2>
<p>You need monthly revenue and order count for a dashboard.</p>
<pre class="language-php"><code class="language-php"><span class="token keyword">use</span> <span class="token package">Illuminate<span class="token punctuation">\</span>Support<span class="token punctuation">\</span>Facades<span class="token punctuation">\</span>DB</span><span class="token punctuation">;</span>

<span class="token variable">$stats</span> <span class="token operator">=</span> <span class="token scope">DB<span class="token punctuation">::</span></span><span class="token function">table</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'orders'</span><span class="token punctuation">)</span>
    <span class="token operator">-&gt;</span><span class="token function">selectRaw</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'DATE_FORMAT(created_at, "%Y-%m") as month'</span><span class="token punctuation">)</span>
    <span class="token operator">-&gt;</span><span class="token function">selectRaw</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'COUNT(*) as orders_count'</span><span class="token punctuation">)</span>
    <span class="token operator">-&gt;</span><span class="token function">selectRaw</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'SUM(total_amount) as revenue_total'</span><span class="token punctuation">)</span>
    <span class="token operator">-&gt;</span><span class="token function">where</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'status'</span><span class="token punctuation">,</span> <span class="token string single-quoted-string">'paid'</span><span class="token punctuation">)</span>
    <span class="token operator">-&gt;</span><span class="token function">groupByRaw</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'DATE_FORMAT(created_at, "%Y-%m")'</span><span class="token punctuation">)</span>
    <span class="token operator">-&gt;</span><span class="token function">orderBy</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'month'</span><span class="token punctuation">)</span>
    <span class="token operator">-&gt;</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre>
<p>Why raw here? MySQL date formatting for grouped reporting is easier in SQL than in PHP post-processing.</p>
<h2>Example 2: Computed status column with CASE</h2>
<p>You want a label derived from multiple columns.</p>
<pre class="language-php"><code class="language-php"><span class="token variable">$users</span> <span class="token operator">=</span> <span class="token scope">DB<span class="token punctuation">::</span></span><span class="token function">table</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'users'</span><span class="token punctuation">)</span>
    <span class="token operator">-&gt;</span><span class="token function">select</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'id'</span><span class="token punctuation">,</span> <span class="token string single-quoted-string">'name'</span><span class="token punctuation">,</span> <span class="token string single-quoted-string">'last_login_at'</span><span class="token punctuation">)</span>
    <span class="token operator">-&gt;</span><span class="token function">selectRaw</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'
        CASE
            WHEN last_login_at IS NULL THEN "never"
            WHEN last_login_at &gt;= NOW() - INTERVAL 30 DAY THEN "active"
            ELSE "inactive"
        END as activity_status
    '</span><span class="token punctuation">)</span>
    <span class="token operator">-&gt;</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre>
<p>This keeps business reporting logic in one query and avoids loops over large datasets.</p>
<h2>Example 3: Custom sorting with orderByRaw</h2>
<p>Sort by business priority first, then by recency.</p>
<pre class="language-php"><code class="language-php"><span class="token variable">$tickets</span> <span class="token operator">=</span> <span class="token scope">DB<span class="token punctuation">::</span></span><span class="token function">table</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'support_tickets'</span><span class="token punctuation">)</span>
    <span class="token operator">-&gt;</span><span class="token function">select</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'id'</span><span class="token punctuation">,</span> <span class="token string single-quoted-string">'subject'</span><span class="token punctuation">,</span> <span class="token string single-quoted-string">'priority'</span><span class="token punctuation">,</span> <span class="token string single-quoted-string">'created_at'</span><span class="token punctuation">)</span>
    <span class="token operator">-&gt;</span><span class="token function">orderByRaw</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'
        FIELD(priority, "critical", "high", "medium", "low")
    '</span><span class="token punctuation">)</span>
    <span class="token operator">-&gt;</span><span class="token function">orderByDesc</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'created_at'</span><span class="token punctuation">)</span>
    <span class="token operator">-&gt;</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre>
<p><code>FIELD(...)</code> is a MySQL-specific helper that is perfect for custom order rules.</p>
<h2>Example 4: Grouped reports with havingRaw</h2>
<p>Find product categories with meaningful sales volume.</p>
<pre class="language-php"><code class="language-php"><span class="token variable">$categories</span> <span class="token operator">=</span> <span class="token scope">DB<span class="token punctuation">::</span></span><span class="token function">table</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'order_items'</span><span class="token punctuation">)</span>
    <span class="token operator">-&gt;</span><span class="token function">join</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'products'</span><span class="token punctuation">,</span> <span class="token string single-quoted-string">'products.id'</span><span class="token punctuation">,</span> <span class="token string single-quoted-string">'='</span><span class="token punctuation">,</span> <span class="token string single-quoted-string">'order_items.product_id'</span><span class="token punctuation">)</span>
    <span class="token operator">-&gt;</span><span class="token function">select</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'products.category_id'</span><span class="token punctuation">)</span>
    <span class="token operator">-&gt;</span><span class="token function">selectRaw</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'COUNT(*) as line_count'</span><span class="token punctuation">)</span>
    <span class="token operator">-&gt;</span><span class="token function">selectRaw</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'SUM(order_items.quantity * order_items.unit_price) as gross_sales'</span><span class="token punctuation">)</span>
    <span class="token operator">-&gt;</span><span class="token function">groupBy</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'products.category_id'</span><span class="token punctuation">)</span>
    <span class="token operator">-&gt;</span><span class="token function">havingRaw</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'SUM(order_items.quantity * order_items.unit_price) &gt; ?'</span><span class="token punctuation">,</span> <span class="token punctuation">[</span><span class="token number">10000</span><span class="token punctuation">]</span><span class="token punctuation">)</span>
    <span class="token operator">-&gt;</span><span class="token function">orderByDesc</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'gross_sales'</span><span class="token punctuation">)</span>
    <span class="token operator">-&gt;</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre>
<p>Notice the binding in <code>havingRaw</code>. Never interpolate dynamic values into raw SQL strings.</p>
<h2>Example 5: Safe whereRaw with bindings</h2>
<p>Use raw conditions with placeholders, not string concatenation.</p>
<pre class="language-php"><code class="language-php"><span class="token variable">$minAge</span> <span class="token operator">=</span> <span class="token number">21</span><span class="token punctuation">;</span>
<span class="token variable">$country</span> <span class="token operator">=</span> <span class="token string single-quoted-string">'KE'</span><span class="token punctuation">;</span>

<span class="token variable">$customers</span> <span class="token operator">=</span> <span class="token scope">DB<span class="token punctuation">::</span></span><span class="token function">table</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'customers'</span><span class="token punctuation">)</span>
    <span class="token operator">-&gt;</span><span class="token function">whereRaw</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'TIMESTAMPDIFF(YEAR, date_of_birth, CURDATE()) &gt;= ?'</span><span class="token punctuation">,</span> <span class="token punctuation">[</span><span class="token variable">$minAge</span><span class="token punctuation">]</span><span class="token punctuation">)</span>
    <span class="token operator">-&gt;</span><span class="token function">whereRaw</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'country_code = ?'</span><span class="token punctuation">,</span> <span class="token punctuation">[</span><span class="token variable">$country</span><span class="token punctuation">]</span><span class="token punctuation">)</span>
    <span class="token operator">-&gt;</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre>
<p>This pattern protects you from SQL injection and keeps query plans reusable.</p>
<h2>Security: how to use raw SQL safely</h2>
<p>When people say raw SQL is dangerous in Laravel, they usually mean unsafe interpolation.</p>
<p>Good:</p>
<pre class="language-php"><code class="language-php"><span class="token scope">DB<span class="token punctuation">::</span></span><span class="token function">table</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'users'</span><span class="token punctuation">)</span><span class="token operator">-&gt;</span><span class="token function">whereRaw</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'email = ?'</span><span class="token punctuation">,</span> <span class="token punctuation">[</span><span class="token variable">$email</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token operator">-&gt;</span><span class="token function">first</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre>
<p>Bad:</p>
<pre class="language-php"><code class="language-php"><span class="token scope">DB<span class="token punctuation">::</span></span><span class="token function">table</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'users'</span><span class="token punctuation">)</span><span class="token operator">-&gt;</span><span class="token function">whereRaw</span><span class="token punctuation">(</span><span class="token string double-quoted-string">"email = '<span class="token interpolation"><span class="token variable">$email</span></span>'"</span><span class="token punctuation">)</span><span class="token operator">-&gt;</span><span class="token function">first</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre>
<p>Use this checklist whenever you write raw SQL:</p>
<ul>
<li>Always pass dynamic values as bindings (<code>?</code>, then array values).</li>
<li>Keep raw fragments small and focused.</li>
<li>Validate and sanitize upstream inputs as usual.</li>
<li>Never allow unchecked user input to control SQL keywords (column names, <code>ORDER BY</code>, etc.).</li>
</ul>
<p>For framework-level guidance, see the official Laravel query documentation: <a href="https://laravel.com/docs/queries">Laravel Query Builder</a>.</p>
<h2>Performance notes for MySQL 8</h2>
<p>Raw SQL can be faster or slower depending on how you write it. Treat performance as a measurement problem.</p>
<ol>
<li>Inspect execution plans with <code>EXPLAIN</code> for expensive queries.</li>
<li>Add indexes that match your filter and join patterns.</li>
<li>Avoid wrapping indexed columns in functions inside <code>WHERE</code> if you need index usage.</li>
<li>Aggregate in SQL when possible, but avoid over-fetching rows.</li>
</ol>
<p>You can profile query timings quickly in local development:</p>
<pre class="language-php"><code class="language-php"><span class="token scope">DB<span class="token punctuation">::</span></span><span class="token function">listen</span><span class="token punctuation">(</span><span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token variable">$query</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token function">logger</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token operator">-&gt;</span><span class="token function">info</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'sql'</span><span class="token punctuation">,</span> <span class="token punctuation">[</span>
        <span class="token string single-quoted-string">'sql'</span> <span class="token operator">=&gt;</span> <span class="token variable">$query</span><span class="token operator">-&gt;</span><span class="token property">sql</span><span class="token punctuation">,</span>
        <span class="token string single-quoted-string">'bindings'</span> <span class="token operator">=&gt;</span> <span class="token variable">$query</span><span class="token operator">-&gt;</span><span class="token property">bindings</span><span class="token punctuation">,</span>
        <span class="token string single-quoted-string">'time_ms'</span> <span class="token operator">=&gt;</span> <span class="token variable">$query</span><span class="token operator">-&gt;</span><span class="token property">time</span><span class="token punctuation">,</span>
    <span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre>
<h2>Testing raw query logic in Laravel 13</h2>
<p>For reporting queries, test both shape and values.</p>
<pre class="language-php"><code class="language-php"><span class="token function">it</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'returns active users first in priority report'</span><span class="token punctuation">,</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token comment">// Arrange test records...</span>

    <span class="token variable">$rows</span> <span class="token operator">=</span> <span class="token scope">DB<span class="token punctuation">::</span></span><span class="token function">table</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'users'</span><span class="token punctuation">)</span>
        <span class="token operator">-&gt;</span><span class="token function">select</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'id'</span><span class="token punctuation">)</span>
        <span class="token operator">-&gt;</span><span class="token function">orderByRaw</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'FIELD(status, "active", "invited", "disabled")'</span><span class="token punctuation">)</span>
        <span class="token operator">-&gt;</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

    <span class="token function">expect</span><span class="token punctuation">(</span><span class="token variable">$rows</span><span class="token punctuation">)</span><span class="token operator">-&gt;</span><span class="token property">not</span><span class="token operator">-&gt;</span><span class="token function">toBeEmpty</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token function">expect</span><span class="token punctuation">(</span><span class="token variable">$rows</span><span class="token operator">-&gt;</span><span class="token function">first</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token operator">-&gt;</span><span class="token property">id</span><span class="token punctuation">)</span><span class="token operator">-&gt;</span><span class="token function">toBe</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre>
<p>If you have a complex query, consider wrapping it in a repository method so your tests target one boundary instead of duplicating SQL across the codebase.</p>
<h2>Common mistakes and fixes</h2>
<table><thead><tr><th>Mistake</th><th>Why it hurts</th><th>Better approach</th></tr></thead><tbody><tr><td>Building SQL with string concatenation</td><td>SQL injection risk</td><td>Use placeholders and bindings</td></tr><tr><td>Overusing raw for simple filters</td><td>Harder to maintain</td><td>Use query builder for basics</td></tr><tr><td>Raw expressions in many controllers</td><td>Logic spread and hard to test</td><td>Move query logic to service/repository</td></tr><tr><td>Ignoring query plans</td><td>Slow pages and reports</td><td>Use <code>EXPLAIN</code> and proper indexes</td></tr><tr><td>Mixing business rules and SQL everywhere</td><td>Reduced readability</td><td>Keep raw fragments focused and named</td></tr></tbody></table>
<h2>FAQ</h2>
<h3>Is DB::raw bad practice?</h3>
<p>No. It is a tool. It becomes a problem only when overused or written unsafely. Use raw SQL when it improves clarity or performance, and keep standard queries in Eloquent/query builder.</p>
<h3>Can I use bindings with raw helpers?</h3>
<p>Yes. <code>whereRaw</code>, <code>havingRaw</code>, <code>selectRaw</code>, and similar helpers support bindings. Prefer them over manually injecting variables into SQL strings.</p>
<h3>Should I use DB::raw for everything advanced?</h3>
<p>Not always. Start with query builder or Eloquent. Use raw expressions for the parts that are truly SQL-specific.</p>
<h3>Does DB::raw lock me into MySQL?</h3>
<p>Sometimes. Functions like <code>FIELD</code> and <code>DATE_FORMAT</code> are MySQL-specific. If portability matters, isolate database-specific logic behind dedicated query methods.</p>
<h2>Final takeaway</h2>
<p>If you use Laravel <code>db raw</code> patterns carefully, they are an advantage, not a risk. In Laravel 13 with MySQL 8, raw expressions are especially useful for reporting, computed fields, and custom sorting, as long as you bind values and verify performance.</p>
<p>Next in this series: <a href="/articles/create-laravel-project-2026">Create a New Laravel Project in 2026</a>.</p>]]></content:encoded>
            <author>kenmsh@gmail.com (Kennedy Mutisya)</author>
        </item>
        <item>
            <title><![CDATA[Laravel .env Explained: Secure Config Files for Production (2026)]]></title>
            <link>mutisyadev.xyz/articles/laravel-env-explained-2026</link>
            <guid>mutisyadev.xyz/articles/laravel-env-explained-2026</guid>
            <pubDate>Fri, 20 Feb 2026 00:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<h1>Laravel .env Explained: Secure Config Files for Production (2026)</h1>
<p>If you use Laravel 13 in real projects, your <code>.env</code> file becomes one of the most important files in the entire app. It controls application behavior, database access, mail credentials, queues, cache drivers, and more.</p>
<p>In this guide, you will learn how Laravel environment variables work, how to avoid common mistakes, and how to safely manage <code>.env</code> from local development to production.</p>
<h2>What is <code>.env</code> in Laravel?</h2>
<p>The <code>.env</code> file stores environment-specific values outside your codebase. Laravel reads these values at boot, then maps them into config files using <code>env()</code> calls.</p>
<p>Example:</p>
<pre class="language-ini"><code class="language-ini"><span class="token key attr-name">APP_NAME</span><span class="token punctuation">=</span><span class="token value attr-value">"<span class="token inner-value">My Laravel App</span>"</span>
<span class="token key attr-name">APP_ENV</span><span class="token punctuation">=</span><span class="token value attr-value">local</span>
<span class="token key attr-name">APP_DEBUG</span><span class="token punctuation">=</span><span class="token value attr-value">true</span>
<span class="token key attr-name">APP_URL</span><span class="token punctuation">=</span><span class="token value attr-value">http://localhost</span>
</code></pre>
<p>This keeps secrets and environment differences out of source-controlled PHP files.</p>
<h2>How Laravel uses environment values</h2>
<p>Laravel does not expect you to call <code>env()</code> all over your app code. Best practice is:</p>
<ol>
<li>Read env vars in config files only (<code>config/*.php</code>).</li>
<li>Access values in app code via <code>config('...')</code>.</li>
</ol>
<p>Example in <code>config/database.php</code>:</p>
<pre class="language-php"><code class="language-php"><span class="token string single-quoted-string">'mysql'</span> <span class="token operator">=&gt;</span> <span class="token punctuation">[</span>
    <span class="token string single-quoted-string">'driver'</span> <span class="token operator">=&gt;</span> <span class="token string single-quoted-string">'mysql'</span><span class="token punctuation">,</span>
    <span class="token string single-quoted-string">'host'</span> <span class="token operator">=&gt;</span> <span class="token function">env</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'DB_HOST'</span><span class="token punctuation">,</span> <span class="token string single-quoted-string">'127.0.0.1'</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
    <span class="token string single-quoted-string">'port'</span> <span class="token operator">=&gt;</span> <span class="token function">env</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'DB_PORT'</span><span class="token punctuation">,</span> <span class="token string single-quoted-string">'3306'</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
    <span class="token string single-quoted-string">'database'</span> <span class="token operator">=&gt;</span> <span class="token function">env</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'DB_DATABASE'</span><span class="token punctuation">,</span> <span class="token string single-quoted-string">'laravel'</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
    <span class="token string single-quoted-string">'username'</span> <span class="token operator">=&gt;</span> <span class="token function">env</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'DB_USERNAME'</span><span class="token punctuation">,</span> <span class="token string single-quoted-string">'root'</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
    <span class="token string single-quoted-string">'password'</span> <span class="token operator">=&gt;</span> <span class="token function">env</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'DB_PASSWORD'</span><span class="token punctuation">,</span> <span class="token string single-quoted-string">''</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
<span class="token punctuation">]</span><span class="token punctuation">,</span>
</code></pre>
<p>Then in your app logic:</p>
<pre class="language-php"><code class="language-php"><span class="token variable">$driver</span> <span class="token operator">=</span> <span class="token function">config</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'database.default'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre>
<p>This avoids surprises when you cache config in production.</p>
<h2>The <code>env()</code> helper: when to use it and when not to</h2>
<p>The <code>env()</code> helper is for configuration loading, not general runtime logic.</p>
<p>Use it here:</p>
<ul>
<li>Inside files in <code>config/*.php</code>.</li>
<li>While defining default configuration values.</li>
</ul>
<p>Avoid it here:</p>
<ul>
<li>Controllers, services, jobs, commands, and listeners.</li>
<li>Blade templates and domain logic.</li>
</ul>
<p>Good pattern:</p>
<pre class="language-php"><code class="language-php"><span class="token comment">// config/services.php</span>
<span class="token string single-quoted-string">'postmark'</span> <span class="token operator">=&gt;</span> <span class="token punctuation">[</span>
    <span class="token string single-quoted-string">'token'</span> <span class="token operator">=&gt;</span> <span class="token function">env</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'POSTMARK_TOKEN'</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
<span class="token punctuation">]</span><span class="token punctuation">,</span>

<span class="token comment">// app/Services/MailerService.php</span>
<span class="token variable">$token</span> <span class="token operator">=</span> <span class="token function">config</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'services.postmark.token'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre>
<p>Why this matters: after <code>php artisan config:cache</code>, Laravel reads cached config values, so direct runtime <code>env()</code> calls can return unexpected values.</p>
<h2>Environment detection in Laravel</h2>
<p>Laravel primarily determines environment via <code>APP_ENV</code>, and you can inspect it using:</p>
<pre class="language-php"><code class="language-php"><span class="token function">app</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token operator">-&gt;</span><span class="token function">environment</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token function">app</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token operator">-&gt;</span><span class="token function">environment</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'local'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre>
<p>For CLI execution, Laravel also supports explicit environment overrides:</p>
<pre class="language-bash"><code class="language-bash">php artisan migrate --env<span class="token operator">=</span>staging
php artisan migrate --env staging
</code></pre>
<p>Behavior to remember (matching framework detector tests):</p>
<ul>
<li><code>--env=local</code> uses <code>local</code>.</li>
<li><code>--env local</code> uses <code>local</code>.</li>
<li><code>--env</code> with no value falls back to normal detection.</li>
<li>Similar flags like <code>--envelope=mail</code> are ignored.</li>
</ul>
<p>You can also provide custom detection logic in lower-level bootstrapping scenarios:</p>
<pre class="language-php"><code class="language-php"><span class="token keyword">use</span> <span class="token package">Illuminate<span class="token punctuation">\</span>Foundation<span class="token punctuation">\</span>EnvironmentDetector</span><span class="token punctuation">;</span>

<span class="token variable">$detector</span> <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">EnvironmentDetector</span><span class="token punctuation">;</span>

<span class="token variable">$environment</span> <span class="token operator">=</span> <span class="token variable">$detector</span><span class="token operator">-&gt;</span><span class="token function">detect</span><span class="token punctuation">(</span><span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">return</span> <span class="token string single-quoted-string">'production'</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token global">$_SERVER</span><span class="token punctuation">[</span><span class="token string single-quoted-string">'argv'</span><span class="token punctuation">]</span> <span class="token operator">??</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre>
<p>In most applications, <code>APP_ENV</code> plus standard Laravel bootstrapping is enough.</p>
<h2>Encrypting and decrypting environment files</h2>
<p>For teams, Laravel supports encrypted environment files so secrets are not stored in plaintext when shared.</p>
<p>Encrypt your current <code>.env</code> file:</p>
<pre class="language-bash"><code class="language-bash">php artisan env:encrypt
</code></pre>
<p>Decrypt later:</p>
<pre class="language-bash"><code class="language-bash">php artisan env:decrypt
</code></pre>
<p>Useful notes:</p>
<ul>
<li>Encryption generates an encrypted env artifact (for example, <code>.env.encrypted</code>).</li>
<li>Decryption requires the correct key, usually provided via CLI option or environment variable.</li>
<li>Do not commit plaintext <code>.env</code>; if you commit encrypted env files, protect keys in your secret manager.</li>
</ul>
<p>This is especially useful when distributing secure defaults across multiple environments.</p>
<h2>Writing variables to an environment file programmatically</h2>
<p>In recent Laravel versions, you can write/update env variables using <code>Env::writeVariables</code>.</p>
<p>Example:</p>
<pre class="language-php"><code class="language-php"><span class="token keyword">use</span> <span class="token package">Illuminate<span class="token punctuation">\</span>Support<span class="token punctuation">\</span>Env</span><span class="token punctuation">;</span>

<span class="token scope">Env<span class="token punctuation">::</span></span><span class="token function">writeVariables</span><span class="token punctuation">(</span><span class="token punctuation">[</span>
    <span class="token string single-quoted-string">'APP_VIBE'</span> <span class="token operator">=&gt;</span> <span class="token string single-quoted-string">'chill'</span><span class="token punctuation">,</span>
    <span class="token string single-quoted-string">'DB_HOST'</span> <span class="token operator">=&gt;</span> <span class="token string single-quoted-string">'127:0:0:1'</span><span class="token punctuation">,</span>
    <span class="token string single-quoted-string">'DB_PORT'</span> <span class="token operator">=&gt;</span> <span class="token number">3306</span><span class="token punctuation">,</span>
    <span class="token string single-quoted-string">'BRAND_NEW_PREFIX'</span> <span class="token operator">=&gt;</span> <span class="token string single-quoted-string">'fresh value'</span><span class="token punctuation">,</span>
<span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token function">base_path</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'.env'</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre>
<p>Overwrite existing keys explicitly:</p>
<pre class="language-php"><code class="language-php"><span class="token scope">Env<span class="token punctuation">::</span></span><span class="token function">writeVariables</span><span class="token punctuation">(</span><span class="token punctuation">[</span>
    <span class="token string single-quoted-string">'DB_CONNECTION'</span> <span class="token operator">=&gt;</span> <span class="token string single-quoted-string">'sqlite'</span><span class="token punctuation">,</span>
    <span class="token string single-quoted-string">'DB_HOST'</span> <span class="token operator">=&gt;</span> <span class="token string single-quoted-string">'127:0:0:1'</span><span class="token punctuation">,</span>
<span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token function">base_path</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'.env'</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token constant boolean">true</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre>
<p>Practical guidance:</p>
<ul>
<li>Use this in setup/install flows, not normal request handling.</li>
<li>Keep backups when mutating env files in automation.</li>
<li>Validate generated env content in CI to avoid broken deploys.</li>
</ul>
<h2>Recommended <code>.env</code> baseline for Laravel 13 + MySQL 8</h2>
<pre class="language-ini"><code class="language-ini"><span class="token key attr-name">APP_NAME</span><span class="token punctuation">=</span><span class="token value attr-value">Laravel</span>
<span class="token key attr-name">APP_ENV</span><span class="token punctuation">=</span><span class="token value attr-value">local</span>
<span class="token key attr-name">APP_KEY</span><span class="token punctuation">=</span>
<span class="token key attr-name">APP_DEBUG</span><span class="token punctuation">=</span><span class="token value attr-value">true</span>
<span class="token key attr-name">APP_URL</span><span class="token punctuation">=</span><span class="token value attr-value">http://localhost</span>

<span class="token key attr-name">APP_LOCALE</span><span class="token punctuation">=</span><span class="token value attr-value">en</span>
<span class="token key attr-name">APP_FALLBACK_LOCALE</span><span class="token punctuation">=</span><span class="token value attr-value">en</span>
<span class="token key attr-name">APP_FAKER_LOCALE</span><span class="token punctuation">=</span><span class="token value attr-value">en_US</span>

<span class="token key attr-name">APP_MAINTENANCE_DRIVER</span><span class="token punctuation">=</span><span class="token value attr-value">file</span>
<span class="token comment"># APP_MAINTENANCE_STORE=database</span>

<span class="token comment"># PHP_CLI_SERVER_WORKERS=4</span>

<span class="token key attr-name">BCRYPT_ROUNDS</span><span class="token punctuation">=</span><span class="token value attr-value">12</span>

<span class="token key attr-name">LOG_CHANNEL</span><span class="token punctuation">=</span><span class="token value attr-value">stack</span>
<span class="token key attr-name">LOG_STACK</span><span class="token punctuation">=</span><span class="token value attr-value">single</span>
<span class="token key attr-name">LOG_DEPRECATIONS_CHANNEL</span><span class="token punctuation">=</span><span class="token value attr-value">null</span>
<span class="token key attr-name">LOG_LEVEL</span><span class="token punctuation">=</span><span class="token value attr-value">debug</span>

<span class="token key attr-name">DB_CONNECTION</span><span class="token punctuation">=</span><span class="token value attr-value">sqlite</span>
<span class="token comment"># DB_HOST=127.0.0.1</span>
<span class="token comment"># DB_PORT=3306</span>
<span class="token comment"># DB_DATABASE=laravel</span>
<span class="token comment"># DB_USERNAME=root</span>
<span class="token comment"># DB_PASSWORD=</span>

<span class="token key attr-name">SESSION_DRIVER</span><span class="token punctuation">=</span><span class="token value attr-value">database</span>
<span class="token key attr-name">SESSION_LIFETIME</span><span class="token punctuation">=</span><span class="token value attr-value">120</span>
<span class="token key attr-name">SESSION_ENCRYPT</span><span class="token punctuation">=</span><span class="token value attr-value">false</span>
<span class="token key attr-name">SESSION_PATH</span><span class="token punctuation">=</span><span class="token value attr-value">/</span>
<span class="token key attr-name">SESSION_DOMAIN</span><span class="token punctuation">=</span><span class="token value attr-value">null</span>

<span class="token key attr-name">BROADCAST_CONNECTION</span><span class="token punctuation">=</span><span class="token value attr-value">log</span>
<span class="token key attr-name">FILESYSTEM_DISK</span><span class="token punctuation">=</span><span class="token value attr-value">local</span>
<span class="token key attr-name">QUEUE_CONNECTION</span><span class="token punctuation">=</span><span class="token value attr-value">database</span>

<span class="token key attr-name">CACHE_STORE</span><span class="token punctuation">=</span><span class="token value attr-value">database</span>
<span class="token comment"># CACHE_PREFIX=</span>

<span class="token key attr-name">MEMCACHED_HOST</span><span class="token punctuation">=</span><span class="token value attr-value">127.0.0.1</span>

<span class="token key attr-name">REDIS_CLIENT</span><span class="token punctuation">=</span><span class="token value attr-value">phpredis</span>
<span class="token key attr-name">REDIS_HOST</span><span class="token punctuation">=</span><span class="token value attr-value">127.0.0.1</span>
<span class="token key attr-name">REDIS_PASSWORD</span><span class="token punctuation">=</span><span class="token value attr-value">null</span>
<span class="token key attr-name">REDIS_PORT</span><span class="token punctuation">=</span><span class="token value attr-value">6379</span>

<span class="token key attr-name">MAIL_MAILER</span><span class="token punctuation">=</span><span class="token value attr-value">log</span>
<span class="token key attr-name">MAIL_SCHEME</span><span class="token punctuation">=</span><span class="token value attr-value">null</span>
<span class="token key attr-name">MAIL_HOST</span><span class="token punctuation">=</span><span class="token value attr-value">127.0.0.1</span>
<span class="token key attr-name">MAIL_PORT</span><span class="token punctuation">=</span><span class="token value attr-value">2525</span>
<span class="token key attr-name">MAIL_USERNAME</span><span class="token punctuation">=</span><span class="token value attr-value">null</span>
<span class="token key attr-name">MAIL_PASSWORD</span><span class="token punctuation">=</span><span class="token value attr-value">null</span>
<span class="token key attr-name">MAIL_FROM_ADDRESS</span><span class="token punctuation">=</span><span class="token value attr-value">"<span class="token inner-value">hello@example.com</span>"</span>
<span class="token key attr-name">MAIL_FROM_NAME</span><span class="token punctuation">=</span><span class="token value attr-value">"<span class="token inner-value">${APP_NAME}</span>"</span>

<span class="token key attr-name">AWS_ACCESS_KEY_ID</span><span class="token punctuation">=</span>
<span class="token key attr-name">AWS_SECRET_ACCESS_KEY</span><span class="token punctuation">=</span>
<span class="token key attr-name">AWS_DEFAULT_REGION</span><span class="token punctuation">=</span><span class="token value attr-value">us-east-1</span>
<span class="token key attr-name">AWS_BUCKET</span><span class="token punctuation">=</span>
<span class="token key attr-name">AWS_USE_PATH_STYLE_ENDPOINT</span><span class="token punctuation">=</span><span class="token value attr-value">false</span>

<span class="token key attr-name">VITE_APP_NAME</span><span class="token punctuation">=</span><span class="token value attr-value">"<span class="token inner-value">${APP_NAME}</span>"</span>
</code></pre>
<p>For Docker/Sail setups, <code>DB_HOST=mysql</code> is correct because <code>mysql</code> is the container service name.</p>
<h2><code>.env</code> vs <code>.env.example</code> (and why both matter)</h2>
<ul>
<li><code>.env</code> is your real, local or server-specific config file.</li>
<li><code>.env.example</code> is the template committed to git for teammates and CI.</li>
</ul>
<p>Every key in <code>.env</code> that others need should exist in <code>.env.example</code> with a safe placeholder.</p>
<p>Example:</p>
<pre class="language-ini"><code class="language-ini"><span class="token key attr-name">APP_NAME</span><span class="token punctuation">=</span><span class="token value attr-value">"<span class="token inner-value">Laravel Starter</span>"</span>
<span class="token key attr-name">APP_ENV</span><span class="token punctuation">=</span><span class="token value attr-value">local</span>
<span class="token key attr-name">APP_KEY</span><span class="token punctuation">=</span>
<span class="token key attr-name">APP_DEBUG</span><span class="token punctuation">=</span><span class="token value attr-value">true</span>
<span class="token key attr-name">APP_URL</span><span class="token punctuation">=</span><span class="token value attr-value">http://localhost</span>

<span class="token key attr-name">DB_CONNECTION</span><span class="token punctuation">=</span><span class="token value attr-value">mysql</span>
<span class="token key attr-name">DB_HOST</span><span class="token punctuation">=</span><span class="token value attr-value">127.0.0.1</span>
<span class="token key attr-name">DB_PORT</span><span class="token punctuation">=</span><span class="token value attr-value">3306</span>
<span class="token key attr-name">DB_DATABASE</span><span class="token punctuation">=</span><span class="token value attr-value">laravel</span>
<span class="token key attr-name">DB_USERNAME</span><span class="token punctuation">=</span><span class="token value attr-value">root</span>
<span class="token key attr-name">DB_PASSWORD</span><span class="token punctuation">=</span>
</code></pre>
<h2>Critical security rules for <code>.env</code></h2>
<p>Treat your <code>.env</code> like credentials, because it often contains credentials.</p>
<ul>
<li>Never commit <code>.env</code> to git.</li>
<li>Never paste secrets in screenshots, PR descriptions, or logs.</li>
<li>Rotate keys immediately if exposure happens.</li>
<li>Use separate credentials per environment (local, staging, production).</li>
<li>Keep production secrets in a secret manager or platform env settings.</li>
</ul>
<p>Quick check:</p>
<pre class="language-bash"><code class="language-bash"><span class="token function">git</span> check-ignore -v .env
</code></pre>
<p>If <code>.env</code> is not ignored, fix <code>.gitignore</code> before doing anything else.</p>
<h2>Why <code>APP_DEBUG</code> must be false in production</h2>
<p><code>APP_DEBUG=true</code> can leak sensitive internals (queries, stack traces, server paths).</p>
<p>Production minimum:</p>
<pre class="language-ini"><code class="language-ini"><span class="token key attr-name">APP_ENV</span><span class="token punctuation">=</span><span class="token value attr-value">production</span>
<span class="token key attr-name">APP_DEBUG</span><span class="token punctuation">=</span><span class="token value attr-value">false</span>
<span class="token key attr-name">APP_URL</span><span class="token punctuation">=</span><span class="token value attr-value">https://yourdomain.com</span>
</code></pre>
<p>That one setting alone prevents a lot of accidental data exposure.</p>
<h2>Config cache and env gotchas</h2>
<p>When you run:</p>
<pre class="language-bash"><code class="language-bash">php artisan config:cache
</code></pre>
<p>Laravel compiles configuration into a cached file. If you update <code>.env</code>, changes may not be reflected until you clear/rebuild cache.</p>
<p>Useful commands:</p>
<pre class="language-bash"><code class="language-bash">php artisan config:clear
php artisan cache:clear
php artisan config:cache
</code></pre>
<p>If your app seems to ignore <code>.env</code> updates, cached config is usually the reason.</p>
<h2>Local, staging, and production strategy</h2>
<p>A practical approach for 2026:</p>
<ul>
<li><strong>Local:</strong> <code>.env</code> file on your machine, Docker/Sail defaults.</li>
<li><strong>Staging:</strong> environment vars set in hosting platform or deploy pipeline.</li>
<li><strong>Production:</strong> environment vars set in secure secret storage, never in repo.</li>
</ul>
<p>This makes environment promotion predictable and auditable.</p>
<h2>Common <code>.env</code> mistakes and fixes</h2>
<table><thead><tr><th>Mistake</th><th>Impact</th><th>Fix</th></tr></thead><tbody><tr><td>Calling <code>env()</code> directly in app code</td><td>Breaks when config is cached</td><td>Move to <code>config/*.php</code>, read via <code>config()</code></td></tr><tr><td>Committing <code>.env</code></td><td>Secret exposure</td><td>Remove file from git, rotate credentials</td></tr><tr><td>Missing key in <code>.env.example</code></td><td>Onboarding failures</td><td>Add all required keys with placeholders</td></tr><tr><td><code>APP_DEBUG=true</code> in production</td><td>Sensitive data leakage</td><td>Set false in production and restart workers</td></tr><tr><td>Wrong DB host in Docker</td><td>Connection refused errors</td><td>Use service name (e.g., <code>mysql</code>)</td></tr></tbody></table>
<h2>Quick sanity checklist before deploy</h2>
<ol>
<li><code>APP_ENV=production</code> and <code>APP_DEBUG=false</code>.</li>
<li>Production DB credentials are not shared with staging/local.</li>
<li><code>.env</code> is not tracked by git.</li>
<li>Config cache is rebuilt after env changes.</li>
<li>Queue workers are restarted after deployment.</li>
</ol>
<h2>FAQ</h2>
<h3>Should <code>.env</code> ever be committed?</h3>
<p>No. Commit <code>.env.example</code> only.</p>
<h3>Is it okay to call <code>env()</code> in controllers or services?</h3>
<p>Avoid it. Use <code>config()</code> in application code so behavior remains stable with cached config.</p>
<h3>Where should production secrets live?</h3>
<p>In your platform secret manager or runtime environment configuration, not in files inside the repository.</p>
<h3>Do I need different <code>.env</code> values per environment?</h3>
<p>Yes. Local, staging, and production should each use separate values and credentials.</p>
<h2>Final takeaway</h2>
<p>If you get <code>.env</code> management right early, you avoid painful deployment bugs and major security risks later. In Laravel 13, keep env values in config files, keep secrets out of git, and treat production settings as sensitive infrastructure data.</p>
<p>Next in this series: <a href="/articles/create-laravel-project-2026">Create a New Laravel 13 Project in 2026</a>, <a href="/articles/laravel-db-raw-guide-2026">DB::raw guide</a>, and the upcoming <a href="/articles/deploy-laravel-project-2026">Laravel deployment guide</a>.</p>]]></content:encoded>
            <author>kenmsh@gmail.com (Kennedy Mutisya)</author>
        </item>
        <item>
            <title><![CDATA[Putting Your Laravel Controllers on a Diet]]></title>
            <link>mutisyadev.xyz/articles/putting-your-laravel-controllers-on-a-diet</link>
            <guid>mutisyadev.xyz/articles/putting-your-laravel-controllers-on-a-diet</guid>
            <pubDate>Mon, 07 Jul 2025 00:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>A controller action that handles a registration form can balloon fast. You start with a simple save, add error handling, then validation, and before you know it you have twenty lines of glue code that the controller has no business owning.</p>
<p>Let us walk through how that happens and how to pull it back.</p>
<h2>The Slow Descent</h2>
<p>You start with the happy path:</p>
<pre class="language-php"><code class="language-php"><span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function-definition function">store</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">{</span>
    <span class="token variable">$user</span> <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">User</span><span class="token punctuation">(</span><span class="token scope">Input<span class="token punctuation">::</span></span><span class="token function">all</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token variable">$user</span><span class="token operator">-&gt;</span><span class="token function">save</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

    <span class="token keyword">return</span> <span class="token scope">View<span class="token punctuation">::</span></span><span class="token function">make</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'account-created'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre>
<p>Straightforward, but fragile. So you add error handling:</p>
<pre class="language-php"><code class="language-php"><span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function-definition function">store</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">{</span>
    <span class="token variable">$user</span> <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">User</span><span class="token punctuation">(</span><span class="token scope">Input<span class="token punctuation">::</span></span><span class="token function">all</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

    <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span> <span class="token variable">$user</span><span class="token operator">-&gt;</span><span class="token function">save</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token keyword">return</span> <span class="token scope">Redirect<span class="token punctuation">::</span></span><span class="token function">to</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'/users/create'</span><span class="token punctuation">)</span>
            <span class="token operator">-&gt;</span><span class="token function">with</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'message'</span><span class="token punctuation">,</span> <span class="token string single-quoted-string">'Something went wrong!'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token keyword">return</span> <span class="token scope">View<span class="token punctuation">::</span></span><span class="token function">make</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'account-created'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre>
<p>Then someone submits without a password. So validation gets tacked on:</p>
<pre class="language-php"><code class="language-php"><span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function-definition function">store</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">{</span>
    <span class="token variable">$input</span> <span class="token operator">=</span> <span class="token scope">Input<span class="token punctuation">::</span></span><span class="token function">all</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

    <span class="token variable">$rules</span> <span class="token operator">=</span> <span class="token punctuation">[</span>
        <span class="token string single-quoted-string">'email'</span>        <span class="token operator">=&gt;</span> <span class="token punctuation">[</span><span class="token string single-quoted-string">'required'</span><span class="token punctuation">,</span> <span class="token string single-quoted-string">'email'</span><span class="token punctuation">,</span> <span class="token string single-quoted-string">'unique:users'</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
        <span class="token string single-quoted-string">'password'</span>     <span class="token operator">=&gt;</span> <span class="token punctuation">[</span><span class="token string single-quoted-string">'required'</span><span class="token punctuation">,</span> <span class="token string single-quoted-string">'confirmed'</span><span class="token punctuation">,</span> <span class="token string single-quoted-string">'min:6'</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
        <span class="token string single-quoted-string">'first_name'</span>   <span class="token operator">=&gt;</span> <span class="token punctuation">[</span><span class="token string single-quoted-string">'required'</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
        <span class="token string single-quoted-string">'last_name'</span>    <span class="token operator">=&gt;</span> <span class="token punctuation">[</span><span class="token string single-quoted-string">'required'</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
        <span class="token string single-quoted-string">'date_of_birth'</span><span class="token operator">=&gt;</span> <span class="token punctuation">[</span><span class="token string single-quoted-string">'required'</span><span class="token punctuation">,</span> <span class="token string single-quoted-string">'date'</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
    <span class="token punctuation">]</span><span class="token punctuation">;</span>

    <span class="token variable">$validation</span> <span class="token operator">=</span> <span class="token scope">Validator<span class="token punctuation">::</span></span><span class="token function">make</span><span class="token punctuation">(</span><span class="token variable">$input</span><span class="token punctuation">,</span> <span class="token variable">$rules</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

    <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token variable">$validation</span><span class="token operator">-&gt;</span><span class="token function">fails</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token keyword">return</span> <span class="token scope">Redirect<span class="token punctuation">::</span></span><span class="token function">to</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'/users/create'</span><span class="token punctuation">)</span>
            <span class="token operator">-&gt;</span><span class="token function">withErrors</span><span class="token punctuation">(</span><span class="token variable">$validation</span><span class="token punctuation">)</span>
            <span class="token operator">-&gt;</span><span class="token function">withInput</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token variable">$user</span> <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">User</span><span class="token punctuation">(</span><span class="token variable">$input</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

    <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span> <span class="token variable">$user</span><span class="token operator">-&gt;</span><span class="token function">save</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token keyword">return</span> <span class="token scope">Redirect<span class="token punctuation">::</span></span><span class="token function">to</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'/users/create'</span><span class="token punctuation">)</span>
            <span class="token operator">-&gt;</span><span class="token function">with</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'message'</span><span class="token punctuation">,</span> <span class="token string single-quoted-string">'Something went wrong!'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token keyword">return</span> <span class="token scope">View<span class="token punctuation">::</span></span><span class="token function">make</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'account-created'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre>
<p>Now the controller is stuffed with logic that has nothing to do with handling HTTP. It is defining form rules, running validations, managing redirects for multiple failure modes, and persisting records. None of that belongs here.</p>
<h2>The Core Mistake</h2>
<p>The trap is thinking of "model" as only "a class that maps to a database table." That thinking forces every piece of business logic into either a controller (wrong) or an Eloquent model (often wrong too, since your model should not care about form validation rules or redirect URLs).</p>
<p>You are allowed to make classes that do not correspond to tables. In fact, that is where most of your application's real code should live.</p>
<h2>Extract a Form Class</h2>
<p>Look at that controller again. The validation section is the biggest chunk of noise:</p>
<pre class="language-php"><code class="language-php"><span class="token variable">$input</span> <span class="token operator">=</span> <span class="token scope">Input<span class="token punctuation">::</span></span><span class="token function">all</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token variable">$rules</span> <span class="token operator">=</span> <span class="token keyword">array</span> <span class="token punctuation">(</span><span class="token operator">...</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token variable">$validation</span> <span class="token operator">=</span> <span class="token scope">Validator<span class="token punctuation">::</span></span><span class="token function">make</span><span class="token punctuation">(</span><span class="token variable">$input</span><span class="token punctuation">,</span> <span class="token variable">$rules</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token variable">$validation</span><span class="token operator">-&gt;</span><span class="token function">fails</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">return</span> <span class="token scope">Redirect<span class="token punctuation">::</span></span><span class="token function">to</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'/users/create'</span><span class="token punctuation">)</span>
        <span class="token operator">-&gt;</span><span class="token function">withErrors</span><span class="token punctuation">(</span><span class="token variable">$validation</span><span class="token punctuation">)</span>
        <span class="token operator">-&gt;</span><span class="token function">withInput</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre>
<p>All of that is about the form: what input it expects and whether that input is valid. So why is there not a <code>UserRegistrationForm</code> class?</p>
<pre class="language-php"><code class="language-php"><span class="token keyword">class</span> <span class="token class-name-definition class-name">UserRegistrationForm</span>
<span class="token punctuation">{</span>
    <span class="token keyword">private</span> <span class="token variable">$rules</span> <span class="token operator">=</span> <span class="token punctuation">[</span>
        <span class="token string single-quoted-string">'email'</span>        <span class="token operator">=&gt;</span> <span class="token punctuation">[</span><span class="token string single-quoted-string">'required'</span><span class="token punctuation">,</span> <span class="token string single-quoted-string">'email'</span><span class="token punctuation">,</span> <span class="token string single-quoted-string">'unique:users'</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
        <span class="token string single-quoted-string">'password'</span>     <span class="token operator">=&gt;</span> <span class="token punctuation">[</span><span class="token string single-quoted-string">'required'</span><span class="token punctuation">,</span> <span class="token string single-quoted-string">'confirmed'</span><span class="token punctuation">,</span> <span class="token string single-quoted-string">'min:6'</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
        <span class="token string single-quoted-string">'first_name'</span>   <span class="token operator">=&gt;</span> <span class="token punctuation">[</span><span class="token string single-quoted-string">'required'</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
        <span class="token string single-quoted-string">'last_name'</span>    <span class="token operator">=&gt;</span> <span class="token punctuation">[</span><span class="token string single-quoted-string">'required'</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
        <span class="token string single-quoted-string">'date_of_birth'</span><span class="token operator">=&gt;</span> <span class="token punctuation">[</span><span class="token string single-quoted-string">'required'</span><span class="token punctuation">,</span> <span class="token string single-quoted-string">'date'</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
    <span class="token punctuation">]</span><span class="token punctuation">;</span>

    <span class="token keyword">private</span> <span class="token variable">$attributes</span><span class="token punctuation">;</span>
    <span class="token keyword">private</span> <span class="token variable">$validation</span><span class="token punctuation">;</span>

    <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function-definition function">__construct</span><span class="token punctuation">(</span><span class="token keyword type-hint">array</span> <span class="token variable">$attributes</span><span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token this keyword">$this</span><span class="token operator">-&gt;</span><span class="token property">attributes</span> <span class="token operator">=</span> <span class="token variable">$attributes</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function-definition function">isInvalid</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token keyword">return</span> <span class="token operator">!</span> <span class="token this keyword">$this</span><span class="token operator">-&gt;</span><span class="token function">isValid</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function-definition function">isValid</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token this keyword">$this</span><span class="token operator">-&gt;</span><span class="token property">validation</span> <span class="token operator">=</span> <span class="token scope">Validator<span class="token punctuation">::</span></span><span class="token function">make</span><span class="token punctuation">(</span>
            <span class="token this keyword">$this</span><span class="token operator">-&gt;</span><span class="token property">attributes</span><span class="token punctuation">,</span>
            <span class="token this keyword">$this</span><span class="token operator">-&gt;</span><span class="token property">rules</span>
        <span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token keyword">return</span> <span class="token this keyword">$this</span><span class="token operator">-&gt;</span><span class="token property">validation</span><span class="token operator">-&gt;</span><span class="token function">passes</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function-definition function">getValidation</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token keyword">return</span> <span class="token this keyword">$this</span><span class="token operator">-&gt;</span><span class="token property">validation</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre>
<p>Now the controller looks like this:</p>
<pre class="language-php"><code class="language-php"><span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function-definition function">store</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">{</span>
    <span class="token variable">$form</span> <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">UserRegistrationForm</span><span class="token punctuation">(</span><span class="token scope">Input<span class="token punctuation">::</span></span><span class="token function">all</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

    <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token variable">$form</span><span class="token operator">-&gt;</span><span class="token function">isInvalid</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token keyword">return</span> <span class="token scope">Redirect<span class="token punctuation">::</span></span><span class="token function">to</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'/users/create'</span><span class="token punctuation">)</span>
            <span class="token operator">-&gt;</span><span class="token function">withErrors</span><span class="token punctuation">(</span><span class="token variable">$form</span><span class="token operator">-&gt;</span><span class="token function">getValidation</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
            <span class="token operator">-&gt;</span><span class="token function">withInput</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token variable">$user</span> <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">User</span><span class="token punctuation">(</span><span class="token scope">Input<span class="token punctuation">::</span></span><span class="token function">all</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

    <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span> <span class="token variable">$user</span><span class="token operator">-&gt;</span><span class="token function">save</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token keyword">return</span> <span class="token scope">Redirect<span class="token punctuation">::</span></span><span class="token function">to</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'/users/create'</span><span class="token punctuation">)</span>
            <span class="token operator">-&gt;</span><span class="token function">with</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'message'</span><span class="token punctuation">,</span> <span class="token string single-quoted-string">'Something went wrong!'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token keyword">return</span> <span class="token scope">View<span class="token punctuation">::</span></span><span class="token function">make</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'account-created'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre>
<p>Already a big improvement. The validation rules live with the form, not the controller. The controller just asks "is this valid?" and acts on the answer.</p>
<h2>Going Further: Give the Form the Save Behaviour</h2>
<p>Since the form already knows what valid input looks like, it might as well know how to turn that input into a user. Pull the save logic in too:</p>
<pre class="language-php"><code class="language-php"><span class="token keyword">class</span> <span class="token class-name-definition class-name">UserRegistrationForm</span>
<span class="token punctuation">{</span>
    <span class="token comment">// ...</span>

    <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function-definition function">save</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token this keyword">$this</span><span class="token operator">-&gt;</span><span class="token function">isInvalid</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
            <span class="token keyword">return</span> <span class="token constant boolean">false</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span>

        <span class="token variable">$user</span> <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">User</span><span class="token punctuation">(</span><span class="token this keyword">$this</span><span class="token operator">-&gt;</span><span class="token property">attributes</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token keyword">return</span> <span class="token variable">$user</span><span class="token operator">-&gt;</span><span class="token function">save</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre>
<p>And now the controller becomes genuinely thin:</p>
<pre class="language-php"><code class="language-php"><span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function-definition function">store</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">{</span>
    <span class="token variable">$form</span> <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">UserRegistrationForm</span><span class="token punctuation">(</span><span class="token scope">Input<span class="token punctuation">::</span></span><span class="token function">all</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

    <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span> <span class="token variable">$form</span><span class="token operator">-&gt;</span><span class="token function">save</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token keyword">return</span> <span class="token scope">Redirect<span class="token punctuation">::</span></span><span class="token function">to</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'/users/create'</span><span class="token punctuation">)</span>
            <span class="token operator">-&gt;</span><span class="token function">withErrors</span><span class="token punctuation">(</span><span class="token variable">$form</span><span class="token operator">-&gt;</span><span class="token function">getValidation</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
            <span class="token operator">-&gt;</span><span class="token function">withInput</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token keyword">return</span> <span class="token scope">View<span class="token punctuation">::</span></span><span class="token function">make</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'account-created'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre>
<p>That is it. The controller handles HTTP input, decides what to do next, and returns a response. The form object handles validation and persistence. If the business rules change, you change the form object, not the controller.</p>
<h2>What We Actually Did</h2>
<p>This refactoring is just moving code to the right bucket. The form object is a plain PHP class with no framework inheritance. It does not extend <code>Eloquent</code>, it does not extend <code>Controller</code>. It represents the concept of "a user registration form" and knows what to do with the data it receives.</p>
<p>This is the foundation of patterns you will see everywhere in mature Laravel applications:</p>
<ul>
<li><strong>Form requests</strong>: Laravel's own evolution of this idea, shipping validated input straight to your controller.</li>
<li><strong>Command objects</strong>: classes that encapsulate a single operation (register a user, place an order, cancel a subscription).</li>
<li><strong>Action classes</strong>: the same concept under a different name, popularised by Laravel 11's <code>make:action</code>.</li>
</ul>
<p>All of these patterns solve the same core problem: controllers should orchestrate, not implement. Push the logic downstream to plain PHP classes that are testable, reusable, and have one job.</p>
<h2>Key Takeaways</h2>
<ul>
<li><strong>Do not equate "model" with "table."</strong> Your application is encouraged to have classes that do not map to database rows.</li>
<li><strong>Controllers should be thin.</strong> If your controller action has validation rules, error handling, user creation, and email sending, you have put everything in the wrong place.</li>
<li><strong>Form objects or command objects</strong> encapsulate a single operation and keep your controllers readable.</li>
<li><strong>When a controller action feels crowded, extract the domain logic</strong> into its own class first. The right name will make the code obvious.</li>
</ul>]]></content:encoded>
            <author>kenmsh@gmail.com (Kennedy Mutisya)</author>
        </item>
        <item>
            <title><![CDATA[Queue Job Batching in Laravel: How It Works]]></title>
            <link>mutisyadev.xyz/articles/queue-job-batching-in-laravel-how-it-works</link>
            <guid>mutisyadev.xyz/articles/queue-job-batching-in-laravel-how-it-works</guid>
            <pubDate>Thu, 06 Jan 2022 00:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>Laravel 8 introduced a neat feature: you can dispatch a group of jobs to the queue, run them in parallel, and then execute specific logic when any fail or when all complete. Here is a look under the hood.</p>
<h2>A Quick Example</h2>
<p>From the official docs:</p>
<pre class="language-php"><code class="language-php"><span class="token variable">$batch</span> <span class="token operator">=</span> <span class="token scope">Bus<span class="token punctuation">::</span></span><span class="token function">batch</span><span class="token punctuation">(</span><span class="token punctuation">[</span>
    <span class="token keyword">new</span> <span class="token class-name">ProcessPodcast</span><span class="token punctuation">(</span><span class="token scope">Podcast<span class="token punctuation">::</span></span><span class="token function">find</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
    <span class="token keyword">new</span> <span class="token class-name">ProcessPodcast</span><span class="token punctuation">(</span><span class="token scope">Podcast<span class="token punctuation">::</span></span><span class="token function">find</span><span class="token punctuation">(</span><span class="token number">2</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
    <span class="token keyword">new</span> <span class="token class-name">ProcessPodcast</span><span class="token punctuation">(</span><span class="token scope">Podcast<span class="token punctuation">::</span></span><span class="token function">find</span><span class="token punctuation">(</span><span class="token number">3</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
    <span class="token keyword">new</span> <span class="token class-name">ProcessPodcast</span><span class="token punctuation">(</span><span class="token scope">Podcast<span class="token punctuation">::</span></span><span class="token function">find</span><span class="token punctuation">(</span><span class="token number">4</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
    <span class="token keyword">new</span> <span class="token class-name">ProcessPodcast</span><span class="token punctuation">(</span><span class="token scope">Podcast<span class="token punctuation">::</span></span><span class="token function">find</span><span class="token punctuation">(</span><span class="token number">5</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
<span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token operator">-&gt;</span><span class="token function">then</span><span class="token punctuation">(</span><span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token class-name type-declaration">Batch</span> <span class="token variable">$batch</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token comment">// All jobs completed successfully...</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token operator">-&gt;</span><span class="token function">catch</span><span class="token punctuation">(</span><span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token class-name type-declaration">Batch</span> <span class="token variable">$batch</span><span class="token punctuation">,</span> <span class="token class-name type-declaration">Throwable</span> <span class="token variable">$e</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token comment">// First batch job failure detected...</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token operator">-&gt;</span><span class="token function">finally</span><span class="token punctuation">(</span><span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token class-name type-declaration">Batch</span> <span class="token variable">$batch</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token comment">// The batch has finished executing...</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token operator">-&gt;</span><span class="token function">name</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'Process Podcasts'</span><span class="token punctuation">)</span>
  <span class="token operator">-&gt;</span><span class="token function">allowFailures</span><span class="token punctuation">(</span><span class="token constant boolean">false</span><span class="token punctuation">)</span>
  <span class="token operator">-&gt;</span><span class="token function">onConnection</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'redis'</span><span class="token punctuation">)</span>
  <span class="token operator">-&gt;</span><span class="token function">onQueue</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'podcasts'</span><span class="token punctuation">)</span>
  <span class="token operator">-&gt;</span><span class="token function">dispatch</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre>
<h2>Storing the Batch</h2>
<p>When you call <code>dispatch()</code>, the batch information is written to the database first. The <code>store()</code> method in <code>Illuminate\Bus\DatabaseBatchRepository</code> inserts a row:</p>
<pre class="language-php"><code class="language-php"><span class="token this keyword">$this</span><span class="token operator">-&gt;</span><span class="token property">connection</span><span class="token operator">-&gt;</span><span class="token function">table</span><span class="token punctuation">(</span><span class="token this keyword">$this</span><span class="token operator">-&gt;</span><span class="token property">table</span><span class="token punctuation">)</span><span class="token operator">-&gt;</span><span class="token function">insert</span><span class="token punctuation">(</span><span class="token punctuation">[</span>
    <span class="token string single-quoted-string">'id'</span>            <span class="token operator">=&gt;</span> <span class="token variable">$id</span><span class="token punctuation">,</span>
    <span class="token string single-quoted-string">'name'</span>          <span class="token operator">=&gt;</span> <span class="token variable">$batch</span><span class="token operator">-&gt;</span><span class="token property">name</span><span class="token punctuation">,</span>
    <span class="token string single-quoted-string">'total_jobs'</span>    <span class="token operator">=&gt;</span> <span class="token number">0</span><span class="token punctuation">,</span>
    <span class="token string single-quoted-string">'pending_jobs'</span>  <span class="token operator">=&gt;</span> <span class="token number">0</span><span class="token punctuation">,</span>
    <span class="token string single-quoted-string">'failed_jobs'</span>   <span class="token operator">=&gt;</span> <span class="token number">0</span><span class="token punctuation">,</span>
    <span class="token string single-quoted-string">'failed_job_ids'</span><span class="token operator">=&gt;</span> <span class="token string single-quoted-string">'[]'</span><span class="token punctuation">,</span>
    <span class="token string single-quoted-string">'options'</span>       <span class="token operator">=&gt;</span> <span class="token function">serialize</span><span class="token punctuation">(</span><span class="token variable">$batch</span><span class="token operator">-&gt;</span><span class="token property">options</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
    <span class="token string single-quoted-string">'created_at'</span>    <span class="token operator">=&gt;</span> <span class="token function">time</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
    <span class="token string single-quoted-string">'cancelled_at'</span>  <span class="token operator">=&gt;</span> <span class="token constant">null</span><span class="token punctuation">,</span>
    <span class="token string single-quoted-string">'finished_at'</span>   <span class="token operator">=&gt;</span> <span class="token constant">null</span><span class="token punctuation">,</span>
<span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre>
<p>Each batch gets a UUID and an optional name. The <code>options</code> array holds everything else: the closures passed to <code>then()</code>, <code>catch()</code>, and <code>finally()</code>, plus <code>allowFailures</code>, <code>onConnection</code>, and <code>onQueue</code> settings. Closures are serialized using <code>Opis\Closure\SerializableClosure</code> so they survive the database round trip.</p>
<h2>Dispatching the Jobs</h2>
<p>Once the batch record exists, Laravel attaches the batch ID to every job and dispatches them all at once using the queue's <code>bulk()</code> method:</p>
<pre class="language-php"><code class="language-php"><span class="token variable">$jobs</span><span class="token operator">-&gt;</span><span class="token property">each</span><span class="token operator">-&gt;</span><span class="token function">withBatchId</span><span class="token punctuation">(</span><span class="token this keyword">$this</span><span class="token operator">-&gt;</span><span class="token property">id</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token this keyword">$this</span><span class="token operator">-&gt;</span><span class="token property">repository</span><span class="token operator">-&gt;</span><span class="token function">incrementTotalJobs</span><span class="token punctuation">(</span><span class="token this keyword">$this</span><span class="token operator">-&gt;</span><span class="token property">id</span><span class="token punctuation">,</span> <span class="token function">count</span><span class="token punctuation">(</span><span class="token variable">$jobs</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token this keyword">$this</span><span class="token operator">-&gt;</span><span class="token property">queue</span>
    <span class="token operator">-&gt;</span><span class="token function">connection</span><span class="token punctuation">(</span><span class="token this keyword">$this</span><span class="token operator">-&gt;</span><span class="token property">options</span><span class="token punctuation">[</span><span class="token string single-quoted-string">'connection'</span><span class="token punctuation">]</span> <span class="token operator">??</span> <span class="token constant">null</span><span class="token punctuation">)</span>
    <span class="token operator">-&gt;</span><span class="token function">bulk</span><span class="token punctuation">(</span><span class="token variable">$jobs</span><span class="token operator">-&gt;</span><span class="token function">all</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token variable">$data</span> <span class="token operator">=</span> <span class="token string single-quoted-string">''</span><span class="token punctuation">,</span> <span class="token this keyword">$this</span><span class="token operator">-&gt;</span><span class="token property">options</span><span class="token punctuation">[</span><span class="token string single-quoted-string">'queue'</span><span class="token punctuation">]</span> <span class="token operator">??</span> <span class="token constant">null</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre>
<p>The total job count is incremented in the database so Laravel can later determine when the batch is done. Using <code>bulk()</code> means all jobs are sent to the queue store in a single transaction, rather than dispatching them one by one.</p>
<h2>Monitoring the Batch</h2>
<h3>On Success</h3>
<p>After each successful job execution, <code>recordSuccessfulJob()</code> is called. It decrements the pending count and checks whether callbacks should fire:</p>
<pre class="language-php"><code class="language-php"><span class="token variable">$counts</span> <span class="token operator">=</span> <span class="token this keyword">$this</span><span class="token operator">-&gt;</span><span class="token function">decrementPendingJobs</span><span class="token punctuation">(</span><span class="token variable">$jobId</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token variable">$counts</span><span class="token operator">-&gt;</span><span class="token property">pendingJobs</span> <span class="token operator">===</span> <span class="token number">0</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token this keyword">$this</span><span class="token operator">-&gt;</span><span class="token property">repository</span><span class="token operator">-&gt;</span><span class="token function">markAsFinished</span><span class="token punctuation">(</span><span class="token this keyword">$this</span><span class="token operator">-&gt;</span><span class="token property">id</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token variable">$counts</span><span class="token operator">-&gt;</span><span class="token property">pendingJobs</span> <span class="token operator">===</span> <span class="token number">0</span> <span class="token operator">&amp;&amp;</span> <span class="token this keyword">$this</span><span class="token operator">-&gt;</span><span class="token function">hasThenCallbacks</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token function">collect</span><span class="token punctuation">(</span><span class="token this keyword">$this</span><span class="token operator">-&gt;</span><span class="token property">options</span><span class="token punctuation">[</span><span class="token string single-quoted-string">'then'</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token operator">-&gt;</span><span class="token function">each</span><span class="token punctuation">(</span><span class="token comment">/* Invoke */</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token variable">$counts</span><span class="token operator">-&gt;</span><span class="token function">allJobsHaveRanExactlyOnce</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">&amp;&amp;</span> <span class="token this keyword">$this</span><span class="token operator">-&gt;</span><span class="token function">hasFinallyCallbacks</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token function">collect</span><span class="token punctuation">(</span><span class="token this keyword">$this</span><span class="token operator">-&gt;</span><span class="token property">options</span><span class="token punctuation">[</span><span class="token string single-quoted-string">'finally'</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token operator">-&gt;</span><span class="token function">each</span><span class="token punctuation">(</span><span class="token comment">/* Invoke */</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre>
<p>When there are no more pending jobs, Laravel marks the batch as finished by writing to the <code>finished_at</code> timestamp. Any <code>then</code> callbacks are triggered if everything ran successfully, and any <code>finally</code> callbacks fire once every job has been attempted at least once.</p>
<h3>On Failure</h3>
<p>When a job exhausts all its attempts, <code>recordFailedJob()</code> is called:</p>
<pre class="language-php"><code class="language-php"><span class="token variable">$counts</span> <span class="token operator">=</span> <span class="token this keyword">$this</span><span class="token operator">-&gt;</span><span class="token function">incrementFailedJobs</span><span class="token punctuation">(</span><span class="token variable">$jobId</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token variable">$counts</span><span class="token operator">-&gt;</span><span class="token property">failedJobs</span> <span class="token operator">===</span> <span class="token number">1</span> <span class="token operator">&amp;&amp;</span> <span class="token operator">!</span> <span class="token this keyword">$this</span><span class="token operator">-&gt;</span><span class="token function">allowsFailures</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token this keyword">$this</span><span class="token operator">-&gt;</span><span class="token function">cancel</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token variable">$counts</span><span class="token operator">-&gt;</span><span class="token property">failedJobs</span> <span class="token operator">===</span> <span class="token number">1</span> <span class="token operator">&amp;&amp;</span> <span class="token this keyword">$this</span><span class="token operator">-&gt;</span><span class="token function">hasCatchCallbacks</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token variable">$batch</span> <span class="token operator">=</span> <span class="token this keyword">$this</span><span class="token operator">-&gt;</span><span class="token function">fresh</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token function">collect</span><span class="token punctuation">(</span><span class="token this keyword">$this</span><span class="token operator">-&gt;</span><span class="token property">options</span><span class="token punctuation">[</span><span class="token string single-quoted-string">'catch'</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token operator">-&gt;</span><span class="token function">each</span><span class="token punctuation">(</span><span class="token comment">/* Invoke */</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token variable">$counts</span><span class="token operator">-&gt;</span><span class="token function">allJobsHaveRanExactlyOnce</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">&amp;&amp;</span> <span class="token this keyword">$this</span><span class="token operator">-&gt;</span><span class="token function">hasFinallyCallbacks</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token variable">$batch</span> <span class="token operator">=</span> <span class="token this keyword">$this</span><span class="token operator">-&gt;</span><span class="token function">fresh</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token function">collect</span><span class="token punctuation">(</span><span class="token this keyword">$this</span><span class="token operator">-&gt;</span><span class="token property">options</span><span class="token punctuation">[</span><span class="token string single-quoted-string">'finally'</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token operator">-&gt;</span><span class="token function">each</span><span class="token punctuation">(</span><span class="token comment">/* Invoke */</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre>
<p>If the batch does not allow failures, the first failure cancels the entire batch, and the <code>cancelled_at</code> field gets set to the current timestamp. The <code>catch</code> callback fires only on the first failure, and <code>finally</code> fires once all jobs have had at least one attempt.</p>
<p>Both <code>recordSuccessfulJob()</code> and <code>recordFailedJob()</code> are called from within <code>Illuminate\Queue\CallQueuedHandler</code>.</p>
<h2>Exploring Further</h2>
<p>If you want to dig deeper into the batch system, the key classes are:</p>
<ul>
<li><code>Illuminate\Bus\Dispatcher::batch()</code>: entry point for creating batches.</li>
<li><code>Illuminate\Bus\PendingBatch</code>: the fluent builder you chain methods on.</li>
<li><code>Illuminate\Bus\DatabaseBatchRepository</code>: handles database storage and tracking.</li>
<li><code>Illuminate\Bus\Batch</code>: the in-memory representation of a running batch.</li>
</ul>]]></content:encoded>
            <author>kenmsh@gmail.com (Kennedy Mutisya)</author>
        </item>
        <item>
            <title><![CDATA[Queue Workers in Laravel: How They Work]]></title>
            <link>mutisyadev.xyz/articles/queue-workers-in-laravel-how-they-work</link>
            <guid>mutisyadev.xyz/articles/queue-workers-in-laravel-how-they-work</guid>
            <pubDate>Tue, 05 Apr 2022 00:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>A queue worker is a PHP process that runs in the background, picks jobs from storage, and executes them according to your configuration. Let us trace how it all happens from the moment you type <code>php artisan queue:work</code>.</p>
<h2>queue:work vs queue:listen</h2>
<p>There are two commands for running workers, and the difference matters.</p>
<ul>
<li><code>php artisan queue:work</code> boots your Laravel application once and keeps the same instance alive indefinitely to process jobs. This is efficient, since you are not rebuilding the framework on every job, but it also means you need to restart the worker manually after any code change for the change to take effect.</li>
<li><code>php artisan queue:work --once</code> boots the app, processes a single job, and dies.</li>
<li><code>php artisan queue:listen</code> runs <code>queue:work --once</code> inside an infinite loop. Every job gets a fresh application instance. Code changes are picked up automatically because each iteration is a new process, but you pay the price of booting the framework on every single job.</li>
</ul>
<h2>Inside queue:work</h2>
<p>The <code>handle()</code> method of <code>Queue\Console\WorkCommand</code> is where it starts.</p>
<h3>Maintenance Mode</h3>
<p>If the app is in maintenance mode and <code>--once</code> is set, the worker just sleeps for the configured duration and dies gracefully:</p>
<pre class="language-php"><code class="language-php"><span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token this keyword">$this</span><span class="token operator">-&gt;</span><span class="token function">downForMaintenance</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">&amp;&amp;</span> <span class="token this keyword">$this</span><span class="token operator">-&gt;</span><span class="token function">option</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'once'</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">return</span> <span class="token this keyword">$this</span><span class="token operator">-&gt;</span><span class="token property">worker</span><span class="token operator">-&gt;</span><span class="token function">sleep</span><span class="token punctuation">(</span><span class="token this keyword">$this</span><span class="token operator">-&gt;</span><span class="token function">option</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'sleep'</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre>
<p>Without that sleep, a supervising process (like Supervisor) would restart the worker instantly on every loop iteration, creating a tight restart cycle that burns CPU for nothing.</p>
<h3>Event Listeners</h3>
<p>The command registers listeners for three events right before the main loop starts:</p>
<pre class="language-php"><code class="language-php"><span class="token this keyword">$this</span><span class="token operator">-&gt;</span><span class="token property">laravel</span><span class="token punctuation">[</span><span class="token string single-quoted-string">'events'</span><span class="token punctuation">]</span><span class="token operator">-&gt;</span><span class="token function">listen</span><span class="token punctuation">(</span><span class="token scope">JobProcessing<span class="token punctuation">::</span></span><span class="token keyword">class</span><span class="token punctuation">,</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token variable">$event</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token this keyword">$this</span><span class="token operator">-&gt;</span><span class="token function">writeOutput</span><span class="token punctuation">(</span><span class="token variable">$event</span><span class="token operator">-&gt;</span><span class="token property">job</span><span class="token punctuation">,</span> <span class="token string single-quoted-string">'starting'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token this keyword">$this</span><span class="token operator">-&gt;</span><span class="token property">laravel</span><span class="token punctuation">[</span><span class="token string single-quoted-string">'events'</span><span class="token punctuation">]</span><span class="token operator">-&gt;</span><span class="token function">listen</span><span class="token punctuation">(</span><span class="token scope">JobProcessed<span class="token punctuation">::</span></span><span class="token keyword">class</span><span class="token punctuation">,</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token variable">$event</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token this keyword">$this</span><span class="token operator">-&gt;</span><span class="token function">writeOutput</span><span class="token punctuation">(</span><span class="token variable">$event</span><span class="token operator">-&gt;</span><span class="token property">job</span><span class="token punctuation">,</span> <span class="token string single-quoted-string">'success'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token this keyword">$this</span><span class="token operator">-&gt;</span><span class="token property">laravel</span><span class="token punctuation">[</span><span class="token string single-quoted-string">'events'</span><span class="token punctuation">]</span><span class="token operator">-&gt;</span><span class="token function">listen</span><span class="token punctuation">(</span><span class="token scope">JobFailed<span class="token punctuation">::</span></span><span class="token keyword">class</span><span class="token punctuation">,</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token variable">$event</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token this keyword">$this</span><span class="token operator">-&gt;</span><span class="token function">writeOutput</span><span class="token punctuation">(</span><span class="token variable">$event</span><span class="token operator">-&gt;</span><span class="token property">job</span><span class="token punctuation">,</span> <span class="token string single-quoted-string">'failed'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token this keyword">$this</span><span class="token operator">-&gt;</span><span class="token function">logFailedJob</span><span class="token punctuation">(</span><span class="token variable">$event</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre>
<p>Failed jobs are logged via the <code>queue.failer</code> service, which by default stores the failure info (connection name, queue name, raw payload, and exception) in a <code>failed_jobs</code> database table.</p>
<h3>Running the Worker</h3>
<p>The <code>runWorker()</code> method collects the connection and queue options, sets a cache driver, then decides between two code paths:</p>
<ul>
<li><code>runNextJob()</code>: used in <code>--once</code> mode, runs one job and exits.</li>
<li><code>daemon()</code>: used in persistent mode, keeps running.</li>
</ul>
<h2>The Daemon Loop</h2>
<p>The <code>Worker::daemon()</code> method is where persistent workers live.</p>
<h3>Signal Handling</h3>
<p>If PHP 7.1+ with the <code>pcntl</code> extension is available, the worker registers async signal handlers:</p>
<pre class="language-php"><code class="language-php"><span class="token function">pcntl_signal</span><span class="token punctuation">(</span><span class="token constant">SIGTERM</span><span class="token punctuation">,</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token this keyword">$this</span><span class="token operator">-&gt;</span><span class="token property">shouldQuit</span> <span class="token operator">=</span> <span class="token constant boolean">true</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token function">pcntl_signal</span><span class="token punctuation">(</span><span class="token constant">SIGUSR2</span><span class="token punctuation">,</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token this keyword">$this</span><span class="token operator">-&gt;</span><span class="token property">paused</span> <span class="token operator">=</span> <span class="token constant boolean">true</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token function">pcntl_signal</span><span class="token punctuation">(</span><span class="token constant">SIGCONT</span><span class="token punctuation">,</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token this keyword">$this</span><span class="token operator">-&gt;</span><span class="token property">paused</span> <span class="token operator">=</span> <span class="token constant boolean">false</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre>
<ul>
<li><strong>SIGTERM</strong> tells the worker to shut down (sent by Supervisor when you restart).</li>
<li><strong>SIGUSR2</strong> pauses the worker.</li>
<li><strong>SIGCONT</strong> resumes a paused worker.</li>
</ul>
<h3>The Main Loop</h3>
<p>The loop does this every iteration:</p>
<ol>
<li>Check if the worker should run (not in maintenance mode, not paused, no listeners returning false).</li>
<li>If it should not run, sleep and check again.</li>
<li>Pull the next available job from the queue storage.</li>
<li>Register a timeout alarm so the process gets killed if the job takes too long.</li>
<li>Run the job.</li>
<li>Check if the worker needs to stop (SIGTERM received, memory limit hit, restart requested).</li>
</ol>
<pre class="language-php"><code class="language-php"><span class="token keyword">while</span> <span class="token punctuation">(</span><span class="token constant boolean">true</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span> <span class="token this keyword">$this</span><span class="token operator">-&gt;</span><span class="token function">daemonShouldRun</span><span class="token punctuation">(</span><span class="token variable">$options</span><span class="token punctuation">,</span> <span class="token variable">$connectionName</span><span class="token punctuation">,</span> <span class="token variable">$queue</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token this keyword">$this</span><span class="token operator">-&gt;</span><span class="token function">pauseWorker</span><span class="token punctuation">(</span><span class="token variable">$options</span><span class="token punctuation">,</span> <span class="token variable">$lastRestart</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">continue</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token variable">$job</span> <span class="token operator">=</span> <span class="token this keyword">$this</span><span class="token operator">-&gt;</span><span class="token function">getNextJob</span><span class="token punctuation">(</span>
        <span class="token this keyword">$this</span><span class="token operator">-&gt;</span><span class="token property">manager</span><span class="token operator">-&gt;</span><span class="token function">connection</span><span class="token punctuation">(</span><span class="token variable">$connectionName</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token variable">$queue</span>
    <span class="token punctuation">)</span><span class="token punctuation">;</span>

    <span class="token this keyword">$this</span><span class="token operator">-&gt;</span><span class="token function">registerTimeoutHandler</span><span class="token punctuation">(</span><span class="token variable">$job</span><span class="token punctuation">,</span> <span class="token variable">$options</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

    <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token variable">$job</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token this keyword">$this</span><span class="token operator">-&gt;</span><span class="token function">runJob</span><span class="token punctuation">(</span><span class="token variable">$job</span><span class="token punctuation">,</span> <span class="token variable">$connectionName</span><span class="token punctuation">,</span> <span class="token variable">$options</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span>
        <span class="token this keyword">$this</span><span class="token operator">-&gt;</span><span class="token function">sleep</span><span class="token punctuation">(</span><span class="token variable">$options</span><span class="token operator">-&gt;</span><span class="token property">sleep</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token this keyword">$this</span><span class="token operator">-&gt;</span><span class="token function">stopIfNecessary</span><span class="token punctuation">(</span><span class="token variable">$options</span><span class="token punctuation">,</span> <span class="token variable">$lastRestart</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre>
<h3>Checking if the Worker Should Run</h3>
<p><code>daemonShouldRun()</code> checks:</p>
<ul>
<li>Application is not in maintenance mode (unless <code>--force</code> was passed).</li>
<li>Worker is not paused.</li>
<li>No event listener returned <code>false</code> from the <code>Looping</code> event.</li>
</ul>
<p>That last check is useful: you can register a <code>Looping</code> listener that returns <code>false</code> to temporarily pause processing under certain conditions, like during a deployment window.</p>
<h3>Fetching the Next Job</h3>
<p><code>getNextJob()</code> loops through the queues (you can specify multiple comma-separated queues) and asks the queue connection to pop a job:</p>
<pre class="language-php"><code class="language-php"><span class="token keyword">foreach</span> <span class="token punctuation">(</span><span class="token function">explode</span><span class="token punctuation">(</span><span class="token string single-quoted-string">','</span><span class="token punctuation">,</span> <span class="token variable">$queue</span><span class="token punctuation">)</span> <span class="token keyword">as</span> <span class="token variable">$queue</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span> <span class="token function">is_null</span><span class="token punctuation">(</span><span class="token variable">$job</span> <span class="token operator">=</span> <span class="token variable">$connection</span><span class="token operator">-&gt;</span><span class="token function">pop</span><span class="token punctuation">(</span><span class="token variable">$queue</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token keyword">return</span> <span class="token variable">$job</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre>
<p>The actual query looks for the oldest job that belongs to the target queue, is not already reserved, is available to run (not delayed), or has been reserved so long it may have frozen (retry timeout). Once found, the job is marked as reserved and its attempt count is incremented.</p>
<h3>Monitoring Timeouts</h3>
<p>If async signals are supported, the worker uses <code>pcntl_alarm()</code> to send a <code>SIGALRM</code> after the timeout period. If the job finishes in time, the next loop iteration resets the alarm. If the job hangs, the alarm fires and kills the process, and Supervisor starts a fresh one.</p>
<pre class="language-php"><code class="language-php"><span class="token variable">$timeout</span> <span class="token operator">=</span> <span class="token this keyword">$this</span><span class="token operator">-&gt;</span><span class="token function">timeoutForJob</span><span class="token punctuation">(</span><span class="token variable">$job</span><span class="token punctuation">,</span> <span class="token variable">$options</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token function">pcntl_alarm</span><span class="token punctuation">(</span><span class="token variable">$timeout</span> <span class="token operator">&gt;</span> <span class="token number">0</span> <span class="token operator">?</span> <span class="token variable">$timeout</span> <span class="token punctuation">:</span> <span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre>
<p>Only one alarm can be active per process, so the next job's alarm replaces the previous one.</p>
<h3>Processing a Job</h3>
<p><code>process()</code> fires the <code>JobProcessing</code> event, checks whether the job has already exceeded max attempts (and marks it failed if so), then calls <code>$job-&gt;fire()</code>:</p>
<pre class="language-php"><code class="language-php"><span class="token this keyword">$this</span><span class="token operator">-&gt;</span><span class="token function">raiseBeforeJobEvent</span><span class="token punctuation">(</span><span class="token variable">$connectionName</span><span class="token punctuation">,</span> <span class="token variable">$job</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token this keyword">$this</span><span class="token operator">-&gt;</span><span class="token function">markJobAsFailedIfAlreadyExceedsMaxAttempts</span><span class="token punctuation">(</span><span class="token variable">$connectionName</span><span class="token punctuation">,</span> <span class="token variable">$job</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token keyword type-casting">int</span><span class="token punctuation">)</span> <span class="token variable">$options</span><span class="token operator">-&gt;</span><span class="token property">maxTries</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token variable">$job</span><span class="token operator">-&gt;</span><span class="token function">fire</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token this keyword">$this</span><span class="token operator">-&gt;</span><span class="token function">raiseAfterJobEvent</span><span class="token punctuation">(</span><span class="token variable">$connectionName</span><span class="token punctuation">,</span> <span class="token variable">$job</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre>
<p>The <code>$job</code> object returned by <code>getNextJob()</code> is an implementation of <code>Contracts\Queue\Job</code>, for example, <code>Queue\Jobs\DatabaseJob</code> when using the database queue driver.</p>
<h3>End-of-Loop Checks</h3>
<p>After every job, <code>stopIfNecessary()</code> checks three things:</p>
<ol>
<li><strong><code>$this-&gt;shouldQuit</code></strong>: set by a SIGTERM signal or a lost database connection.</li>
<li><strong>Memory exceeded</strong>: if the worker's memory usage passes the <code>--memory</code> limit, it stops. Memory leaks in long-running processes are real; this is the safety valve.</li>
<li><strong>Queue restart requested</strong>: compares the stored restart timestamp against the current value. If they differ, someone ran <code>php artisan queue:restart</code>. The worker stops so it will be restarted with fresh code.</li>
</ol>
<h2>Key Takeaways</h2>
<ul>
<li><strong><code>queue:work</code></strong> boots the app once and stays alive: efficient but needs manual restarting after code changes.</li>
<li><strong><code>queue:listen</code></strong> boots the app on every job: picks up code changes automatically but costs more resources.</li>
<li><strong>Signal handling</strong> (SIGTERM, SIGUSR2, SIGCONT) lets Supervisor orchestrate the worker lifecycle.</li>
<li><strong>Timeouts</strong> use <code>pcntl_alarm</code> to kill hung jobs, on PHP 7.1+ only.</li>
<li><strong>Memory limits</strong> and <strong>restart signals</strong> keep long-running workers from degrading over time.</li>
<li><strong>The <code>Looping</code> event</strong> gives you a hook to pause processing during specific conditions.</li>
</ul>]]></content:encoded>
            <author>kenmsh@gmail.com (Kennedy Mutisya)</author>
        </item>
        <item>
            <title><![CDATA[The Model Was 99% Confident and Completely Wrong]]></title>
            <link>mutisyadev.xyz/articles/the-model-was-99-percent-confident-and-completely-wrong</link>
            <guid>mutisyadev.xyz/articles/the-model-was-99-percent-confident-and-completely-wrong</guid>
            <pubDate>Sat, 05 Sep 2026 00:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>The model had been trained, exported to ONNX, and was returning predictions in about seven milliseconds.</p>
<p>Then I gave it this:</p>
<blockquote>
<p>"nataka kuuza magunia 20 ya viazi gikomba"</p>
</blockquote>
<p>"I want to sell 20 bags of potatoes at Gikomba."</p>
<p>It predicted:</p>
<p><strong><code>pledge</code></strong></p>
<p>Confidence:</p>
<p><strong>99%.</strong></p>
<p>The correct intent was:</p>
<p><strong><code>sale_listing</code></strong></p>
<p>That was the moment the project became interesting.</p>
<p>I am relatively new to machine learning. I built this classifier with AI coding agents doing most of the implementation. They wrote much of the training pipeline, synthetic data generators, serving stack, cloud training configuration, and parts of the analysis tooling.</p>
<p>Code arrived much faster than understanding.</p>
<p>That changed what I spent my time doing.</p>
<p>Instead of spending most of my time asking:</p>
<blockquote>
<p>How do I implement this?</p>
</blockquote>
<p>I increasingly found myself asking:</p>
<blockquote>
<p>What would convince me that this is actually working?</p>
</blockquote>
<p>That question uncovered a broken quantized model, a dataset shortcut, a leaky evaluation split, a taxonomy mismatch between the model and the application, and thousands of supposedly "real" messages that turned out to be load-test traffic.</p>
<p>The classifier is the thing I built.</p>
<p><strong>Verification is the story.</strong></p>
<hr>
<h2>I didn't need a better model</h2>
<p>The system I was working on has a WhatsApp assistant that talks to food traders.</p>
<p>When a trader sends a free-form message, the application currently sends that message to Gemini 2.5 Flash. Gemini determines what the trader is trying to do and extracts the relevant information.</p>
<p>It works.</p>
<p>The problem was architectural rather than functional.</p>
<p>Every message that needed intent routing depended on a relatively large general-purpose model. That introduces latency and another dependency into a path where the answer is usually one of a finite set of intents.</p>
<p>So I started exploring a smaller encoder model that could run locally on CPU.</p>
<p>The idea was simple:</p>
<p><strong>Use a small classifier for the common case. Fall back to Gemini when it isn't confident enough.</strong></p>
<p>This wasn't primarily a cost optimization. The attraction was latency and resilience.</p>
<p>If the classifier could answer in single-digit milliseconds, we could potentially avoid paying the latency of a general-purpose model for every message while retaining Gemini as the fallback.</p>
<p>That sounds straightforward.</p>
<p>It wasn't.</p>
<hr>
<h2>First, I tried not training anything</h2>
<p>Before generating a dataset, I tried zero-shot classification.</p>
<p>The idea was to give an NLI model the trader's message and a list of possible intent descriptions, then ask it to choose the most likely one.</p>
<p>It was useful as a baseline.</p>
<p>It was also clearly not good enough.</p>
<p>Some predictions were reasonable. Others were not. On a small benchmark, several messages landed on the wrong intent.</p>
<p>Then I tried something more interesting.</p>
<p>I tested a quantized model from a model hub.</p>
<p>It returned valid JSON.</p>
<p>It looked like a working classifier.</p>
<p>It wasn't.</p>
<p>Its predictions were effectively neutral: the same class kept winning with almost no meaningful separation between the logits.</p>
<p>This was a useful lesson because nothing about the output looked obviously broken.</p>
<p>The API worked.</p>
<p>The JSON was valid.</p>
<p>The model loaded.</p>
<p>The prediction looked plausible.</p>
<p>The model was wrong.</p>
<p>So I started putting acceptance tests around the model rather than trusting the existence of an output.</p>
<p>For model exports, I wanted to check things like:</p>
<ul>
<li>Does the exported model produce the same predictions?</li>
<li>How different are the logits?</li>
<li>Does accuracy remain intact?</li>
<li>What happens to latency?</li>
<li>Does quantization actually preserve the behavior I care about?</li>
</ul>
<p>My own int8 export improved substantially through several attempts:</p>
<p><strong>72% → 79% → 82% → 94.4% → 95.6% agreement</strong></p>
<p>But my acceptance bar was 98%.</p>
<p>So I didn't ship it.</p>
<p>The fp32 ONNX model matched the PyTorch reference and ran at roughly <strong>7 ms</strong> on CPU.</p>
<p>That was good enough.</p>
<p>The lesson wasn't "ONNX is great."</p>
<p>It was:</p>
<blockquote>
<p><strong>An optimization isn't an optimization until you measure what it preserved.</strong></p>
</blockquote>
<hr>
<h2>Then I had to create a dataset</h2>
<p>The next problem was data.</p>
<p>I didn't have a clean labelled dataset of trader conversations ready to train on.</p>
<p>So I generated one.</p>
<p>Using Gemini.</p>
<p>Which meant I was asking the same general family of model I was trying to reduce dependency on to invent examples for the smaller model.</p>
<p>There is something slightly uncomfortable about that.</p>
<p>The first dataset had <strong>1,386 examples</strong> across 33 intents and seven language/register categories.</p>
<p>The first model was mediocre.</p>
<p>Then I changed the dataset.</p>
<p>Instead of asking the generator to produce arbitrary examples for each class, I started giving it things that should make classification harder:</p>
<ul>
<li>similar intents,</li>
<li>the same products,</li>
<li>the same quantities,</li>
<li>realistic vocabulary,</li>
<li>previous bot messages,</li>
<li>deliberately damaged WhatsApp-style text,</li>
<li>and examples from languages and registers used by the application.</li>
</ul>
<p>The second dataset grew to <strong>9,751 examples</strong>.</p>
<p>The held-out score improved from roughly <strong>0.70 to 0.86</strong>, and then to about <strong>0.916</strong> in the later 33-label experiment.</p>
<p>That looked like progress.</p>
<p>But there was a problem hiding inside the improvement.</p>
<hr>
<h2>Synthetic data doesn't just create examples</h2>
<p>Consider the potato message.</p>
<p>The generator understands that potatoes can appear in many contexts.</p>
<p>A trader might be buying them.</p>
<p>Selling them.</p>
<p>Pledging them.</p>
<p>Asking about their price.</p>
<p>Asking about availability.</p>
<p>Asking about a market.</p>
<p>The model needs to learn the difference.</p>
<p>So I deliberately introduced <strong>contrastive examples</strong>.</p>
<p>If the target intent was <code>sale_listing</code>, I asked the generator to produce examples for that intent alongside examples for several confusable intents.</p>
<p>Same product.</p>
<p>Similar quantity.</p>
<p>Similar language.</p>
<p>Different meaning.</p>
<p>That helped.</p>
<p>But it also exposed something deeper.</p>
<p>When you generate your training data, you aren't simply producing more observations.</p>
<p>You're defining the world the model gets to see.</p>
<p>The model isn't learning "how traders talk."</p>
<p>It's learning patterns in the examples I gave it.</p>
<p>And those examples came from another model.</p>
<p>So synthetic data does not only give you more examples.</p>
<p><strong>It gives you more examples of the generator's worldview.</strong></p>
<hr>
<h2>The 99% prediction</h2>
<p>That brings me back to the potatoes.</p>
<p>After improving the dataset, I tested a message equivalent to:</p>
<blockquote>
<p>"I want to sell 20 bags of potatoes at Gikomba."</p>
</blockquote>
<p>The classifier predicted:</p>
<p><code>pledge</code></p>
<p>with probability:</p>
<p><strong>0.99</strong></p>
<p>This was particularly interesting because the previous version had actually classified the message differently.</p>
<p>After the dataset changes, the model became more confident.</p>
<p>It also became more wrong.</p>
<p>The correct <code>sale_listing</code> prediction had been around 0.50 in the comparison I was running.</p>
<p>This is the kind of result that makes a model dangerous to trust casually.</p>
<p>A prediction of 51% gives you a reason to hesitate.</p>
<p>A prediction of 99% feels authoritative.</p>
<p>But the number doesn't mean what you instinctively want it to mean.</p>
<p>A softmax probability isn't a guarantee that the model is correct.</p>
<p>And in this case, the model had found a shortcut.</p>
<p>It wasn't confused.</p>
<p><strong>The dataset had taught it the wrong shortcut.</strong></p>
<p>That distinction matters.</p>
<p>If I had only looked at aggregate accuracy, I might have concluded that the new dataset was better.</p>
<p>The model had improved on the benchmark.</p>
<p>The benchmark was telling me something real.</p>
<p>It just wasn't telling me everything I needed to know.</p>
<hr>
<h2>Then I found another shortcut</h2>
<p>The classifier doesn't always have enough information in the trader's message alone.</p>
<p>Consider:</p>
<blockquote>
<p>"yes"</p>
</blockquote>
<p>Or:</p>
<blockquote>
<p>"20 bags"</p>
</blockquote>
<p>Those messages are nearly impossible to classify without knowing what the bot asked immediately before them.</p>
<p>So I changed the input from:</p>
<p><strong>trader message</strong></p>
<p>to:</p>
<p><strong>previous bot message + trader message</strong></p>
<p>The model now receives a sentence pair.</p>
<p>For example:</p>
<blockquote>
<p>Bot: "Do you want to sell potatoes?"</p>
<p>Trader: "Yes."</p>
</blockquote>
<p>The word "yes" isn't the intent.</p>
<p>The conversation is.</p>
<p>This made the architecture more useful.</p>
<p>It also created a new data problem.</p>
<p>One of the intents, <code>follow_up</code>, was disproportionately associated with a particular kind of previous message.</p>
<p>About <strong>94% of its examples</strong> had question-shaped previous turns.</p>
<p>Other intents had that pattern much less consistently.</p>
<p>The model could therefore learn an extremely cheap rule:</p>
<blockquote>
<p>If the previous message looks like a question, this is probably <code>follow_up</code>.</p>
</blockquote>
<p>It didn't need to understand the conversation.</p>
<p>It just needed to recognize the shape of the previous turn.</p>
<p>So I changed the generation rules in the next dataset version.</p>
<p>The fix wasn't "train harder."</p>
<p>It was:</p>
<p><strong>change what the model is allowed to learn from.</strong></p>
<p>That is a recurring theme in this project.</p>
<hr>
<h2>Then I discovered the classifier wasn't connected to most of the application</h2>
<p>This was probably my favorite failure.</p>
<p>At some point I stopped looking at the model and started reading the application code.</p>
<p>The original intent enum had grown from 33 labels to 35.</p>
<p>I audited the actual routing code.</p>
<p>There were roughly <strong>45 destinations or branches</strong> that the existing intent taxonomy couldn't cleanly represent.</p>
<p>Worse, around <strong>11 of the 35 intent kinds</strong> weren't handled by the main switch that consumed the classifier output.</p>
<p>There was also a <code>followUpNeeded</code> value that was being produced but not actually consumed in the relevant routing path.</p>
<p>And there was another three-tier detector sitting in the codebase that, as far as the current application was concerned, had no importers.</p>
<p>The model was getting better.</p>
<p>The application didn't necessarily have anywhere useful to send the result.</p>
<p>So the taxonomy eventually grew to <strong>74 labels</strong>.</p>
<p>This changed how I thought about the project.</p>
<p>I had originally thought I was building a classifier.</p>
<p>I was actually changing a piece of a larger decision system.</p>
<p>The model's output only matters if:</p>
<ol>
<li>the taxonomy represents the application's real decisions,</li>
<li>the application actually consumes those decisions,</li>
<li>the routing is correct,</li>
<li>and the model can distinguish the decisions reliably.</li>
</ol>
<p>A model can be technically excellent and operationally irrelevant.</p>
<hr>
<h2>The dataset had another problem</h2>
<p>There was another number I initially liked:</p>
<p><strong>0.916</strong></p>
<p>It looked good.</p>
<p>The later evaluation used 8,307 training examples and 1,444 evaluation examples.</p>
<p>Across epochs, the score climbed roughly:</p>
<p><strong>0.787 → 0.876 → 0.904 → 0.917 → 0.916</strong></p>
<p>That looks like a healthy training curve.</p>
<p>But I eventually inspected the split itself.</p>
<p>A substantial number of evaluation examples had near-duplicates in the training set.</p>
<p>In one audit, <strong>435 of the 1,444 evaluation rows</strong> had a near-duplicate on the training side.</p>
<p>About 30%.</p>
<p>That makes the headline score much less interesting.</p>
<p>If the evaluation set contains variations of things the model has effectively already seen, the benchmark is answering a weaker question than I thought.</p>
<p>So I changed the process.</p>
<p>The correct order is closer to:</p>
<p><strong>split first → augment only the training side → evaluate on untouched examples</strong></p>
<p>rather than:</p>
<p><strong>generate → augment → split</strong></p>
<p>That sounds like a small implementation detail.</p>
<p>It isn't.</p>
<p>The order in which you construct an experiment can determine whether the experiment means anything.</p>
<hr>
<h2>There were 25,327 "real" messages</h2>
<p>At one point I found a local <code>chat_messages</code> table containing:</p>
<p><strong>25,327 rows.</strong></p>
<p>That sounded promising.</p>
<p>I thought I might have a real corpus.</p>
<p>I investigated it.</p>
<p>There were only <strong>123 distinct idle-stage messages</strong>, spread across about 720 sessions from a load-test day.</p>
<p>The messages came from scripted personas.</p>
<p>They weren't a representative sample of actual trader conversations.</p>
<p>So:</p>
<p><strong>25,327 rows of data</strong></p>
<p>became:</p>
<p><strong>no real trader messages used for training or evaluation.</strong></p>
<p>This was another case where the first number was technically true and practically misleading.</p>
<p>And it reinforced something I was beginning to understand:</p>
<blockquote>
<p><strong>Data provenance matters more than data volume.</strong></p>
</blockquote>
<p>A million synthetic examples are not automatically more valuable than a thousand carefully labelled production examples.</p>
<hr>
<h2>Training was the easy part</h2>
<p>My laptop is a MacBook with 16 GB of memory.</p>
<p>The model I used, AfroXLMR, is around 270 million parameters.</p>
<p>Training locally was possible up to a point.</p>
<p>Then memory became the bottleneck.</p>
<p>I reduced the batch size and used gradient accumulation.</p>
<p>It initially worked.</p>
<p>Then the machine started thrashing.</p>
<p>A training step that had taken around 1.2 seconds eventually approached 10 seconds.</p>
<p>So I stopped trying to make the laptop do a job it wasn't suited for.</p>
<p>I moved training to Vertex AI.</p>
<p>That introduced an entirely different class of problems.</p>
<p>The first cloud training attempt failed because the selected PyTorch image wasn't compatible with the Transformers version I was using.</p>
<p>Some <code>gcloud</code> flags I assumed existed didn't exist in the form I expected.</p>
<p>One duplicated training script couldn't import the project correctly.</p>
<p>At one point a hardcoded label list in the cloud job drifted from the actual taxonomy.</p>
<p>These weren't sophisticated machine-learning problems.</p>
<p>They were engineering problems.</p>
<p>And AI coding agents were involved in several of them.</p>
<p>That's worth emphasizing because AI-assisted development can create a strange illusion of progress.</p>
<p>You can have a repository full of code very quickly.</p>
<p>You can have Docker files.</p>
<p>Training scripts.</p>
<p>Cloud configuration.</p>
<p>FastAPI endpoints.</p>
<p>Export scripts.</p>
<p>Benchmarking tools.</p>
<p>Documentation.</p>
<p>Everything can look finished.</p>
<p>And still be wrong.</p>
<p><strong>AI can write the cloud plumbing. It cannot remove the need to read the error message.</strong></p>
<p>The final training run completed in about <strong>259 seconds</strong> on an L4 GPU.</p>
<p>The infrastructure worked.</p>
<p>But getting there required reading failures rather than asking the agent to simply try again.</p>
<hr>
<h2>What the AI actually did</h2>
<p>The AI coding agents were extremely useful.</p>
<p>They accelerated almost every mechanical part of the project.</p>
<p>They helped scaffold:</p>
<ul>
<li>the training pipeline,</li>
<li>dataset generators,</li>
<li>cloud training jobs,</li>
<li>model export,</li>
<li>FastAPI serving,</li>
<li>benchmarking,</li>
<li>documentation,</li>
<li>and parts of the application routing audit.</li>
</ul>
<p>They also made mistakes.</p>
<p>Among them:</p>
<ul>
<li>incompatible cloud images,</li>
<li>nonexistent CLI flags,</li>
<li>stale hardcoded labels,</li>
<li>shell quoting bugs,</li>
<li>an incorrect Python environment,</li>
<li>and a generator that hung without a timeout and lost thousands of generated rows held only in memory.</li>
</ul>
<p>None of that makes the agents "bad."</p>
<p>It changes what I expect from them.</p>
<p>I don't think the valuable distinction is:</p>
<blockquote>
<p>AI writes code vs. humans write code.</p>
</blockquote>
<p>The more useful distinction is:</p>
<blockquote>
<p><strong>Who is responsible for deciding whether the code is correct?</strong></p>
</blockquote>
<p>The agent can generate ten implementations.</p>
<p>It can explain why each one should work.</p>
<p>It can confidently tell you that the cloud configuration is valid.</p>
<p>That doesn't make any of those things true.</p>
<p>The human's role moves upward.</p>
<p>Less typing.</p>
<p>More auditing.</p>
<p>Less implementation trivia.</p>
<p>More experimental design.</p>
<p>Less:</p>
<blockquote>
<p>"How do I build this?"</p>
</blockquote>
<p>More:</p>
<blockquote>
<p>"What evidence would make me change my mind?"</p>
</blockquote>
<hr>
<h2>So is the model good?</h2>
<p>I don't know yet.</p>
<p>And that's the honest answer.</p>
<p>The <strong>0.916 held-out accuracy</strong> is a useful measurement.</p>
<p>It is not a production accuracy claim.</p>
<p>The current evaluation has several limitations.</p>
<p>The training data is synthetic.</p>
<p>The evaluation set had leakage problems that I had to fix in the data-generation pipeline.</p>
<p>There is no permanently untouched real-world test set.</p>
<p>The classes are imbalanced. One label had 573 generated examples while another had 170.</p>
<p>The weakest per-label recall was around 0.69.</p>
<p>The softmax confidence has not been properly calibrated on real labelled traffic.</p>
<p>And the application recognizes language and register patterns, including Kikuyu and Sheng, that aren't fully represented in the generated training data.</p>
<p>I also need better evaluation metrics than a single accuracy number.</p>
<p>Macro-F1.</p>
<p>Per-intent precision and recall.</p>
<p>Confusion matrices.</p>
<p>Calibration.</p>
<p>Performance on real production messages.</p>
<p>Most importantly, I need to know what happens when the model sees language it wasn't trained to imitate.</p>
<p>That is the experiment that matters.</p>
<hr>
<h2>What I would do next</h2>
<p>The next version of this project is much less glamorous.</p>
<p>I want to export a representative sample of production messages.</p>
<p>Then I want humans to label them.</p>
<p>Not thousands at first.</p>
<p>A few hundred carefully selected examples would already tell me far more than another 10,000 synthetic messages.</p>
<p>I'd want people who actually understand the language and conversational patterns involved, particularly for Swahili and Hausa.</p>
<p>I'd keep a real test set permanently outside training.</p>
<p>Then:</p>
<ol>
<li>Fix the split and augmentation pipeline.</li>
<li>Train without contaminating evaluation data.</li>
<li>Measure macro-F1 and per-intent confusion.</li>
<li>Test on real trader messages.</li>
<li>Calibrate the confidence threshold.</li>
<li>Finish routing the expanded 74-label taxonomy.</li>
<li>Run the classifier beside Gemini in shadow mode.</li>
<li>Compare predictions before allowing the classifier to make production decisions.</li>
</ol>
<p>Only then would I decide whether the cascade is actually useful.</p>
<p>And I wouldn't be surprised if the answer is no.</p>
<p>That would still make the project successful.</p>
<hr>
<h2>The thing I was actually learning</h2>
<p>When I started this, I thought I was learning machine learning.</p>
<p>I was.</p>
<p>But that wasn't the most important thing.</p>
<p>I learned what supervised classification is.</p>
<p>I learned about transfer learning, embeddings, sentence-pair classification, stratified splits, class imbalance, quantization, inference, calibration, and model serving.</p>
<p>Those things matter.</p>
<p>But the more important lesson was about engineering judgment.</p>
<p>When implementation becomes cheap, <strong>verification becomes scarce</strong>.</p>
<p>AI made it possible for me to go from:</p>
<blockquote>
<p>"I want to build a classifier"</p>
</blockquote>
<p>to:</p>
<blockquote>
<p>"Here is a trained model, an evaluation script, a cloud job, an ONNX export, and a FastAPI endpoint"</p>
</blockquote>
<p>very quickly.</p>
<p>That was useful.</p>
<p>It was also dangerous.</p>
<p>Because every stage produced artifacts that looked like progress.</p>
<p>The model had a score.</p>
<p>The API returned JSON.</p>
<p>The cloud job completed.</p>
<p>The ONNX model ran in seven milliseconds.</p>
<p>The dataset had thousands of rows.</p>
<p>The repository had documentation.</p>
<p>And yet:</p>
<ul>
<li>the quantized model was broken,</li>
<li>the model learned a shortcut,</li>
<li>the evaluation split leaked,</li>
<li>the taxonomy didn't match the application,</li>
<li>and the "real" dataset wasn't real.</li>
</ul>
<p>None of those failures were caused by a lack of code.</p>
<p>They were caused by asking the wrong questions, or asking them too late.</p>
<p>The most valuable skill I practiced wasn't training a model.</p>
<p>It was learning to ask:</p>
<blockquote>
<p><strong>What would I expect to see if this were actually working?</strong></p>
</blockquote>
<p>Then checking.</p>
<p>The classifier may eventually replace some of the Gemini calls.</p>
<p>It may not.</p>
<p>I still don't know whether a model trained entirely on synthetic trader language will hold up when real traders start using it.</p>
<p>That's the next experiment.</p>
<p>And this time, I know what I need to measure.</p>]]></content:encoded>
            <author>kenmsh@gmail.com (Kennedy Mutisya)</author>
        </item>
        <item>
            <title><![CDATA[Tinkerwell Cheatsheet: The Ultimate Guide to the PHP Playground for Laravel Developers]]></title>
            <link>mutisyadev.xyz/articles/tinkerwell-cheatsheet</link>
            <guid>mutisyadev.xyz/articles/tinkerwell-cheatsheet</guid>
            <pubDate>Sun, 20 Jul 2025 00:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<h1>Tinkerwell Cheatsheet: The Ultimate Guide to the PHP Playground for Laravel Developers</h1>
<p>Are you a Laravel developer looking to boost your productivity and streamline your PHP development workflow? Tinkerwell is your go-to PHP playground. This comprehensive Tinkerwell cheatsheet covers all the essential keyboard shortcuts, smart features, and hidden tools to help you write, test, and debug code faster than ever.</p>
<h2>🔑 Tinkerwell Keyboard Shortcuts (macOS &amp; Windows/Linux)</h2>
<p>Use this quick reference table to save time and work smarter inside the Tinkerwell editor:</p>
<table><thead><tr><th>Action</th><th>macOS</th><th>Windows/Linux</th></tr></thead><tbody><tr><td>Run code</td><td><code>Cmd+R</code></td><td><code>Ctrl+R</code></td></tr><tr><td>Run selected code</td><td><code>Cmd+Shift+R</code></td><td><code>Ctrl+Shift+R</code></td></tr><tr><td>Open Anything (Command Palette)</td><td><code>Cmd+Shift+P</code></td><td><code>Ctrl+Shift+P</code></td></tr><tr><td>Prettify code</td><td><code>Cmd+P</code></td><td><code>Ctrl+P</code></td></tr><tr><td>Toggle AI Modal</td><td><code>Cmd+Shift+L</code></td><td><code>Ctrl+Shift+L</code></td></tr><tr><td>Auto Evaluate Code</td><td><code>Cmd+Shift+A</code></td><td><code>Ctrl+Shift+A</code></td></tr><tr><td>Clear Output</td><td><code>Ctrl+L</code></td><td><code>Ctrl+L</code></td></tr><tr><td>Toggle Editor Layout</td><td><code>Ctrl+.</code></td><td><code>Ctrl+.</code></td></tr><tr><td>Toggle Toolbar</td><td><code>Cmd+Alt+Shift+T</code></td><td><code>Ctrl+Alt+Shift+T</code></td></tr><tr><td>Duplicate Tab</td><td><code>Cmd+Shift+D</code></td><td><code>Ctrl+Shift+D</code></td></tr><tr><td>Open Welcome Tab</td><td><code>Cmd+Shift+W</code></td><td><code>Ctrl+Shift+W</code></td></tr><tr><td>Open Preferences</td><td><code>Cmd+,</code></td><td><code>Ctrl+,</code></td></tr><tr><td>View History</td><td><code>Cmd+Y</code></td><td><code>Ctrl+I</code></td></tr><tr><td>Toggle Snippets</td><td><code>Cmd+B</code></td><td><code>Ctrl+B</code></td></tr><tr><td>View Logs</td><td><code>Cmd+L</code></td><td><code>Ctrl+L</code></td></tr></tbody></table>
<h2>⚙️ Essential Tinkerwell Shortcuts for Daily Productivity</h2>
<h3>✅ Code Execution</h3>
<p><strong>Run Code</strong>: Instantly execute the full code in the editor (<code>Cmd+R</code> / <code>Ctrl+R</code>)</p>
<p><strong>Run Selected Code</strong>: Only run the highlighted portion of your script (<code>Cmd+Shift+R</code> / <code>Ctrl+Shift+R</code>)</p>
<p><strong>Auto Evaluate</strong>: Let Tinkerwell auto-run your code as you type (<code>Cmd+Shift+A</code> / <code>Ctrl+Shift+A</code>)</p>
<h3>🔍 Navigation &amp; Interface Control</h3>
<p><strong>Open Anything</strong>: Use the command palette to jump to files, functions, or features (<code>Cmd+Shift+P</code> / <code>Ctrl+Shift+P</code>)</p>
<p><strong>Toggle AI Modal</strong>: Interact with Tinkerwell's built-in AI assistant for code suggestions (<code>Cmd+Shift+L</code> / <code>Ctrl+Shift+L</code>)</p>
<p><strong>Toggle Snippets</strong>: Quickly access and insert your saved snippets (<code>Cmd+B</code> / <code>Ctrl+B</code>)</p>
<p><strong>View History</strong>: Go back in time and revisit past inputs and outputs (<code>Cmd+Y</code> / <code>Ctrl+I</code>)</p>
<h3>🧹 Code Management Tools</h3>
<p><strong>Prettify Code</strong>: Format your PHP code for clarity and consistency (<code>Cmd+P</code> / <code>Ctrl+P</code>)</p>
<p><strong>Clear Output</strong>: Clean your output panel for a fresh view (<code>Ctrl+L</code>)</p>
<p><strong>Duplicate Tab</strong>: Clone your current session to test variations without losing progress (<code>Cmd+Shift+D</code> / <code>Ctrl+Shift+D</code>)</p>
<h3>💡 Pro Tip for Power Users</h3>
<p><strong>Switch Editor Layout</strong>: Use <code>Ctrl+.</code> to toggle between split and single views – perfect for multitasking or comparing code blocks side-by-side.</p>
<h2>🚀 Supercharge Your Laravel Workflow with Tinkerwell</h2>
<p>Whether you're debugging complex logic, experimenting with Eloquent queries, or just tinkering with ideas, Tinkerwell helps you stay in flow.</p>
<p>Save this guide, bookmark it, and share it with fellow developers.</p>
<p>Happy tinkering! 🎯</p>]]></content:encoded>
            <author>kenmsh@gmail.com (Kennedy Mutisya)</author>
        </item>
        <item>
            <title><![CDATA[Tools That Power My Day]]></title>
            <link>mutisyadev.xyz/articles/tools-that-power-my-day</link>
            <guid>mutisyadev.xyz/articles/tools-that-power-my-day</guid>
            <pubDate>Wed, 16 Jul 2025 00:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<h2>Introduction</h2>
<p>As a developer working on macOS, the right set of tools can make all the difference between a productive day and a frustrating one. Over the years, I’ve curated a toolkit that not only streamlines my workflow but also makes my daily routine genuinely enjoyable. Here’s a look at the essential apps and utilities that power my day.</p>
<h2>My Essential Tools</h2>
<ul>
<li><a href="https://www.jetbrains.com/phpstorm/"><strong>PHPStorm</strong></a> – My primary editor. It’s where most of my work happens, from writing code to debugging and everything in between.</li>
<li><a href="https://cleanshot.com/"><strong>CleanShot</strong></a> – For capturing and annotating screenshots with ease.</li>
<li><a href="https://www.google.com/chrome/"><strong>Chrome</strong></a> &amp; <a href="https://arc.net/"><strong>Arc</strong></a> – My browsers of choice for development and research.</li>
<li><a href="https://dolphin-anty.com/?"><strong>Dolphin {anty}</strong></a> – Multiple browsers. (Could be logged in the same app with different users. Handy when I am debugging.).</li>
<li><a href="https://www.raycast.com/"><strong>Raycast</strong></a> – The real game-changer. More on this below.</li>
<li><a href="https://www.warp.dev/"><strong>Warp</strong></a> – My terminal of choice, especially when I need a modern, fast CLI experience.</li>
<li><a href="https://screen.studio/"><strong>Screen Studio</strong></a> &amp; <a href="https://presentify.compzets.com/"><strong>Presentify</strong></a> – My secret weapon for making quick explainer videos or PR walkthroughs.</li>
<li><a href="https://obsproject.com/"><strong>OBS</strong></a> – For more advanced screen recording needs.</li>
<li><a href="https://tableplus.com/"><strong>TablePlus</strong></a> – My go-to for browsing and managing MySQL and ClickHouse databases.</li>
<li><a href="https://tinkerwell.app/"><strong>Tinkerwell</strong></a> – For running PHP code snippets and experimenting quickly.</li>
<li><a href="https://runjs.app/"><strong>RunJS</strong></a> – A JavaScript playground for quick tests.</li>
<li><a href="https://www.raycast.com/store/focus"><strong>Raycast Focus</strong></a> – Helps me stay in the zone.</li>
<li><a href="https://www.cursor.so/"><strong>Cursor</strong></a> &amp; <a href="https://claude.ai/"><strong>Claude Code</strong></a> – For AI-powered coding assistance.</li>
<li><a href="https://www.docker.com/"><strong>Docker</strong></a> – Ensures my local environment matches production as closely as possible.</li>
<li><a href="https://www.raycast.com/store/notes"><strong>Raycast Notes</strong></a> – For jotting down quick ideas.</li>
<li><a href="https://www.git-tower.com/"><strong>Git Tower</strong></a> – Great for visualizing git history, but I still love the command line for most git operations.</li>
<li><a href="https://www.spotify.com/"><strong>Spotify (Instrumentals)</strong></a> – My soundtrack for focus and flow.</li>
</ul>
<h2>Why I Love Raycast</h2>
<p>Raycast has completely changed the way I interact with my Mac. With extensions for virtually all my apps and AI available at the press of a button, it’s become the central hub for my workflow. Whether I’m launching apps, searching files, or running custom scripts, Raycast makes everything faster and more intuitive.</p>
<h2>Hidden Gem: Presentify</h2>
<p>If you ever need to make quick videos for PRs or explainers, Presentify is a gem. It lets me annotate my screen in real-time, making my recordings much clearer and more engaging. It’s one of those tools I wish I’d discovered sooner.</p>
<h2>Recent Discoveries I Can’t Live Without</h2>
<p>Raycast is the standout here. Since I started using it, my productivity has noticeably improved. It’s not just an app launcher. It’s a workflow enhancer.</p>
<h2>GUI vs. Command Line</h2>
<p>I use TablePlus for browsing and managing databases, primarily MySQL and ClickHouse. For git, I prefer the command line (like a true cave man), but Git Tower is great for visualizing complex histories. Warp is especially helpful when I get stuck or need a more modern CLI experience.</p>
<h2>Raycast Extensions &amp; Custom Scripts</h2>
<p>One of my favorite things about Raycast is the ability to create custom scripts. I have a script that prepares my code environment: it checks out the correct branch and does some plumbing based on my JIRA ticket. This automation saves me time and mental energy every day.</p>
<h2>Staying Focused</h2>
<p>To stay in the zone, I rely on Raycast Focus and instrumental playlists on Spotify. This combo helps me block out distractions and maintain deep focus during coding sessions.</p>
<h2>If I Could Only Keep Three Tools</h2>
<p>If I had to choose just three tools from my list, they would be:</p>
<ol>
<li><strong>PHPStorm</strong> – It’s my primary editor and the heart of my workflow.</li>
<li><strong>Raycast Focus</strong> – It fundamentally improves the way I interact with my Mac and helps me stay productive.</li>
<li><strong>Docker</strong> – It ensures my development environment is as close to production as possible, reducing surprises during deployment.</li>
</ol>
<h2>Final Thoughts</h2>
<p>The right tools don’t just make you more productive. They make your work more enjoyable. These are the apps and utilities that power my day as a developer on macOS. If you’re looking to optimize your own workflow, I highly recommend giving some of these a try. Who knows, you might discover a new favorite!</p>]]></content:encoded>
            <author>kenmsh@gmail.com (Kennedy Mutisya)</author>
        </item>
    </channel>
</rss>