<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Anass Ez-zouaine — Backend, AI &amp; Shopify Engineer</title><description>Senior backend, software architect, and AI engineer. 14+ years shipping Laravel SaaS, Shopify Plus apps, and AI features (Claude, MCP, RAG). Remote-first.</description><link>https://ansezz.com/</link><item><title>Trust is not a QA strategy: test AI code too</title><link>https://ansezz.com/blog/testing-ai-generated-code/</link><guid isPermaLink="true">https://ansezz.com/blog/testing-ai-generated-code/</guid><description>AI code is 95% syntactically clean and fails security tests 45% of the time. Half of agentic PRs ship no tests. The verification pipeline I run instead.</description><pubDate>Sun, 09 Aug 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Trust is not a QA strategy.&lt;/p&gt;
&lt;p&gt;It does not matter whether you are reviewing a pull request from a junior developer or accepting an entire feature an autonomous agent produced in ninety seconds. The engineering question is identical: does this code actually work, and how do you know?&lt;/p&gt;
&lt;p&gt;Somewhere in the shift to &lt;a href=&quot;https://ansezz.com/blog/agentic-workflows-vibe-coding/&quot;&gt;agentic workflows&lt;/a&gt;, a lot of teams quietly stopped asking the second half of that question. LLMs emit code that compiles, lints clean, and reads better than most of what humans write under deadline. So the code &lt;em&gt;looks&lt;/em&gt; reviewed. Teams merge it, ship it, and find out what it does in production.&lt;/p&gt;
&lt;h2&gt;Syntax is not semantics&lt;/h2&gt;
&lt;p&gt;This is the part that catches experienced engineers off guard, because our instincts were trained on human output.&lt;/p&gt;
&lt;p&gt;Human code that looks sloppy usually &lt;em&gt;is&lt;/em&gt; rough, and we review it accordingly. Human code that looks polished usually got there because someone iterated on it — which means someone thought about it. Polish was a proxy for scrutiny.&lt;/p&gt;
&lt;p&gt;LLMs break that correlation completely. Veracode&apos;s &lt;a href=&quot;https://www.veracode.com/blog/spring-2026-genai-code-security/&quot;&gt;Spring 2026 GenAI Code Security update&lt;/a&gt; measured a syntax correctness rate above 95% across the models it tested, against a security pass rate of 55%. The code is almost always well-formed. It is secure roughly half the time. Formatting stopped carrying any signal about correctness, and most review habits have not caught up.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// Generated in one shot. Clean names, correct types, zero lint warnings.
function calculateDiscount(user: User, cartTotal: number): number {
  if (user.isVip) {
    return cartTotal * 0.2;
  }
  return cartTotal * 0.05;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Nothing here is &lt;em&gt;wrong&lt;/em&gt;, exactly. It is just unfinished in ways that only show up under real traffic: no guard against a negative &lt;code&gt;cartTotal&lt;/code&gt; from a refund flow, no clamp on the maximum discount, no decision about what happens when &lt;code&gt;user&lt;/code&gt; arrives undefined from a cache miss. The type signature says those cases cannot happen. The type signature is a claim about the code, not about production.&lt;/p&gt;
&lt;h2&gt;What the data actually says&lt;/h2&gt;
&lt;p&gt;Veracode&apos;s Spring 2026 report evaluated 80 coding tasks across Java, JavaScript, C#, and Python. The aggregate numbers are worth internalizing, because the failures are not evenly spread — they cluster hard around exactly the categories where a missing sanitizer is invisible in review.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;CWE category&lt;/th&gt;
&lt;th&gt;Security pass rate&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Insecure cryptography (CWE-327)&lt;/td&gt;
&lt;td&gt;86%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SQL injection (CWE-89)&lt;/td&gt;
&lt;td&gt;82%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cross-site scripting (CWE-80)&lt;/td&gt;
&lt;td&gt;15%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Log injection (CWE-117)&lt;/td&gt;
&lt;td&gt;13%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/testing-ai-generated-code/security-metrics.webp&quot; alt=&quot;Dashboard visualization of AI code security pass rates and vulnerability counts by category&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Parameterized queries and modern crypto defaults are baked into the training data — models get those right most of the time because the safe pattern &lt;em&gt;is&lt;/em&gt; the common pattern. Output encoding is contextual. Whether a string needs HTML-escaping, attribute-escaping, or JS-escaping depends on where it lands in the template, and the model cannot see the template from inside the function it is writing. So it guesses, and it guesses wrong about six times out of seven.&lt;/p&gt;
&lt;p&gt;Language matters too. The same report puts Python at a 62% security pass rate and Java at 29%. If your stack is JVM-heavy, the baseline you are working from is worse than the headline number suggests.&lt;/p&gt;
&lt;p&gt;One genuinely encouraging data point: GPT-5 with extended reasoning hit 70–72%, the best pass rate Veracode has observed. That is real improvement. It is still a coin flip you would never accept from a human contributor.&lt;/p&gt;
&lt;h2&gt;The testing gap in agentic pull requests&lt;/h2&gt;
&lt;p&gt;Chat completions are one thing. Autonomous agents opening PRs are another, and this is where the accounting gets uncomfortable.&lt;/p&gt;
&lt;p&gt;A July 2026 empirical study accepted at ICSME, &lt;a href=&quot;https://arxiv.org/abs/2607.18057&quot;&gt;Test Coverage Analysis of Agentic Pull Requests&lt;/a&gt;, analysed 4,882 agent-generated pull requests from the AIDev dataset — 532 Java, 4,350 Python, across five coding agents. Two findings should reshape how you gate merges:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Agents included test changes in only 49.6% of the PRs that touched code under test.&lt;/strong&gt; Not greenfield repos with no test infrastructure — code that already sat inside a test suite, changed without the suite being touched.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Existing test suites did not pick up the slack.&lt;/strong&gt; They executed 61.5% of the agents&apos; changed executable lines in Java and just 27.0% in Python. In 64.8% of Python PRs, &lt;em&gt;not one changed line&lt;/em&gt; was executed by any existing test. The safety net everyone assumes is there is mostly holes.&lt;/p&gt;
&lt;p&gt;Agents optimize for satisfying the prompt. Ask for a checkout feature and you get a controller, a migration, and a view — because that is what &quot;done&quot; looks like from the prompt&apos;s perspective. Boundary conditions, negative assertions, and concurrency cases are not in the prompt, so they are not in the diff.&lt;/p&gt;
&lt;h2&gt;Coverage numbers are lying to you&lt;/h2&gt;
&lt;p&gt;Here is the failure mode I see most often: a team wires up a coverage gate, sees 90%, and concludes the AI-authored code is verified.&lt;/p&gt;
&lt;p&gt;Line coverage measures execution, not verification. It tells you a line ran during the test suite. It says nothing about whether anything checked the result.&lt;/p&gt;
&lt;p&gt;A separate 2026 study, &lt;a href=&quot;https://arxiv.org/abs/2606.18168&quot;&gt;All Smoke, No Alarm&lt;/a&gt;, looked specifically at what agent-written tests assert. Across 86,156 test-file patches from 33,596 agent-authored pull requests spanning 2,807 repositories — Codex, Copilot, Devin, Cursor, and Claude Code — 80.2% of those patches contained weak or no explicit oracle signals. An agent rewarded for a green build learns very quickly that the cheapest path to green is an assertion that cannot fail.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# 100% line coverage on process_payment. Verifies essentially nothing.
def test_process_payment():
    result = process_payment(100)
    assert result is not None
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That test passes if &lt;code&gt;process_payment&lt;/code&gt; charges the right amount. It also passes if it charges zero, double-charges, returns a stale object, or silently swallows a declined card. Coverage went up. Confidence should not have.&lt;/p&gt;
&lt;p&gt;The fix is mutation testing, which measures the thing you actually care about: if I break the code, does a test go red? Stryker for JS/TS, Infection for PHP, mutmut for Python, PIT for Java. Mutation score is the only coverage-adjacent metric I have found that agents cannot game by writing more assertions of nothing.&lt;/p&gt;
&lt;h2&gt;Error handling that hides the failure&lt;/h2&gt;
&lt;p&gt;Back in the ICSME coverage study, there is a number that gets less attention than it deserves: existing tests miss 86.0% of the try/catch blocks agents touch in Java, and 81.0% of Python error-handling constructs. The recovery paths are the least-tested code in the diff.&lt;/p&gt;
&lt;p&gt;This is the worst possible failure shape. A crash is loud, has a stack trace, and pages someone. A &lt;code&gt;catch&lt;/code&gt; block that logs a generic string and returns a default value produces a system that keeps running while quietly returning wrong answers. You do not find those in staging. You find them in a reconciliation report three weeks later.&lt;/p&gt;
&lt;p&gt;When you review agent output, read the &lt;code&gt;catch&lt;/code&gt; blocks first. Ask what happens to the caller when the block executes, and whether the log line contains enough context to reconstruct the failure at 3am. That question is also the difference between &lt;a href=&quot;https://ansezz.com/blog/logging-vs-monitoring/&quot;&gt;logging and monitoring&lt;/a&gt; — a swallowed exception is logged, and monitored by nobody.&lt;/p&gt;
&lt;h2&gt;The throughput and stability trade&lt;/h2&gt;
&lt;p&gt;The &lt;a href=&quot;https://dora.dev/dora-report-2025/&quot;&gt;2025 DORA State of AI-assisted Software Development&lt;/a&gt; report is the macro version of everything above. Its central finding: higher AI adoption correlates with an increase in delivery throughput &lt;em&gt;and&lt;/em&gt; an increase in delivery instability, at the same time.&lt;/p&gt;
&lt;p&gt;That is not an argument against AI. It is a statement about where the bottleneck moved. When change volume goes up and the control systems — automated testing, version control discipline, fast feedback — stay where they were, instability is the arithmetic result. DORA&apos;s own ROI model puts a number on it: in their sample calculation, a change failure rate drifting from 5% to 6% after AI adoption costs $344,000 in downtime.&lt;/p&gt;
&lt;p&gt;AI amplifies whatever your delivery process already was. That is the same trade-off I mapped out in &lt;a href=&quot;https://ansezz.com/blog/ai-vs-traditional-development/&quot;&gt;AI vs traditional development&lt;/a&gt;, now with a large-scale dataset behind it instead of an argument.&lt;/p&gt;
&lt;h2&gt;What I actually measure&lt;/h2&gt;
&lt;p&gt;Coverage percentage is a vanity metric here. These four are not:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Change failure rate, segmented.&lt;/strong&gt; Tag agent-authored PRs at open time and compute CFR separately from human-authored changes. If you cannot split the two, you cannot tell whether AI is helping.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Mutation score on the diff.&lt;/strong&gt; Not the repo — the changed lines. It answers &quot;would a test catch a regression here&quot; directly.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Test-change ratio.&lt;/strong&gt; What share of code-modifying PRs also modify tests. Industry baseline is roughly 50%. Yours should be visible on a dashboard.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Diff coverage, not total coverage.&lt;/strong&gt; Total coverage hides new code behind a large tested legacy base. Diff coverage exposes exactly the lines that just arrived.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/testing-ai-generated-code/verification-pipeline.webp&quot; alt=&quot;Architecture diagram of a CI pipeline with static analysis, diff coverage, and mutation testing gates&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;The verification pipeline&lt;/h2&gt;
&lt;p&gt;Manual review does not scale against agent output. A reviewer facing 800 lines of plausible-looking code per hour is not reviewing — they are skimming for style. The gate has to be automatic, and it has to run on the diff. I make the full case for &lt;a href=&quot;https://ansezz.com/blog/stop-reading-code-ai-review/&quot;&gt;giving up line-by-line reading&lt;/a&gt; separately; this is the tooling that has to exist first.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;name: Agentic PR gate
on:
  pull_request:
    types: [opened, synchronize, reopened]

jobs:
  sast:
    runs-on: ubuntu-latest
    container:
      image: semgrep/semgrep
    steps:
      - uses: actions/checkout@v5
      - run: semgrep ci --config=p/owasp-top-ten

  diff-coverage:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v5
        with:
          fetch-depth: 0
      - uses: shivammathur/setup-php@v2
        with:
          php-version: &quot;8.4&quot;
          coverage: pcov
      - run: composer install --no-interaction --prefer-dist
      - run: vendor/bin/phpunit --coverage-cobertura=coverage.xml
      - run: pipx install diff-cover
      # Fail on new lines that no test executes — total coverage is irrelevant here.
      - run: diff-cover coverage.xml --compare-branch=origin/${{ github.base_ref }} --fail-under=90

  mutation:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v5
        with:
          fetch-depth: 0
      - uses: shivammathur/setup-php@v2
        with:
          php-version: &quot;8.4&quot;
          coverage: pcov
      - run: composer install --no-interaction --prefer-dist
      # Mutate only the changed files, and require tests to actually kill the mutants.
      - run: |
          vendor/bin/infection \
            --git-diff-filter=AM \
            --git-diff-base=origin/${{ github.base_ref }} \
            --min-msi=70 \
            --threads=max
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Three layers, each catching what the previous one cannot. Semgrep&apos;s OWASP ruleset catches the XSS and injection patterns that dominate the failure table above. &lt;code&gt;diff-cover&lt;/code&gt; refuses the PR where new lines are unexecuted. Infection refuses the PR where new lines are executed but unasserted — the oracle problem, enforced by a machine rather than by a tired reviewer.&lt;/p&gt;
&lt;p&gt;Two practices sit upstream of the pipeline and are worth more than any of it:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Write the spec first.&lt;/strong&gt; Give the agent the test file, the OpenAPI schema, or the behavioral contract before it writes the implementation. It is much better at satisfying an explicit oracle than at inventing one, and you keep authorship of the definition of correct.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Give the agent real context.&lt;/strong&gt; A large share of the XSS failures come from the model not being able to see the rendering context. Wiring your agent into the actual schema, actual codebase, and actual docs — the &lt;a href=&quot;https://ansezz.com/blog/claude-mcp-dev-tools/&quot;&gt;MCP tooling story&lt;/a&gt; — measurably reduces guessing. It reduces it. It does not remove the need to verify.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/testing-ai-generated-code/review-workflow.webp&quot; alt=&quot;Software testing workflow illustration with review checklists and coverage graphs&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Takeaways&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Polish stopped being a proxy for scrutiny.&lt;/strong&gt; 95% syntax correctness against a 55% security pass rate means clean formatting now tells you nothing about whether the logic holds.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Failures cluster in context-dependent categories.&lt;/strong&gt; Crypto and SQL injection pass 82–86% of the time. XSS and log injection pass 13–15%. Review output encoding first.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Half of agentic PRs touch code without touching tests&lt;/strong&gt;, and existing suites execute as little as 27% of the changed lines. Gate on test presence, not good intentions.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Mutation score over line coverage.&lt;/strong&gt; 80.2% of agent-written test patches carry weak or absent oracles. Only mutation testing catches an assertion that cannot fail.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Read the catch blocks first.&lt;/strong&gt; Test suites miss over 80% of the error-handling paths agents touch, which means the likely production symptom is a wrong answer, not a crash.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;None of this is an argument for writing code by hand. I ship agent-authored code every week. The argument is that verification is the part of the job that did not get automated, and pretending otherwise just moves the discovery of your bugs from CI to your customers.&lt;/p&gt;
&lt;p&gt;What is currently standing between an agent-authored PR and production in your pipeline? If the honest answer is &quot;a human skimming the diff,&quot; that is worth fixing this quarter — &lt;a href=&quot;https://ansezz.com/contact/&quot;&gt;tell me what your stack looks like&lt;/a&gt; and I will tell you where I would put the first gate. 🤘&lt;/p&gt;
</content:encoded><category>ai</category><category>ai</category><category>code-quality</category><category>agentic-ai</category><category>ci-cd</category></item><item><title>Building secure agentic commerce on Shopify in 2026</title><link>https://ansezz.com/blog/secure-agentic-commerce-shopify/</link><guid isPermaLink="true">https://ansezz.com/blog/secure-agentic-commerce-shopify/</guid><description>How agentic commerce on Shopify stays safe: UCP negotiation, an MCP governance layer, scoped credentials, signed requests, and human approval at checkout.</description><pubDate>Fri, 07 Aug 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;An AI agent can discover a product, build a cart, and reach checkout faster than a human. It can also make an expensive mistake in seconds.&lt;/p&gt;
&lt;p&gt;Agentic commerce changes the security boundary of a Shopify store. A storefront is no longer only serving pages to people. It exposes product data, pricing, inventory, fulfillment options, checkout actions, and order state to autonomous software.&lt;/p&gt;
&lt;p&gt;That creates a real problem. If an agent receives broad Shopify credentials, a prompt injection or an implementation bug can expose customer data or trigger an unauthorized transaction. The fix isn&apos;t to block agents. It&apos;s to give them a narrow, observable, policy-controlled path through the commerce stack.&lt;/p&gt;
&lt;h2&gt;The security problem in agentic commerce&lt;/h2&gt;
&lt;p&gt;Traditional Shopify integrations assume a known application, a fixed workflow, and a human reviewing the final checkout screen. Autonomous agents remove all three assumptions.&lt;/p&gt;
&lt;p&gt;A buyer agent may:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Interpret a natural-language request.&lt;/li&gt;
&lt;li&gt;Search product catalogs.&lt;/li&gt;
&lt;li&gt;Compare variants, prices, and delivery options.&lt;/li&gt;
&lt;li&gt;Build a cart across multiple turns.&lt;/li&gt;
&lt;li&gt;Select a payment handler.&lt;/li&gt;
&lt;li&gt;Complete checkout or hand the buyer to a merchant page.&lt;/li&gt;
&lt;li&gt;Monitor fulfillment, refunds, returns, or cancellations.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Every step carries model uncertainty. The agent may misread intent. Product data may be stale. A tool description may be too broad. An external page may carry malicious instructions. A retry may duplicate an action that moves money.&lt;/p&gt;
&lt;p&gt;This is why agentic commerce needs a different engineering model. The agent should never hold unrestricted access to Shopify Admin APIs. It should reach approved commerce capabilities through a controlled protocol layer.&lt;/p&gt;
&lt;p&gt;The principle is short:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Let the agent reason about commerce, but never let it define its own permissions.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;How Shopify agentic commerce works with UCP&lt;/h2&gt;
&lt;p&gt;The &lt;strong&gt;Universal Commerce Protocol (UCP)&lt;/strong&gt; gives agents and merchants a standard way to describe capabilities, negotiate compatibility, and execute commerce workflows.&lt;/p&gt;
&lt;p&gt;Shopify&apos;s UCP implementation covers the main buyer journey:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Negotiation and authentication&lt;/li&gt;
&lt;li&gt;Product discovery&lt;/li&gt;
&lt;li&gt;Cart creation and updates&lt;/li&gt;
&lt;li&gt;Checkout creation and completion&lt;/li&gt;
&lt;li&gt;Order monitoring and post-purchase events&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;UCP is not a replacement for Shopify&apos;s commerce APIs. It&apos;s a standardized interaction layer so different agents and merchant systems can speak in shared commerce concepts. It supports multiple transports — REST, JSON-RPC, MCP, and A2A, plus the JSON-RPC 2.0 channel behind Shopify&apos;s Embedded Checkout Protocol — while the business logic stays the same.&lt;/p&gt;
&lt;p&gt;The mechanism that matters for security is profile negotiation.&lt;/p&gt;
&lt;p&gt;A merchant publishes a business profile, usually at:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;https://store.example.com/.well-known/ucp
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;On Shopify, that profile is tied to the merchant storefront. It&apos;s a self-contained document declaring &lt;code&gt;version&lt;/code&gt;, &lt;code&gt;services&lt;/code&gt;, &lt;code&gt;capabilities&lt;/code&gt;, &lt;code&gt;payment_handlers&lt;/code&gt;, and — via &lt;code&gt;supported_versions&lt;/code&gt; — the profile URIs for older protocol versions. (See the &lt;a href=&quot;https://ansezz.com/blog/shopify-ucp-quick-start/&quot;&gt;Shopify UCP quick-start&lt;/a&gt; for getting the manifest live.)&lt;/p&gt;
&lt;p&gt;The agent hosts its own platform profile at an HTTPS URL and passes that URL on every request through the &lt;code&gt;UCP-Agent&lt;/code&gt; header. The platform profile mirrors the business one and adds a &lt;code&gt;signing_keys&lt;/code&gt; registry. That single document is both the capability declaration and the key-resolution mechanism — which is why profile validation is a security control, not a formality.&lt;/p&gt;
&lt;p&gt;Shopify then computes the intersection:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;merchant capabilities ∩ agent capabilities = negotiated capabilities
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Only compatible capabilities become active. Extensions — &lt;code&gt;dev.ucp.shopping.discounts&lt;/code&gt;, &lt;code&gt;dev.ucp.shopping.buyer_consent&lt;/code&gt;, &lt;code&gt;dev.ucp.shopping.fulfillment&lt;/code&gt; — carry an &lt;code&gt;extends&lt;/code&gt; field naming their parent capability, and they never auto-activate. If the parent isn&apos;t in the negotiated set, the extension drops out with it.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/secure-agentic-commerce-shopify/ucp-negotiation.webp&quot; alt=&quot;Diagram of UCP profile discovery, capability negotiation, and payment handler selection&quot; /&gt;&lt;/p&gt;
&lt;p&gt;A trimmed platform profile, showing the shape rather than a complete document:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
  &quot;version&quot;: &quot;2026-04-08&quot;,
  &quot;services&quot;: {
    &quot;dev.ucp.shopping&quot;: {}
  },
  &quot;capabilities&quot;: {
    &quot;dev.ucp.shopping.cart&quot;: {},
    &quot;dev.ucp.shopping.checkout&quot;: {}
  },
  &quot;signing_keys&quot;: [
    {
      &quot;kty&quot;: &quot;EC&quot;,
      &quot;crv&quot;: &quot;P-256&quot;,
      &quot;kid&quot;: &quot;agent-2026-08&quot;,
      &quot;x&quot;: &quot;...&quot;,
      &quot;y&quot;: &quot;...&quot;
    }
  ]
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Protocol versions are still moving through 2026 — check the &lt;a href=&quot;https://ucp.dev/specification/reference/&quot;&gt;UCP specification&lt;/a&gt; for the current registry shape. Pin the version your integration uses, validate it strictly, and test version negotiation before rollout. Never silently downgrade to an older capability set.&lt;/p&gt;
&lt;p&gt;For the wider shift from visual storefronts to programmable commerce, see &lt;a href=&quot;https://ansezz.com/blog/agentic-commerce-shopify/&quot;&gt;why agentic commerce will change the way you build Shopify stores&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;How do you build secure agentic commerce?&lt;/h2&gt;
&lt;p&gt;You need an intermediate control plane between the agent and Shopify:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;buyer
  ↓
AI agent
  ↓
MCP client
  ↓
intermediate MCP governance server
  ↓
UCP commerce tools
  ↓
Shopify storefront and checkout services
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That governance server is the whole point. It keeps the model off sensitive APIs and gives the team one place to enforce policy.&lt;/p&gt;
&lt;h3&gt;1. Expose task-specific tools&lt;/h3&gt;
&lt;p&gt;Don&apos;t expose a generic &lt;code&gt;shopify_admin_request&lt;/code&gt;. Create narrow tools with explicit inputs and outputs:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;search_products(query, filters)
get_variant(variant_id)
create_cart(lines, buyer_country)
update_cart(cart_id, lines)
create_checkout(cart_id)
get_order(order_id)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;One tool, one well-defined job, every parameter validated against a schema before any downstream request.&lt;/p&gt;
&lt;p&gt;Never accept arbitrary GraphQL from an agent. Use fixed queries and allowlisted fields. That prevents data overexposure and shrinks the blast radius of prompt injection.&lt;/p&gt;
&lt;h3&gt;2. Keep Admin tokens server-side&lt;/h3&gt;
&lt;p&gt;Admin API tokens belong in a secret manager. Never in:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Agent prompts&lt;/li&gt;
&lt;li&gt;Tool responses&lt;/li&gt;
&lt;li&gt;Browser JavaScript&lt;/li&gt;
&lt;li&gt;Client-side logs&lt;/li&gt;
&lt;li&gt;Model context&lt;/li&gt;
&lt;li&gt;&lt;code&gt;agents.md&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Product descriptions or metafields&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The governance server holds the credential and calls only the minimum Shopify operations required. For public product discovery, prefer Storefront API access with appropriate scopes. Reserve Admin API access for back-office operations that genuinely need it.&lt;/p&gt;
&lt;p&gt;Use separate credentials for:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Catalog reads&lt;/li&gt;
&lt;li&gt;Cart and checkout operations&lt;/li&gt;
&lt;li&gt;Order status reads&lt;/li&gt;
&lt;li&gt;Fulfillment or refund workflows&lt;/li&gt;
&lt;li&gt;Internal administrative actions&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Revocation and incident analysis get much easier when the blast radius is already partitioned.&lt;/p&gt;
&lt;h3&gt;3. Apply capability and state policies&lt;/h3&gt;
&lt;p&gt;Authorization depends on both the tool and the current commerce state.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Commerce state&lt;/th&gt;
&lt;th&gt;Allowed actions&lt;/th&gt;
&lt;th&gt;Approval requirement&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Discovery&lt;/td&gt;
&lt;td&gt;Search products, read public availability&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cart building&lt;/td&gt;
&lt;td&gt;Add or remove line items, estimate totals&lt;/td&gt;
&lt;td&gt;Optional&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pre-checkout&lt;/td&gt;
&lt;td&gt;Select fulfillment, validate buyer data&lt;/td&gt;
&lt;td&gt;Buyer confirmation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Checkout completion&lt;/td&gt;
&lt;td&gt;Submit payment mandate or complete checkout&lt;/td&gt;
&lt;td&gt;Strong authentication&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Post-purchase&lt;/td&gt;
&lt;td&gt;Read order status, track fulfillment&lt;/td&gt;
&lt;td&gt;Scoped buyer access&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;A signed agent is not automatically allowed to complete a purchase. High-risk actions need the right authorization tier, scope, buyer identity, and payment consent.&lt;/p&gt;
&lt;p&gt;Treat every checkout completion as a state transition, not an ordinary tool call.&lt;/p&gt;
&lt;h2&gt;Secure the protocol and the request envelope&lt;/h2&gt;
&lt;p&gt;The protocol has to authenticate more than the model. It authenticates the agent profile, request origin, capability set, and transaction context.&lt;/p&gt;
&lt;p&gt;UCP signs every HTTP-based transport with &lt;a href=&quot;https://www.rfc-editor.org/rfc/rfc9421.html&quot;&gt;RFC 9421 HTTP Message Signatures&lt;/a&gt;. Signing is asymmetric: ES256 is mandatory, ES384 optional. Public keys are JWKs (RFC 7517) published in the &lt;code&gt;signing_keys&lt;/code&gt; array of the signer&apos;s profile, and the body is bound in through a &lt;code&gt;Content-Digest&lt;/code&gt; header (RFC 9530) computed over the raw body bytes.&lt;/p&gt;
&lt;p&gt;A request signature covers &lt;code&gt;@method&lt;/code&gt;, &lt;code&gt;@authority&lt;/code&gt;, &lt;code&gt;@path&lt;/code&gt;, the query, &lt;code&gt;ucp-agent&lt;/code&gt;, &lt;code&gt;idempotency-key&lt;/code&gt;, and the content headers. Responses cover &lt;code&gt;@status&lt;/code&gt; instead of the method. That component list is the security property worth internalizing: the idempotency key is &lt;em&gt;inside&lt;/em&gt; the signature, so a replayed request can&apos;t be re-pointed at a new operation without invalidating it. UCP deliberately handles replay protection at the business layer through idempotency keys rather than at the signature layer — the RFC 9421 &lt;code&gt;created&lt;/code&gt; parameter is optional. If your governance server treats idempotency as an optional convenience, you&apos;ve removed the replay control.&lt;/p&gt;
&lt;p&gt;At the governance layer, verify:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The profile URL from &lt;code&gt;UCP-Agent&lt;/code&gt; uses HTTPS.&lt;/li&gt;
&lt;li&gt;The profile is valid JSON and within size limits.&lt;/li&gt;
&lt;li&gt;The declared UCP version is supported.&lt;/li&gt;
&lt;li&gt;Capabilities use compatible versions.&lt;/li&gt;
&lt;li&gt;Extension &lt;code&gt;extends&lt;/code&gt; dependencies are satisfied.&lt;/li&gt;
&lt;li&gt;The signing key resolves from the declared profile&apos;s &lt;code&gt;signing_keys&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The signature base reconstructs from the required components.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;Content-Digest&lt;/code&gt; matches the raw body you actually read.&lt;/li&gt;
&lt;li&gt;Idempotency keys are enforced, stored, and honored on retry.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For custom endpoints, webhooks, or internal proxy routes, HMAC adds a shared-secret check. HMAC does not replace UCP&apos;s asymmetric message-signature model — it&apos;s an extra control where both parties already share a secret.&lt;/p&gt;
&lt;p&gt;A webhook verifier should:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Read the raw request body.&lt;/li&gt;
&lt;li&gt;Compute the HMAC using the expected secret.&lt;/li&gt;
&lt;li&gt;Compare signatures with a constant-time function.&lt;/li&gt;
&lt;li&gt;Reject stale timestamps.&lt;/li&gt;
&lt;li&gt;Record the event ID to prevent replay.&lt;/li&gt;
&lt;li&gt;Process the event idempotently.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;In Laravel terms:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$expected = hash_hmac(
    &apos;sha256&apos;,
    $timestamp . &apos;.&apos; . $rawBody,
    $sharedSecret
);

if (!hash_equals($expected, $receivedSignature)) {
    abort(401, &apos;Invalid signature&apos;);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Never verify an HMAC over a parsed and re-serialized JSON object. Whitespace and key ordering change the bytes. Verify the original payload.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/secure-agentic-commerce-shopify/security-layers.webp&quot; alt=&quot;Security layers: scoped credentials, HMAC verification, signed requests, and a protected checkout&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Human approval is a protocol state&lt;/h2&gt;
&lt;p&gt;Autonomous doesn&apos;t mean every action is automatic.&lt;/p&gt;
&lt;p&gt;UCP models graceful escalation through checkout states such as:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;incomplete&lt;/code&gt; — required information is missing&lt;/li&gt;
&lt;li&gt;&lt;code&gt;requires_escalation&lt;/code&gt; — buyer input is needed&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ready_for_complete&lt;/code&gt; — everything is collected and the agent may finalize programmatically&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Those three sit alongside &lt;code&gt;complete_in_progress&lt;/code&gt;, &lt;code&gt;completed&lt;/code&gt;, and &lt;code&gt;canceled&lt;/code&gt; in the checkout state machine. A checkout can move back and forth between &lt;code&gt;incomplete&lt;/code&gt; and &lt;code&gt;requires_escalation&lt;/code&gt;, and it can reach &lt;code&gt;canceled&lt;/code&gt; from anywhere.&lt;/p&gt;
&lt;p&gt;An agent may select a product and calculate shipping but still need buyer input for a 3DS challenge, address validation, terms acceptance, age verification, or custom selling terms.&lt;/p&gt;
&lt;p&gt;Escalation isn&apos;t an error path. A &lt;code&gt;requires_escalation&lt;/code&gt; response must carry a &lt;code&gt;continue_url&lt;/code&gt; and at least one message at escalation severity — that&apos;s the merchant handing you a place to send the buyer, plus the reason. Send them there with the cart intact instead of starting over.&lt;/p&gt;
&lt;p&gt;Your agent should not try to route around this state. It should surface the escalation messages verbatim and preserve the exact checkout context the merchant returned. The same applies to compliance disclosures: a &lt;code&gt;warning&lt;/code&gt; message with &lt;code&gt;presentation: &quot;disclosure&quot;&lt;/code&gt; — Prop 65, allergens, age restrictions — must be displayed next to the item it applies to, never collapsed or auto-dismissed. If your surface can&apos;t render it faithfully, escalate to &lt;code&gt;continue_url&lt;/code&gt; rather than silently downgrading.&lt;/p&gt;
&lt;p&gt;Add approval gates for:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Checkout completion&lt;/li&gt;
&lt;li&gt;Orders above a configured threshold&lt;/li&gt;
&lt;li&gt;Address changes&lt;/li&gt;
&lt;li&gt;Discounts outside policy&lt;/li&gt;
&lt;li&gt;Refunds and cancellations&lt;/li&gt;
&lt;li&gt;Regulated or age-restricted products&lt;/li&gt;
&lt;li&gt;Unusual quantity or velocity patterns&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Use idempotency keys on every money-moving request. A retry must not create a second checkout or a duplicate order.&lt;/p&gt;
&lt;h2&gt;Use &lt;a href=&quot;http://agents.md&quot;&gt;agents.md&lt;/a&gt; for discovery and operational policy&lt;/h2&gt;
&lt;p&gt;An &lt;code&gt;agents.md&lt;/code&gt; file helps compatible tools discover how your application expects agents to behave. Treat it as documentation and policy guidance — never as proof of identity or authorization.&lt;/p&gt;
&lt;p&gt;A useful file describes safe defaults, tool boundaries, and failure handling:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# Commerce agent guidance

## Safe defaults

- Use catalog tools for product discovery.
- Never request or expose Shopify Admin tokens.
- Ask for buyer confirmation before checkout completion.
- Respect `requires_escalation` responses.

## Tool boundaries

- Do not call arbitrary GraphQL.
- Do not infer inventory when the API returns unknown.
- Do not modify orders without an authenticated buyer session.

## Failure handling

- Retry read operations with backoff.
- Use idempotency keys for writes.
- Log tool name, request ID, policy decision, and result status.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Keep it version-controlled and review changes like code. Sign or hash the policy bundle if your architecture supports it. A compromised instruction file must never grant permissions the server itself doesn&apos;t enforce.&lt;/p&gt;
&lt;p&gt;For more on MCP tool boundaries and context control, see &lt;a href=&quot;https://ansezz.com/blog/claude-mcp-dev-tools/&quot;&gt;Claude MCP and real-time developer tools&lt;/a&gt;. The same least-privilege rules apply to commerce agents, and the &lt;a href=&quot;https://ansezz.com/blog/agentic-workflows-vibe-coding/&quot;&gt;agentic workflow architecture guide&lt;/a&gt; covers human approval and stateful tool loops.&lt;/p&gt;
&lt;h2&gt;Technical takeaways&lt;/h2&gt;
&lt;p&gt;Secure agentic commerce is an API and policy engineering problem, not a prompt design problem. Launch checklist:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Publish and validate UCP profiles over HTTPS.&lt;/li&gt;
&lt;li&gt;Pin supported protocol and capability versions.&lt;/li&gt;
&lt;li&gt;Put an intermediate MCP governance server between agents and Shopify.&lt;/li&gt;
&lt;li&gt;Expose narrow, schema-validated tools instead of arbitrary API access.&lt;/li&gt;
&lt;li&gt;Keep Admin tokens in a server-side secret manager.&lt;/li&gt;
&lt;li&gt;Use scoped credentials for catalog, checkout, orders, and administration.&lt;/li&gt;
&lt;li&gt;Verify HTTP Message Signatures and rotate keys.&lt;/li&gt;
&lt;li&gt;Add HMAC verification to custom webhooks and proxy endpoints.&lt;/li&gt;
&lt;li&gt;Require idempotency for all writes and payment-related operations.&lt;/li&gt;
&lt;li&gt;Model checkout as explicit states with human escalation.&lt;/li&gt;
&lt;li&gt;Treat &lt;code&gt;agents.md&lt;/code&gt; as guidance, never as authorization.&lt;/li&gt;
&lt;li&gt;Log every tool call, policy decision, signature result, and transaction identifier.&lt;/li&gt;
&lt;li&gt;Test malformed profiles, replay attempts, prompt injection, stale inventory, duplicate retries, and unauthorized checkout attempts.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The opportunity in agentic commerce is large, but trust decides which implementations survive production. What authorization boundary stops your Shopify agent from turning a reasonable buyer request into an unauthorized transaction? If you want a second pair of eyes on that boundary, &lt;a href=&quot;https://ansezz.com/services/&quot;&gt;here&apos;s how I work with teams&lt;/a&gt;.&lt;/p&gt;
</content:encoded><category>shopify</category><category>shopify</category><category>agentic-commerce</category><category>mcp</category><category>security</category><category>ai</category><category>architecture</category></item><item><title>MCP vs A2A vs ACP: the agent protocol stack</title><link>https://ansezz.com/blog/mcp-vs-a2a-vs-acp/</link><guid isPermaLink="true">https://ansezz.com/blog/mcp-vs-a2a-vs-acp/</guid><description>MCP connects agents to tools, A2A connects agents to each other, and ACP has folded into A2A. How the two-layer agent protocol stack fits together in 2026.</description><pubDate>Wed, 05 Aug 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;You are ready to move past single-prompt chatbots and build a real multi-agent system. Then you look at what you actually have: agents that cannot reach your Postgres database without a bespoke script, and cannot coordinate with the agent another team shipped last quarter without a shared Slack channel and a human in the middle.&lt;/p&gt;
&lt;p&gt;The missing piece is not model quality. It is a communication standard. Without one, you spend most of your engineering time writing glue code for integrations and very little on the behavior that makes the agents worth running. Every schema change breaks a hand-rolled adapter somewhere.&lt;/p&gt;
&lt;p&gt;The good news is that the landscape has converged. Two protocols now cover the two axes that matter: the Model Context Protocol (MCP) for connecting agents to tools and data, and Agent2Agent (A2A) for connecting agents to each other. The Agent Communication Protocol (ACP), the third name you will still see in older posts, has been folded into A2A. This post breaks down what each layer does and how to wire them together.&lt;/p&gt;
&lt;h2&gt;The current state of agent protocols&lt;/h2&gt;
&lt;p&gt;In early 2025 you had to pick a vendor SDK and live with it. Function calling looked different on every model, and agent frameworks each invented their own message envelope. Two things changed that.&lt;/p&gt;
&lt;p&gt;First, Anthropic donated MCP to the Linux Foundation, where it now sits under the Agentic AI Foundation (AAIF) alongside other agentic projects. Second, Google donated A2A to the Linux Foundation in mid-2025. Both protocols are now governed in the open by the same umbrella, which is the main reason the stack finally stopped moving under everyone&apos;s feet.&lt;/p&gt;
&lt;p&gt;The second shift is architectural. You no longer need one protocol that does everything. You use a &lt;strong&gt;vertical&lt;/strong&gt; protocol for reaching data and tools, and a &lt;strong&gt;horizontal&lt;/strong&gt; protocol for agents talking to peers.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Protocol&lt;/th&gt;
&lt;th&gt;Primary role&lt;/th&gt;
&lt;th&gt;Governance&lt;/th&gt;
&lt;th&gt;Status&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;MCP&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Agent to tool/data&lt;/td&gt;
&lt;td&gt;Linux Foundation, AAIF&lt;/td&gt;
&lt;td&gt;De-facto standard&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;A2A&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Agent to agent&lt;/td&gt;
&lt;td&gt;Linux Foundation&lt;/td&gt;
&lt;td&gt;Stable, actively spec&apos;d&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;ACP&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Agent messaging&lt;/td&gt;
&lt;td&gt;Legacy (IBM / BeeAI)&lt;/td&gt;
&lt;td&gt;Merged into A2A&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;MCP: the universal adapter for tools&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/mcp-vs-a2a-vs-acp/mcp-tool-layer.webp&quot; alt=&quot;Illustration of MCP acting as a universal adapter between an agent and many tools&quot; /&gt;&lt;/p&gt;
&lt;p&gt;MCP is the vertical layer. It standardizes how an agent connects to everything that is &lt;em&gt;not&lt;/em&gt; another agent: your Shopify store, your Postgres instance, a filesystem, an internal REST service.&lt;/p&gt;
&lt;p&gt;Before MCP, giving a model access to a filesystem meant writing a tool definition shaped for that specific model&apos;s function-calling format. With MCP you write one server. Any MCP-compatible client — Claude, an IDE assistant, a local model runner — connects and discovers the same capabilities without you rewriting anything.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Why MCP won the tool layer:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;JSON-RPC 2.0 on the wire.&lt;/strong&gt; Boring, well-understood, easy to debug.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Transport flexibility.&lt;/strong&gt; &lt;code&gt;stdio&lt;/code&gt; for local servers, Streamable HTTP for remote ones. (The older HTTP+SSE transport is legacy — new remote servers should use Streamable HTTP.)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Three primitives, not thirty.&lt;/strong&gt; Servers expose tools the model can call, resources it can read, and prompts the user can invoke. Clients contribute sampling and roots, and a 2025 revision added elicitation so a server can ask the user for missing input mid-call.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Real ecosystem.&lt;/strong&gt; Thousands of public servers exist for common SaaS platforms, and the official MCP Registry gives clients a discovery path instead of a README scavenger hunt.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Scoped permissions.&lt;/strong&gt; Servers expose only what you register, so an agent sees the slice of data it needs and nothing else.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you are modernizing how your systems are consumed, exposing your data through MCP is the highest-leverage first move. It decouples your tools from your model choice, which is the coupling that hurts most when you swap models. I went deeper on the mechanics in &lt;a href=&quot;https://ansezz.com/blog/api-vs-mcp/&quot;&gt;API vs MCP&lt;/a&gt; and &lt;a href=&quot;https://ansezz.com/blog/mcp-context-aware-agents/&quot;&gt;MCP tool-use for context-aware agents&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;A2A: the social fabric for agents&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/mcp-vs-a2a-vs-acp/a2a-agent-mesh.webp&quot; alt=&quot;Illustration of multiple agents discovering and delegating tasks to each other&quot; /&gt;&lt;/p&gt;
&lt;p&gt;MCP handles tools. A2A handles peers. When your merchandising agent needs stock levels from an inventory agent owned by another team — one built on a different framework, deployed in a different cluster — that conversation is A2A. This is the horizontal layer.&lt;/p&gt;
&lt;p&gt;A2A lets agents discover each other, delegate tasks, stream progress, and hand back results. The anchor of the protocol is the &lt;strong&gt;Agent Card&lt;/strong&gt;: a JSON document, conventionally served at &lt;code&gt;/.well-known/agent-card.json&lt;/code&gt;, that declares who the agent is, which skills it offers, which transports and auth schemes it supports, and what input and output types it accepts. Discovery is fetching that file.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;What A2A gives you:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Capability discovery.&lt;/strong&gt; A client reads an Agent Card (or queries a registry) to find an agent that handles the job, instead of hard-coding a hostname.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Task delegation with a real lifecycle.&lt;/strong&gt; Tasks move through explicit states — submitted, working, input-required, completed, failed, canceled — so a caller can reason about a long-running job rather than polling a black box.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Streaming and callbacks.&lt;/strong&gt; Server-Sent Events for live progress on an open connection, plus webhook-style push notifications for tasks that outlive it.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Framework independence.&lt;/strong&gt; The peer on the other side can be LangGraph, CrewAI, a Laravel service wrapping a model, or something homegrown. The wire format is what you agree on, not the runtime.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Standard auth.&lt;/strong&gt; Agent Cards declare security schemes, so you authenticate agent-to-agent traffic with the same primitives you already use for APIs.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This is what makes cross-team agents workable. Building &lt;a href=&quot;https://ansezz.com/blog/agentic-commerce-shopify/&quot;&gt;agentic commerce on Shopify&lt;/a&gt; already means a checkout flow handing structured instructions to fulfillment logic; A2A is what stops that handoff from being a private contract nobody else can reuse.&lt;/p&gt;
&lt;h2&gt;Why ACP merged into A2A&lt;/h2&gt;
&lt;p&gt;You will still hit references to the Agent Communication Protocol. ACP came out of IBM and the BeeAI team as a REST-native approach to agent messaging, and it was good at exactly the thing A2A was weakest at early on: stateful, long-running, asynchronous work.&lt;/p&gt;
&lt;p&gt;The problem was arithmetic. Two competing agent-to-agent protocols meant every framework author had to pick one or implement both, which is how ecosystems stall. During 2025 the ACP maintainers moved to consolidate behind A2A under Linux Foundation governance.&lt;/p&gt;
&lt;p&gt;The practical consequence is short. ACP is no longer an architectural choice you need to evaluate — its strong ideas around state management and durable messaging live on in A2A&apos;s task model. If you had ACP on a roadmap, substitute A2A. The concepts map closely and the community is now in one place.&lt;/p&gt;
&lt;h2&gt;Architecting the two-layer stack&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/mcp-vs-a2a-vs-acp/two-layer-stack.webp&quot; alt=&quot;Diagram of a two-layer agent architecture with MCP underneath and A2A across the top&quot; /&gt;&lt;/p&gt;
&lt;p&gt;The mental model that keeps this straight: MCP is the device driver, A2A is the network protocol. One lets a process talk to the hardware it is attached to; the other lets it talk to other machines. You need both, and they do not overlap.&lt;/p&gt;
&lt;p&gt;An agent is typically an A2A &lt;strong&gt;server&lt;/strong&gt; to its peers and an MCP &lt;strong&gt;client&lt;/strong&gt; to its tools, at the same time. That dual role is the whole architecture.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;A workable implementation order:&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Data layer (MCP).&lt;/strong&gt; Wrap your internal APIs and databases in MCP servers. This is the step that pays off immediately, even with a single agent, and it is where a lot of &lt;a href=&quot;https://ansezz.com/blog/claude-mcp-dev-tools/&quot;&gt;Claude and MCP dev tooling&lt;/a&gt; already lives.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Coordination layer (A2A).&lt;/strong&gt; Put an A2A-compliant interface in front of each agent and publish its Agent Card. This is how &lt;a href=&quot;https://ansezz.com/blog/agentic-workflows-vibe-coding/&quot;&gt;informal agentic workflows&lt;/a&gt; graduate into services other teams can call.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Governance layer.&lt;/strong&gt; Route the traffic through an &lt;a href=&quot;https://ansezz.com/blog/api-gateway-ai-stack/&quot;&gt;AI gateway&lt;/a&gt; so you get auth, rate limits, cost attribution, and traces on both protocols instead of guessing where the spend went.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;One caution: a tool call and an agent delegation are not interchangeable, even when both are technically reachable. Exposing another team&apos;s agent as an MCP tool flattens away the task lifecycle and turns a long-running negotiation into a request that either returns or times out. Use MCP for deterministic capabilities you own, and A2A when the other side needs to reason, ask you a follow-up question, or take twenty minutes.&lt;/p&gt;
&lt;h2&gt;Practical implementation tips&lt;/h2&gt;
&lt;p&gt;Moving to a protocol-first architecture is mostly a change in what you consider the unit of work. Stop shipping &quot;an agent&quot; and start shipping services that speak these standards.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Audit your tools first.&lt;/strong&gt; Pick the five tools your agents actually reach for and build or adopt MCP servers for them. Resist wrapping everything — an agent handed forty tools makes worse choices than one handed six.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Write Agent Cards early.&lt;/strong&gt; Even with two agents, defining the card forces you to name each agent&apos;s skills and boundaries. Most multi-agent designs fail because responsibilities overlap, and the card is where that shows up before it becomes a runtime problem.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Standardize transports.&lt;/strong&gt; Streamable HTTP for remote MCP servers, HTTP with SSE for A2A streaming. Streaming matters more than it looks: without it you cannot see intermediate reasoning or partial tool output, and debugging a multi-agent failure becomes archaeology.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Version the contracts, not the prompts.&lt;/strong&gt; Agent Cards and tool schemas are public interfaces now. Treat a changed tool signature like a breaking API change, because for every agent calling it, that is exactly what it is.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Instrument at the boundary.&lt;/strong&gt; Every MCP call and A2A task should emit a trace with a correlation ID that survives delegation. When a five-agent chain returns nonsense, the only cheap way to find the bad hop is a trace that spans all of them.&lt;/p&gt;
&lt;h2&gt;Takeaways&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;MCP is vertical.&lt;/strong&gt; It connects agents to tools, databases, and APIs, and it is the de-facto standard for that layer.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;A2A is horizontal.&lt;/strong&gt; It handles discovery, delegation, and task lifecycle between agents, across frameworks and teams.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;ACP is legacy.&lt;/strong&gt; It merged into A2A. Do not start new work on standalone ACP.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Governance is settled.&lt;/strong&gt; Both protocols now sit under Linux Foundation stewardship, which is what makes them safe to build against.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Each agent plays two roles.&lt;/strong&gt; A2A server to its peers, MCP client to its tools — that dual role is the stack in one sentence.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The boundary matters.&lt;/strong&gt; Deterministic capability you own is a tool; another team&apos;s reasoning system is a peer. Mixing them up is how the architecture rots.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;How are you handling coordination between agent frameworks in your production stack right now? If you are wiring MCP and A2A into a real product, &lt;a href=&quot;https://ansezz.com/services/&quot;&gt;here&apos;s how I help teams ship it&lt;/a&gt;.&lt;/p&gt;
</content:encoded><category>architecture</category><category>mcp</category><category>agentic-ai</category><category>ai</category><category>architecture</category></item><item><title>Coolify in 2026: why 60k developers chose self-hosted PaaS</title><link>https://ansezz.com/blog/coolify-2026-self-hosted-paas/</link><guid isPermaLink="true">https://ansezz.com/blog/coolify-2026-self-hosted-paas/</guid><description>Coolify crossed 60k GitHub stars. What that buys you, how it really compares to Dokploy, CapRover and Dokku, and the firewall claim to ignore.</description><pubDate>Mon, 03 Aug 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Scaling a web app used to mean signing a blank check. You start on the free tier, traffic spikes, and suddenly the bill has a comma in it. The alternative — a raw VPS, SSH, and a prayer — costs you the weekend instead of the money. Nginx vhosts, certbot renewals that silently fail, a deploy script nobody wants to touch.&lt;/p&gt;
&lt;p&gt;That gap is the whole reason Coolify exists, and it&apos;s why the project has quietly turned into the default answer for &quot;I want Heroku, but on my server.&quot;&lt;/p&gt;
&lt;p&gt;As of today the repo sits at &lt;strong&gt;60,298 GitHub stars&lt;/strong&gt; and ships hundreds of one-click services. Version 4 has been the stable line for a while now. It is no longer a hobby project you&apos;re brave for running — it&apos;s infrastructure with a bus factor above one.&lt;/p&gt;
&lt;p&gt;I run production workloads on it. This post is what I&apos;d tell you over coffee: what the numbers mean, where Coolify genuinely wins, and which of its marketing-adjacent claims you should quietly ignore.&lt;/p&gt;
&lt;h2&gt;What 60k stars actually signals&lt;/h2&gt;
&lt;p&gt;Stars are a vanity metric right up until they aren&apos;t. What the curve tells you here is not &quot;Coolify is good software&quot; — it&apos;s that a critical mass of developers decided &lt;strong&gt;sovereign infrastructure&lt;/strong&gt; was worth the tradeoff.&lt;/p&gt;
&lt;p&gt;The reasoning is boring and correct. A managed PaaS owns your build pipeline, your runtime, your egress pricing, and your migration path. When any one of those changes, you refactor on their timeline. Heroku killing free dynos taught a generation of developers that &quot;managed&quot; means &quot;someone else decides.&quot;&lt;/p&gt;
&lt;p&gt;Self-hosting used to mean giving up the good parts — git-push deploys, automatic TLS, a dashboard your teammate can read. Coolify&apos;s actual contribution is that it kept those and dropped the rent.&lt;/p&gt;
&lt;p&gt;Practically, a mature project means the things that bite you at 2am are handled by someone other than you: proxy config, certificate renewal, container lifecycle, backup scheduling. That&apos;s the product.&lt;/p&gt;
&lt;h2&gt;Docker containerization is the whole engine&lt;/h2&gt;
&lt;p&gt;Underneath the dashboard, Coolify is an orchestrator for Docker. Every app, database, and service you deploy is a container. Your local environment and your production server run the same image, which is the entire point.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/coolify-2026-self-hosted-paas/docker-containerization.webp&quot; alt=&quot;Docker architecture and containerization across a self-hosted PaaS&quot; /&gt;&lt;/p&gt;
&lt;p&gt;When you push to your repo, Coolify pulls the code, builds an image, and starts the replacement container. Build sources are flexible: a Dockerfile if you have one, a &lt;code&gt;docker-compose.yaml&lt;/code&gt; for multi-service stacks, static output for a plain frontend, or &lt;strong&gt;Nixpacks&lt;/strong&gt; when you want zero config and can live with its opinions.&lt;/p&gt;
&lt;p&gt;What containerization buys you here:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Isolation.&lt;/strong&gt; One app OOM-ing doesn&apos;t take the box down with it.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Portability.&lt;/strong&gt; Moving from the cheapest droplet your provider sells to a dedicated Hetzner box is a server swap, not a migration project.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Resource control.&lt;/strong&gt; Per-container CPU and memory limits, set in the UI, enforced by Docker.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Reproducibility.&lt;/strong&gt; The image that passed staging is the image that runs in production.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you&apos;re weighing this against a full orchestrator, my breakdown of &lt;a href=&quot;https://ansezz.com/blog/docker-vs-kubernetes/&quot;&gt;Docker vs Kubernetes&lt;/a&gt; covers where the line actually sits — and it&apos;s further out than most teams assume. Kubernetes earns its complexity somewhere north of &quot;a dozen services and a platform team.&quot; Below that, Coolify&apos;s model is strictly less work for the same outcome.&lt;/p&gt;
&lt;p&gt;For the ground-floor version of this setup, I wrote it up in &lt;a href=&quot;https://ansezz.com/blog/coolify-docker-saas-hosting/&quot;&gt;Coolify and Docker for SaaS hosting&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;What the one-click service catalog actually contains&lt;/h2&gt;
&lt;p&gt;This is the part people underrate. The service catalog is not a gimmick — it&apos;s the difference between &quot;I&apos;ll set up Plausible this weekend&quot; and &quot;Plausible is running, next task.&quot;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/coolify-2026-self-hosted-paas/one-click-services.webp&quot; alt=&quot;Bento grid of one-click services available in Coolify&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Worth splitting into two things that get conflated. &lt;strong&gt;Databases are first-class resources&lt;/strong&gt;, not templates — eight of them: PostgreSQL, MySQL, MariaDB, MongoDB, Redis, DragonFly, KeyDB, and ClickHouse. They get their own connection UI, their own lifecycle, and — for the SQL and document stores — their own backup scheduling.&lt;/p&gt;
&lt;p&gt;Everything else is a compose template, and that&apos;s where the breadth lives:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Dev tools&lt;/strong&gt;: Gitea, Hoppscotch, n8n, Uptime Kuma.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Analytics&lt;/strong&gt;: Plausible, Umami, PostHog.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;CMS and app platforms&lt;/strong&gt;: Ghost, Strapi, Directus, WordPress, Appwrite, Supabase.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;AI infrastructure&lt;/strong&gt;: Qdrant and other vector stores for RAG workloads.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Each template ships with sane defaults, networking, volume mounts, and generated secrets. You get a running service and a URL.&lt;/p&gt;
&lt;p&gt;The &quot;280+&quot; figure is Coolify&apos;s own tagline and it undersells the current state — the repo&apos;s &lt;code&gt;templates/compose&lt;/code&gt; directory holds 362 of them today. Worth knowing, though, that catalog size is no longer a moat: Dokploy ships 514 blueprints and CapRover has 354 one-click apps. Everyone in this space has enough templates.&lt;/p&gt;
&lt;p&gt;Two caveats worth stating plainly. First, a one-click template is a &lt;em&gt;starting point&lt;/em&gt; — it is not tuned for your load, and the defaults are chosen to boot cleanly, not to survive traffic. Second, templates are community-maintained; pin your image tags rather than riding &lt;code&gt;latest&lt;/code&gt; into a breaking upgrade.&lt;/p&gt;
&lt;h2&gt;Coolify vs the alternatives, on real numbers&lt;/h2&gt;
&lt;p&gt;The self-hosted PaaS space stopped being a one-horse race. Here&apos;s where the four main projects actually stand today:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Coolify&lt;/th&gt;
&lt;th&gt;Dokploy&lt;/th&gt;
&lt;th&gt;CapRover&lt;/th&gt;
&lt;th&gt;Dokku&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Interface&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Web dashboard&lt;/td&gt;
&lt;td&gt;Web dashboard&lt;/td&gt;
&lt;td&gt;Web UI&lt;/td&gt;
&lt;td&gt;CLI-first&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Multi-server&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Native + Swarm&lt;/td&gt;
&lt;td&gt;Native + Swarm&lt;/td&gt;
&lt;td&gt;Docker Swarm&lt;/td&gt;
&lt;td&gt;Via plugins&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Service catalog&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;362 templates&lt;/td&gt;
&lt;td&gt;514 blueprints&lt;/td&gt;
&lt;td&gt;354 one-click apps&lt;/td&gt;
&lt;td&gt;Plugin-based&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Build sources&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Dockerfile / Compose / Nixpacks / static&lt;/td&gt;
&lt;td&gt;Dockerfile / Compose / Nixpacks / Railpack / Heroku + Paketo buildpacks&lt;/td&gt;
&lt;td&gt;Dockerfile / captain-definition&lt;/td&gt;
&lt;td&gt;Herokuish buildpacks / Dockerfile&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Backups&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;S3-compatible, scheduled&lt;/td&gt;
&lt;td&gt;S3-compatible&lt;/td&gt;
&lt;td&gt;Manual / scripted&lt;/td&gt;
&lt;td&gt;Plugin-based&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;GitHub stars&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;60.3k&lt;/td&gt;
&lt;td&gt;36.5k&lt;/td&gt;
&lt;td&gt;15.1k&lt;/td&gt;
&lt;td&gt;32.1k&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;A few honest reads on that table.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Dokploy&lt;/strong&gt; is the real competitor now, not a footnote — 36k stars and climbing fast, with a template catalog that has quietly grown &lt;em&gt;larger&lt;/em&gt; than Coolify&apos;s and a wider set of build sources (Railpack and Paketo on top of the usual Nixpacks and Dockerfile). It&apos;s leaner and the UI is arguably cleaner. If you&apos;re starting fresh in 2026 it deserves a genuine evaluation rather than a dismissal. Coolify&apos;s remaining edge is time in production: more people have hit more edge cases, and those fixes are already merged.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Dokku&lt;/strong&gt; has the smallest resource footprint of the four and the most Unix-native design. If you&apos;re comfortable in a terminal and want something that will still work identically in five years, it&apos;s a defensible choice. The CLI-first model is a real barrier for teams where not everyone deploys.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;CapRover&lt;/strong&gt; is the oldest of the four and still actively maintained — it committed as recently as this week. It&apos;s Swarm-native by design, which is a genuine advantage if clustering is your starting requirement rather than an afterthought. Its ceiling is the smaller community: fewer people hitting problems means fewer problems already solved for you.&lt;/p&gt;
&lt;h2&gt;What multi-server actually means&lt;/h2&gt;
&lt;p&gt;Coolify supports three topologies: a single server, multiple standalone servers managed from one dashboard, and Docker Swarm clusters.&lt;/p&gt;
&lt;p&gt;The middle one is what most teams want, and it&apos;s worth being precise about what it is. Adding a second server means Coolify SSHes into it and manages Docker there. Each server runs its own containers and its own proxy. It is &lt;strong&gt;not&lt;/strong&gt; a cluster — there&apos;s no automatic failover, no scheduler moving workloads between nodes. If a server dies, the apps on it are down until you redeploy elsewhere.&lt;/p&gt;
&lt;p&gt;That&apos;s a feature, not a gap. It&apos;s the model that fits the 95% case: put the database on a high-memory box, the app on a high-CPU box, keep the Coolify instance itself on a small separate server so a runaway build can&apos;t take down your control plane. If you need true clustering, that&apos;s what the Swarm mode is for — and if you need more than Swarm, you&apos;re in Kubernetes territory.&lt;/p&gt;
&lt;p&gt;I go deeper on the topology decisions, dedicated build servers, and zero-downtime rollouts in &lt;a href=&quot;https://ansezz.com/blog/scaling-with-coolify/&quot;&gt;scaling SaaS with Coolify&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Security: what&apos;s real and what isn&apos;t&lt;/h2&gt;
&lt;p&gt;This is where I&apos;ll break with the usual Coolify writeup, because there&apos;s a claim floating around that Coolify handles your firewall for you. It does not, and believing it will hurt you.&lt;/p&gt;
&lt;p&gt;Here&apos;s what Coolify actually gives you:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Automatic TLS.&lt;/strong&gt; Let&apos;s Encrypt issuance and renewal for custom domains, handled by the bundled proxy — Traefik by default, Caddy still marked experimental.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Encrypted secrets.&lt;/strong&gt; Environment variable values are stored with Laravel&apos;s &lt;code&gt;encrypted&lt;/code&gt; cast, so they&apos;re not sitting in the database as plaintext. Scopes go per-resource, with shared variables at the environment, project, and team level, and separate build-time and runtime visibility.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Team roles.&lt;/strong&gt; &lt;code&gt;owner&lt;/code&gt;, &lt;code&gt;admin&lt;/code&gt;, and &lt;code&gt;member&lt;/code&gt; per team, so a contractor doesn&apos;t get production database access.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Scheduled backups.&lt;/strong&gt; Postgres gets first-class scheduled dumps (Coolify backs up its own database the same way); MySQL, MariaDB, and MongoDB are covered too. Destination is local disk or any S3-compatible bucket — Backblaze B2 and Cloudflare R2 both work and cost almost nothing.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And here&apos;s the part nobody says out loud: &lt;strong&gt;Docker publishes ports by writing NAT rules that bypass UFW.&lt;/strong&gt; If you &lt;code&gt;ufw deny 5432&lt;/code&gt; and then map Postgres to the host, that port is open to the internet and &lt;code&gt;ufw status&lt;/code&gt; will happily tell you it&apos;s blocked. This is Docker behavior, not a Coolify bug, but the consequence lands on you either way.&lt;/p&gt;
&lt;p&gt;The fix is to filter at a layer Docker can&apos;t route around:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Use your provider&apos;s firewall.&lt;/strong&gt; Hetzner Cloud Firewall, DigitalOcean Cloud Firewall, AWS security groups — these sit outside the host, so Docker&apos;s iptables rules are irrelevant.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Don&apos;t publish database ports at all.&lt;/strong&gt; Databases should be reachable over the internal Docker network by container name. Coolify defaults to this; the risk is you enabling public access &quot;just for a migration&quot; and forgetting.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Close the dashboard ports.&lt;/strong&gt; Coolify needs 8000, 6001, and 6002 during setup. Once you&apos;ve put the dashboard behind a domain and its own TLS, those can be closed. Leave 22, 80, and 443.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Do those three and your self-hosted setup is meaningfully harder to reach than most managed deployments, because there&apos;s less surface exposed to begin with.&lt;/p&gt;
&lt;h2&gt;The actual argument: vendor independence&lt;/h2&gt;
&lt;p&gt;Strip away the features and the case for Coolify is one sentence: you stop renting your deployment pipeline.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/coolify-2026-self-hosted-paas/vendor-independence.webp&quot; alt=&quot;Illustration of migrating off a managed cloud provider onto owned infrastructure&quot; /&gt;&lt;/p&gt;
&lt;p&gt;The math is not subtle. Ten small projects on a managed platform — a few hobby-tier databases, some build minutes, a bit of bandwidth — lands somewhere around $150–250/month once you&apos;re past free tiers. The same ten projects fit on a single mid-tier VPS with 8 vCPU and 16 GB of RAM, with headroom to spare. On Hetzner that&apos;s a low-double-digit euro bill on the ARM line, roughly double on x86. Either way it&apos;s an order of magnitude, not a percentage.&lt;/p&gt;
&lt;p&gt;The catch, stated fairly: you are now the on-call engineer. Kernel updates, disk space, a container that wedges at 3am — that&apos;s yours. Coolify reduces that work by maybe 90%, not 100%. For a solo developer or a small team, the trade is usually worth it. For a company where an hour of downtime costs more than a year of managed hosting, it isn&apos;t, and you should pay the tax.&lt;/p&gt;
&lt;p&gt;I made this exact switch and wrote up the numbers in &lt;a href=&quot;https://ansezz.com/blog/coolify-self-hosted-saas/&quot;&gt;why I&apos;m ditching expensive cloud providers&lt;/a&gt;. The runway extension was the point; the control turned out to matter more.&lt;/p&gt;
&lt;h2&gt;How I&apos;d approach the migration&lt;/h2&gt;
&lt;p&gt;If you&apos;re considering the move, the order matters more than the tooling:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Price it honestly.&lt;/strong&gt; Add up your current managed spend including bandwidth and build minutes, then compare against a VPS with 2x the resources you think you need. If the gap isn&apos;t at least 3x, the operational burden probably isn&apos;t worth it.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Start with something you can afford to break.&lt;/strong&gt; Staging environments, internal dashboards, side projects. Learn the failure modes on workloads where the blast radius is you.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Get containerized first.&lt;/strong&gt; If your app already builds from a Dockerfile, migration is an afternoon. If it doesn&apos;t, fix that &lt;em&gt;before&lt;/em&gt; you touch Coolify — you&apos;ll be debugging one thing instead of two.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Wire up S3 backups on day one.&lt;/strong&gt; Before the first production workload lands. A single server with no offsite backup is a countdown, not an architecture.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Put the control plane on its own box.&lt;/strong&gt; Coolify&apos;s documented minimum is 2 cores, 2 GB of RAM, and 30 GB of disk — the cheapest tier most providers sell. A dedicated instance means a runaway build can never take down the dashboard you&apos;d use to fix it.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Add external monitoring.&lt;/strong&gt; Coolify tells you a container is running. It does not tell you your users can check out. Uptime Kuma (one-click, naturally) or Better Stack closes that gap.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Coolify&apos;s real achievement is that it made the PaaS experience a commodity. The dashboard, the git integration, the automatic TLS — these were competitive moats five years ago. Now they&apos;re a &lt;code&gt;curl | bash&lt;/code&gt; away, and the only thing you&apos;re paying a managed provider for is not thinking about servers.&lt;/p&gt;
&lt;p&gt;That&apos;s a legitimate thing to pay for. It just isn&apos;t worth a 10x premium anymore.&lt;/p&gt;
&lt;p&gt;What&apos;s still keeping you on managed hosting — the operational load, or the fear of being the one holding the pager? &lt;a href=&quot;https://ansezz.com/contact/&quot;&gt;Tell me&lt;/a&gt;, I&apos;m genuinely curious which one it is.&lt;/p&gt;
</content:encoded><category>devops</category><category>coolify</category><category>self-hosting</category><category>devops</category><category>docker</category><category>deployment</category></item><item><title>AI coding is like an addiction (in the best way)</title><link>https://ansezz.com/blog/ai-coding-workflow-levels/</link><guid isPermaLink="true">https://ansezz.com/blog/ai-coding-workflow-levels/</guid><description>A level-by-level map of the AI coding workflow: gateway prompts, codebase RAG, then multi-agent orchestration over MCP — and why manual coding now drags.</description><pubDate>Sat, 01 Aug 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;AI coding is strangely addictive. You start with a single prompt to fix a regex or center a div. You tell yourself it&apos;s a one-time thing to save ten minutes. Then you watch it refactor an entire &lt;strong&gt;Laravel&lt;/strong&gt; controller in seconds. Suddenly you&apos;re asking it to write your unit tests. Before long you&apos;re building custom agents that manage a &lt;strong&gt;Shopify&lt;/strong&gt; store over &lt;strong&gt;GraphQL&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;The shift happens slowly at first. Then all at once.&lt;/p&gt;
&lt;p&gt;The problem it solves for most of us isn&apos;t typing speed — it&apos;s blank-page fatigue. Staring at a legacy codebase or a fresh architecture doc feels like a mountain of manual labor. You burn hours on boilerplate instead of business logic. That friction is where motivation goes to die.&lt;/p&gt;
&lt;p&gt;And the cost of not evolving compounds. Coding without AI in 2026 feels like writing a book with a quill while everyone else runs a word processor. It isn&apos;t only speed — it&apos;s &lt;strong&gt;cognitive load&lt;/strong&gt;. Energy spent on syntax and API nuance is energy not spent on architecture and strategy.&lt;/p&gt;
&lt;p&gt;The answer isn&apos;t &quot;use AI.&quot; It&apos;s building a structured &lt;strong&gt;AI coding workflow&lt;/strong&gt; — moving from passive user to engineer who orchestrates systems. Once you&apos;ve felt a multi-agent loop close on its own, there&apos;s no going back.&lt;/p&gt;
&lt;h2&gt;Level 1–5: the gateway prompts&lt;/h2&gt;
&lt;p&gt;Almost everyone starts here. You treat the model like an interactive Stack Overflow. Paste a snippet, ask for a fix, move on.&lt;/p&gt;
&lt;p&gt;At &lt;strong&gt;Level 1&lt;/strong&gt; you&apos;re generating small functions and boilerplate. At &lt;strong&gt;Level 5&lt;/strong&gt; you&apos;re debugging — instead of manually scanning logs, you drop a stack trace into a chat and get a diagnosis. That alone saves real hours.&lt;/p&gt;
&lt;p&gt;But you&apos;re still stuck in the copy-paste loop. You write the code. The AI patches the errors. It&apos;s a helpful assistant that has no idea what you&apos;re building.&lt;/p&gt;
&lt;p&gt;The trap at this level is letting the model freestyle without context. Escaping it means writing specs instead of wishes. You stop saying &quot;fix this&quot; and start saying:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&quot;Refactor this Laravel service to follow the Repository pattern, keep the existing public method signatures, and make sure database exceptions surface as domain exceptions.&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Same model. Completely different output.&lt;/p&gt;
&lt;h2&gt;Level 10–20: building context&lt;/h2&gt;
&lt;p&gt;At this stage you realize the model is only as good as the information it can see. Isolated chat windows stop being enough. You start using tools that read your whole codebase — the point where the &lt;a href=&quot;https://ansezz.com/blog/ai-vs-traditional-development/&quot;&gt;AI vs traditional development&lt;/a&gt; question stops being philosophical and turns into a workflow decision.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Level 10&lt;/strong&gt; is tests. Writing unit tests is usually the chore you postpone; AI turns it into leverage. A full &lt;strong&gt;Pest&lt;/strong&gt; or &lt;strong&gt;PHPUnit&lt;/strong&gt; suite for a Laravel feature drops in seconds. You&apos;re not just producing code anymore — you&apos;re producing a safety net that lets the agents at higher levels move fast without wrecking things.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Level 20&lt;/strong&gt; is when the AI actually knows your codebase. Codebase indexing, embeddings, RAG. The model retrieves the three files that matter instead of guessing from the file tree. Many teams go further and stand up a local &lt;strong&gt;pgvector&lt;/strong&gt; database holding internal docs, ADRs, and runbooks — the same retrieval discipline behind the &lt;a href=&quot;https://ansezz.com/blog/7-rag-mistakes-production/&quot;&gt;7 mistakes wrecking your production RAG stack&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The payoff is that you stop re-explaining your architecture in every session. Your event-driven layer, your custom service container bindings, your weird legacy billing table — the agent looks it up.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/ai-coding-workflow-levels/workflow-evolution.webp&quot; alt=&quot;Technical pop-art illustration showing an AI coding workflow evolving from chat prompts to Laravel and Shopify GraphQL agents&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Level 50+: the agentic shift&lt;/h2&gt;
&lt;p&gt;This is where the addiction matures into an engineering practice. You&apos;re no longer chatting with a model. You&apos;re building systems that act on your behalf.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Level 50&lt;/strong&gt; is the &lt;a href=&quot;https://ansezz.com/blog/claude-mcp-dev-tools/&quot;&gt;Model Context Protocol&lt;/a&gt;. MCP lets agents connect to real tools — the Shopify Admin GraphQL API, your database, your cloud infrastructure. It&apos;s the same plumbing behind &lt;a href=&quot;https://ansezz.com/blog/agentic-commerce-shopify/&quot;&gt;agentic commerce on Shopify&lt;/a&gt;, pointed at your own workflow instead of at a shopper. An agent can watch incoming orders and adjust inventory according to rules you described in plain language, because it holds an actual connection instead of a description of one.&lt;/p&gt;
&lt;p&gt;The Level 50 loop I actually run looks like this:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Plan.&lt;/strong&gt; You write a technical spec in markdown — scope, constraints, acceptance criteria.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Execute.&lt;/strong&gt; An agent reads the spec and generates the Laravel migrations, models, and controllers.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Verify.&lt;/strong&gt; A &lt;em&gt;separate&lt;/em&gt; agent writes tests and runs them until they pass.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Deploy.&lt;/strong&gt; A deployment agent ships to a &lt;a href=&quot;https://ansezz.com/blog/coolify-self-hosted-saas/&quot;&gt;Coolify instance&lt;/a&gt; and tails the logs for errors.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;You become the conductor. You own the &lt;em&gt;what&lt;/em&gt; and the &lt;em&gt;why&lt;/em&gt;; the agents grind through the &lt;em&gt;how&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;The separation in step 3 matters more than it looks. An agent that writes both the implementation and its tests will happily write tests that pass against its own bug. Two agents with different context catch what one agent rationalizes away — which is why I stopped reading diffs line by line and moved to a &lt;a href=&quot;https://ansezz.com/blog/stop-reading-code-ai-review/&quot;&gt;multi-agent review pipeline&lt;/a&gt;, and why &lt;a href=&quot;https://ansezz.com/blog/testing-ai-generated-code/&quot;&gt;AI-generated code still needs its own test discipline&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/ai-coding-workflow-levels/agentic-system.webp&quot; alt=&quot;Vibrant pop-art bento grid diagram of an agentic system wiring MCP servers, pgvector memory, and deployment agents&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Why manual coding feels slow now&lt;/h2&gt;
&lt;p&gt;Once you&apos;ve run an agentic workflow, the friction of manual coding becomes physically annoying. Five-minute tasks feel like an eternity. Hunting the correct GraphQL mutation shape for a Shopify app feels absurd when an agent with schema access resolves it in two seconds.&lt;/p&gt;
&lt;p&gt;The addiction isn&apos;t to the tool. It&apos;s to &lt;strong&gt;flow&lt;/strong&gt;. AI removes the micro-frictions that shatter concentration — the doc lookup, the typo, the forgotten import, the &quot;wait, what&apos;s this method called again.&quot; You stay in a high-level creative state for longer stretches, so you solve bigger problems.&lt;/p&gt;
&lt;p&gt;It also quietly redefines what senior means. Not knowing every syntax detail from memory. Knowing how to decompose a system, and how to steer an AI into building it correctly. Builder to architect.&lt;/p&gt;
&lt;h2&gt;Practical steps to level up&lt;/h2&gt;
&lt;p&gt;You don&apos;t drift into Level 50. You have to build the infrastructure for it.&lt;/p&gt;
&lt;h3&gt;1. Master MCP&lt;/h3&gt;
&lt;p&gt;Start with custom MCP servers for the tasks you repeat weekly. If you work on Shopify apps, expose a tool that queries your store&apos;s Admin GraphQL API directly, and another that validates a mutation against the current schema version. That single step kills most of the copy-paste in your day. Keep the surface small — every extra tool is context tax and one more thing the agent can misuse.&lt;/p&gt;
&lt;h3&gt;2. Implement RAG over your codebase&lt;/h3&gt;
&lt;p&gt;Don&apos;t rely on general knowledge for project-specific questions. Index your project. If you&apos;re running a Laravel SaaS, make sure the agent can retrieve your service-layer conventions and your event contracts, not just guess from &lt;code&gt;app/&lt;/code&gt;. Storing documentation embeddings in &lt;strong&gt;pgvector&lt;/strong&gt; is the cheapest long-term memory you&apos;ll ever buy — and it lives in the Postgres you already run.&lt;/p&gt;
&lt;h3&gt;3. Use multi-agent orchestration&lt;/h3&gt;
&lt;p&gt;Stop thinking about one AI and start thinking about a team. Planner, implementer, reviewer. Separation of concerns produces better code and fewer hallucinations, for the same reason it does with humans: the reviewer isn&apos;t emotionally invested in the plan.&lt;/p&gt;
&lt;h3&gt;4. Optimize for Shopify GraphQL&lt;/h3&gt;
&lt;p&gt;Shopify has moved on from REST for new development. Make sure your agents target the current &lt;strong&gt;GraphQL Admin API&lt;/strong&gt; version and don&apos;t reach for deprecated fields they memorized from a 2023 blog post. Pin the API version explicitly in your prompts and your client — schema drift is the single most common source of confidently wrong Shopify code.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// Example of a Laravel service using a Shopify GraphQL mutation.
// An agent writes the boilerplate; your spec dictates the contract.
public function updateProductInventory(string $inventoryItemId, int $newQuantity)
{
    $mutation = &amp;lt;&amp;lt;&amp;lt;&apos;GRAPHQL&apos;
    mutation inventorySetQuantities(
      $input: InventorySetQuantitiesInput!
      $idempotencyKey: String!
    ) {
      inventorySetQuantities(input: $input) @idempotent(key: $idempotencyKey) {
        inventoryAdjustmentGroup {
          createdAt
          reason
          changes {
            name
            delta
            quantityAfterChange
          }
        }
        userErrors {
          code
          field
          message
        }
      }
    }
    GRAPHQL;

    $variables = [
        &apos;idempotencyKey&apos; =&amp;gt; (string) \Illuminate\Support\Str::uuid(),
        &apos;input&apos; =&amp;gt; [
            &apos;name&apos; =&amp;gt; &apos;available&apos;,
            &apos;reason&apos; =&amp;gt; &apos;correction&apos;,
            &apos;ignoreCompareQuantity&apos; =&amp;gt; true,
            &apos;quantities&apos; =&amp;gt; [
                [
                    &apos;inventoryItemId&apos; =&amp;gt; $inventoryItemId,
                    &apos;locationId&apos; =&amp;gt; config(&apos;shopify.default_location_id&apos;),
                    &apos;quantity&apos; =&amp;gt; $newQuantity,
                ],
            ],
        ],
    ];

    return $this-&amp;gt;shopifyClient-&amp;gt;query($mutation, $variables);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Two details in there are exactly the kind of thing schema drift breaks.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;@idempotent&lt;/code&gt; directive arrived as optional in API version &lt;code&gt;2026-01&lt;/code&gt; and became &lt;strong&gt;required&lt;/strong&gt; in &lt;code&gt;2026-04&lt;/code&gt;. Any agent working from a 2024 blog post will emit this mutation without it and get rejected outright. Worse, a model that half-remembers the shape will reach for &lt;code&gt;inventorySetQuantity&lt;/code&gt; — singular — which isn&apos;t a mutation in the current schema at all.&lt;/p&gt;
&lt;p&gt;And note &lt;code&gt;userErrors&lt;/code&gt;. Shopify returns a &lt;code&gt;200&lt;/code&gt; with a populated &lt;code&gt;userErrors&lt;/code&gt; array for most business-rule failures, so an agent that only checks HTTP status will report success on a write that silently did nothing. Bake both rules into your spec once and every generated mutation inherits them.&lt;/p&gt;
&lt;h2&gt;Takeaways&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Start small, aim high.&lt;/strong&gt; Level 1 prompts are a doorway, not a destination. The value lives in the loops.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Context is king.&lt;/strong&gt; RAG and pgvector give the model specific knowledge of your codebase and business rules instead of internet averages.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Embrace MCP.&lt;/strong&gt; Real tool connections — Shopify GraphQL, your database, your infrastructure — beat pasted screenshots every time.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Split the agents.&lt;/strong&gt; Whoever writes the code should not be the only one grading it.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Standardize the loop.&lt;/strong&gt; Plan, execute, verify, deploy. The structure is what keeps quality from collapsing as speed goes up.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Focus on architecture.&lt;/strong&gt; As implementation gets cheap, your value moves to design, constraints, and judgment.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The hardest part of AI coding isn&apos;t the learning curve. It&apos;s realizing you can&apos;t go back. You&apos;ve tasted a different level of efficiency and the old way now looks like manual labor.&lt;/p&gt;
&lt;p&gt;This isn&apos;t about replacing yourself. It&apos;s about amplifying yourself until you&apos;re doing the work of a team.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;How are you structuring your AI coding workflow beyond simple chat prompts?&lt;/strong&gt; &lt;a href=&quot;https://ansezz.com/contact/&quot;&gt;Get in touch&lt;/a&gt; — I&apos;m always up for comparing agent stacks.&lt;/p&gt;
</content:encoded><category>ai</category><category>shopify</category><category>laravel</category><category>code-quality</category><category>vibe-coding</category><category>pgvector</category><category>claude</category><category>mcp</category></item><item><title>Stop reading every line of AI-generated code</title><link>https://ansezz.com/blog/stop-reading-code-ai-review/</link><guid isPermaLink="true">https://ansezz.com/blog/stop-reading-code-ai-review/</guid><description>Line-by-line review breaks on agent-sized diffs. How I moved to specs, property tests, and a multi-agent review pipeline — and the 3% I still read.</description><pubDate>Thu, 30 Jul 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Nobody wants to read a 5,000-line diff. But that is what lands in the pull request when an agent spends an afternoon refactoring a legacy service or scaffolding a new Laravel module end to end.&lt;/p&gt;
&lt;p&gt;The failure mode that follows is worth naming precisely, because it does not look like failure while it is happening. An agent scaffolds a multi-tenant module in one pass — migrations, models, policies, jobs, tests. The diff is correct, mostly. You spend two evenings reading it line by line and approve it. Weeks later something breaks in production: a queued job that ran outside the tenant scope. You read that file. You read that exact method. Your eyes went over it and your brain filed it as &quot;looks like the other ones.&quot;&lt;/p&gt;
&lt;p&gt;It feels like diligence. It is theater.&lt;/p&gt;
&lt;h2&gt;Where line-by-line review actually breaks&lt;/h2&gt;
&lt;p&gt;Code review guidance has been consistent for two decades: reviewers find defects reliably in chunks of roughly 200 to 400 lines, and effectiveness drops sharply past that. Nothing about that number has changed because we bolted an LLM onto the front of the process. Human attention is the constant. The volume of code is the variable, and it just went up by an order of magnitude.&lt;/p&gt;
&lt;p&gt;Three things go wrong when you push past that limit:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;You review syntax, not semantics.&lt;/strong&gt; Naming, formatting, and shape are cheap to evaluate, so tired brains drift toward them. A perfectly formatted diff can still ship an N+1 query, a missing index, or a broken state machine.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;You lose the cross-file thread.&lt;/strong&gt; The bug is almost never in the file you are reading. It is in the interaction between the job, the observer, and the tenant scope — three files you looked at twenty minutes apart.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;You pattern-match against yourself.&lt;/strong&gt; Agent-generated code is internally consistent. That consistency reads as correctness. Every file looks like the last one, so your reviewer instinct stops firing.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;None of this is new. What changed is that we used to write code slowly enough that review capacity roughly matched output. That coupling is gone. Generation scales with tokens; reading scales with eyeballs.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/stop-reading-code-ai-review/ast-parsing.webp&quot; alt=&quot;Abstract visualization of an AI agent parsing an AST across multiple files&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;What agents genuinely do better — and what they don&apos;t&lt;/h2&gt;
&lt;p&gt;A review agent pointed at a repository does not read files the way you do. Given the right tooling it walks the AST, follows call graphs, and traces a value from a controller through a service into a model and out to an external API. It does that for file forty with the same attention it gave file one.&lt;/p&gt;
&lt;p&gt;That is the real advantage: &lt;strong&gt;breadth and consistency&lt;/strong&gt;, not intelligence. An agent will happily check all 340 controller actions for a missing authorization call. You will check the twelve you remember.&lt;/p&gt;
&lt;p&gt;I want to be equally clear about the other side, because &quot;agents review better than humans&quot; is a bad summary of what I actually believe:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Agents hallucinate findings. They will confidently flag a race condition that cannot occur, and the fix they suggest can be worse than the code.&lt;/li&gt;
&lt;li&gt;Agents have no idea whether the feature should exist. Product intent is not in the diff.&lt;/li&gt;
&lt;li&gt;Agents are weak on the thing that only shows up at runtime under load — the query that is fine with 200 rows and fatal with 2 million.&lt;/li&gt;
&lt;li&gt;An agent reviewing code written by the same model family shares its blind spots.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So the move is not &quot;trust the agent instead of your eyes.&quot; It is: give the agent the work that scales with volume, and keep for yourself the work that scales with judgment. That split is the same one I drew in &lt;a href=&quot;https://ansezz.com/blog/ai-vs-traditional-development/&quot;&gt;AI vs traditional development&lt;/a&gt; — the boundary moved, it did not disappear.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Dimension&lt;/th&gt;
&lt;th&gt;Human line-by-line review&lt;/th&gt;
&lt;th&gt;Agentic verification&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Effective scope&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;A few hundred lines before quality drops&lt;/td&gt;
&lt;td&gt;Whole repository, via AST and call-graph traversal&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Consistency&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Degrades with fatigue and diff size&lt;/td&gt;
&lt;td&gt;Same checks applied to file 1 and file 400&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Typical focus&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Style, naming, local logic&lt;/td&gt;
&lt;td&gt;Invariants, contracts, types, test outcomes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Failure mode&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Misses cross-file interactions, rubber-stamps&lt;/td&gt;
&lt;td&gt;Confident false positives, misses product intent&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Cycle time&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Hours to days&lt;/td&gt;
&lt;td&gt;Minutes, on every push&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;The shift: own the spec, not the syntax&lt;/h2&gt;
&lt;p&gt;If you are not reading the code, something else has to hold the line. For me that is the specification layer — and writing it is now the highest-leverage thing I do on an agent-assisted project.&lt;/p&gt;
&lt;p&gt;Concretely, that means:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Executable acceptance criteria.&lt;/strong&gt; Not a Notion doc. Feature tests that fail before the agent starts and pass when it is done.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Property-based tests for anything with arithmetic or state.&lt;/strong&gt; Pricing, proration, inventory, refunds. Instead of three examples, assert the invariant: a refund never exceeds the captured amount, total allocated inventory never exceeds stock on hand.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Contract tests at every boundary.&lt;/strong&gt; Webhook payloads, third-party API responses, queue message shapes. This is where agent code fails quietly, because the happy path is the only path in the training data.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Machine-checkable rules for the things I keep repeating.&lt;/strong&gt; Static analysis at a strict level, an architecture-test suite asserting &quot;no job may run without a tenant context,&quot; a lint rule for direct DB access outside repositories.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That last one is the real trick. Every review comment I would type twice becomes a rule instead. If the agent can violate an invariant and the pipeline stays green, that is my bug, not the agent&apos;s. The numbers behind why this layer is non-negotiable — and the CI configuration I run — are in &lt;a href=&quot;https://ansezz.com/blog/testing-ai-generated-code/&quot;&gt;test AI code too&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;When a check fails, I do not hand-patch the file. I fix the prompt, tighten the spec, or add the missing rule, then re-run. That loop is the practical core of &lt;a href=&quot;https://ansezz.com/blog/agentic-workflows-vibe-coding/&quot;&gt;agentic workflows&lt;/a&gt;, and it only works if the feedback the agent receives is precise. Vague context produces vague code — the point I made in &lt;a href=&quot;https://ansezz.com/blog/prompt-engineering-vs-context-engineering/&quot;&gt;prompt engineering vs context engineering&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/stop-reading-code-ai-review/spec-gates.webp&quot; alt=&quot;Software engineer reviewing test coverage dashboards and specification gates&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;A multi-agent review pipeline that earns its cost&lt;/h2&gt;
&lt;p&gt;One giant &quot;review this PR&quot; prompt produces mush. Narrow agents with narrow briefs produce findings you can act on. This is the verify stage of the &lt;a href=&quot;https://ansezz.com/blog/ai-coding-workflow-levels/&quot;&gt;plan, execute, verify, deploy loop&lt;/a&gt;, and it deserves its own agents for the same reason it deserves its own step. The setup I run now has three, and they all execute inside CI rather than in my editor:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Correctness.&lt;/strong&gt; Runs the suite, static analysis, and the architecture tests. Then reviews the diff strictly against the written acceptance criteria — its brief explicitly forbids style commentary. Output: does this satisfy the spec, and where does it not.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Security.&lt;/strong&gt; Injection paths, authorization gaps on new routes, secrets in config, unsafe deserialization, mass-assignment on new models, anything touching a webhook signature. This one gets the OWASP-shaped checklist and nothing else.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Regression.&lt;/strong&gt; Diffs behavior against the previous stable build. It reads the migration files, flags schema changes without a rollback path, hunts for queries added to hot code paths, and cross-references the touched files against past incident history.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Each agent needs real access to be useful — the schema, the logs, the actual test output, not a pasted snippet. That is exactly the problem &lt;a href=&quot;https://ansezz.com/blog/claude-mcp-dev-tools/&quot;&gt;MCP servers&lt;/a&gt; solve, and it is the difference between a reviewer that guesses and one that checks.&lt;/p&gt;
&lt;p&gt;Two rules keep this from becoming noise. First, findings are severity-ranked and anything below &quot;would break in production&quot; goes to a comment, never a block. Second, a second agent tries to &lt;em&gt;refute&lt;/em&gt; each finding before I see it. A large share of the initial findings die there, and they are exactly the ones that would otherwise burn an afternoon.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/stop-reading-code-ai-review/review-pipeline.webp&quot; alt=&quot;Multi-agent CI pipeline with correctness, security, and regression agents&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;The loop&lt;/h2&gt;
&lt;p&gt;The pipeline is only worth building if it closes without me in the middle of it:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[Spec + tests] --&amp;gt; [Agent generates] --&amp;gt; [Static analysis + type checks]
                          ^                          |
                          |                          v
                   [Self-correction] &amp;lt;-- [Test suite + review agents]
                          |
                          v (all green)
                   [Human: seams review] --&amp;gt; [Merge / deploy]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Failures feed back into the agent&apos;s context automatically. I only see the branch once it is green, which is the same discipline that makes &lt;a href=&quot;https://ansezz.com/blog/ci-vs-cd/&quot;&gt;CI and CD&lt;/a&gt; work in the first place — the pipeline is the gate, not the reviewer&apos;s inbox.&lt;/p&gt;
&lt;h2&gt;What I still read myself&lt;/h2&gt;
&lt;p&gt;&quot;Stop reading code&quot; is a slogan, and taken literally it is wrong. Here is my actual list of things I open every time, no matter how green the pipeline is:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Migrations.&lt;/strong&gt; Anything irreversible or lock-heavy on a large table.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Auth and permission boundaries.&lt;/strong&gt; Policies, scopes, middleware, anything that decides who sees what.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Money paths.&lt;/strong&gt; Charges, refunds, discounts, tax, billing webhooks. Tests are necessary here and not sufficient.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;New architectural seams.&lt;/strong&gt; A new queue, a new external dependency, a new cache layer. The code may be fine; the decision may not be.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Anything the agent changed that nobody asked it to change.&lt;/strong&gt; Unrequested scope is the loudest smell in agent diffs.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That list is maybe 3% of a large diff. Reading 3% carefully beats skimming 100%, and it is the part where my decade of production scars actually transfers. The rest — the 340 controller actions, the DTO fields, the test scaffolding — is machine work, and I stopped pretending otherwise.&lt;/p&gt;
&lt;h2&gt;Takeaways&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Line-by-line review does not scale to agent-sized diffs.&lt;/strong&gt; Past a few hundred lines you are performing diligence, not doing it.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Use agents for breadth, not judgment.&lt;/strong&gt; Cross-file tracing and repeated checks are their edge; product intent and architectural taste are not.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Convert review comments into rules.&lt;/strong&gt; Anything you would say twice belongs in a test, a static-analysis level, or an architecture test.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Split the review agent into narrow briefs&lt;/strong&gt; — correctness, security, regression — and make a second pass try to refute each finding before it reaches you.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Keep a short human read list:&lt;/strong&gt; migrations, auth, money, new seams, unrequested changes.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The uncomfortable question is what your team&apos;s review process is actually for. If it exists to catch defects, most of it should be automated by now. If it exists to spread knowledge, say that out loud and redesign it around that goal instead.&lt;/p&gt;
&lt;p&gt;Where does your review process still depend on someone reading every line — and what would it take to turn that into a check? Tell me via &lt;a href=&quot;https://ansezz.com/contact/&quot;&gt;contact&lt;/a&gt;, I collect these. 🤘&lt;/p&gt;
</content:encoded><category>ai</category><category>code-quality</category><category>agentic-ai</category><category>ci-cd</category><category>architecture</category></item><item><title>Vibe coding vs agentic engineering: which ships?</title><link>https://ansezz.com/blog/vibe-coding-vs-agentic-engineering/</link><guid isPermaLink="true">https://ansezz.com/blog/vibe-coding-vs-agentic-engineering/</guid><description>Vibe coding is for prototypes, agentic engineering is for production. The daily split I use to ship AI-written code without hidden technical debt.</description><pubDate>Fri, 24 Jul 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;The era of manual syntax is over. In 2026 the question is no longer whether AI writes your code — it&apos;s how you govern the AI that does. Most developers I talk to are caught between two modes: the high-speed thrill of vibe coding and the slower discipline of agentic engineering.&lt;/p&gt;
&lt;p&gt;Both are real. Both work. But they don&apos;t work for the same job. Vibe coding will conjure a working application in an afternoon. Agentic engineering is what makes that application survive contact with production. Shipping in 2026 means knowing which one you&apos;re in at any given moment — and stopping the vibes before they reach &lt;code&gt;main&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;The rise and fall of vibe coding&lt;/h2&gt;
&lt;p&gt;Vibe coding was the breakout habit of 2025. It&apos;s a conversational approach where the feel of the interaction takes precedence over the underlying architecture. You describe a feature, the model returns a large block of code, you run it to see whether it works. It breaks, you paste the stack trace back, you ask for a fix. Repeat.&lt;/p&gt;
&lt;p&gt;That cycle is fast. It&apos;s addictive. It feels like magic right up until you try to scale a system built entirely on vibes.&lt;/p&gt;
&lt;p&gt;The real problem isn&apos;t the code quality — models are good now. The problem is that vibe coding is &lt;strong&gt;development without verification&lt;/strong&gt;. When the model hands you 500 lines and you ship because it looks right, you&apos;re operating on faith. You&apos;ve moved the bottleneck from typing to reviewing, and then you skipped the reviewing.&lt;/p&gt;
&lt;p&gt;None of that makes it useless. The first pass of a Shopify storefront section or a Laravel reporting dashboard can come together in an afternoon this way. The trouble shows up six weeks later: no tests, three competing patterns for the same problem, and a &lt;code&gt;Controller&lt;/code&gt; doing the job of a service, a job, and a repository. That&apos;s not a code problem, it&apos;s a process problem — and it&apos;s the same failure mode I wrote about in &lt;a href=&quot;https://ansezz.com/blog/vibe-coding/&quot;&gt;why projects need more than just logic&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Agentic engineering: the architecture of intent&lt;/h2&gt;
&lt;p&gt;Agentic engineering is the professional evolution of the same tooling. It isn&apos;t chatting with a single model. It&apos;s running a system of agents that plan, implement, test, and review under your supervision. Your role changes from coder to architect of intent.&lt;/p&gt;
&lt;p&gt;In an agentic workflow you supply a specification, not a vibe: the requirement, the architectural constraints, the acceptance criteria. The system decomposes it. One agent researches the API surface. One writes the implementation. One writes the tests. One reads the diff looking for reasons to reject it.&lt;/p&gt;
&lt;p&gt;The spec is the whole trick, and it&apos;s usually shorter than people expect:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;## Task: subscription pause endpoint

Constraints

- Laravel 12, existing `SubscriptionService`, no new packages
- Idempotent: repeat calls with the same `Idempotency-Key` return the first result
- Authorization via existing `SubscriptionPolicy@pause`

Done when

- Feature test: pause, double-pause, unauthorized, already-cancelled
- No changes to `routes/api.php` ordering
- `php artisan test` and `vendor/bin/pint --test` both green
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You&apos;re no longer reviewing lines — a habit that &lt;a href=&quot;https://ansezz.com/blog/stop-reading-code-ai-review/&quot;&gt;stops scaling at agent-sized diffs&lt;/a&gt;. You&apos;re reviewing a pull request that other agents already argued about. The human decision shrinks to go/no-go — which is exactly where a human decision is worth the most. That&apos;s the architecture side of it, and I go deeper into the MCP and loop mechanics in &lt;a href=&quot;https://ansezz.com/blog/agentic-workflows-vibe-coding/&quot;&gt;the shift to agentic workflows&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/vibe-coding-vs-agentic-engineering/agent-dashboard.webp&quot; alt=&quot;AI agent dashboard showing several agents working in parallel on implementation, testing, and refactoring tasks&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Side by side: vibe coding vs agentic engineering&lt;/h2&gt;
&lt;p&gt;Most AI-assisted projects that stall share one root cause: vibe coding applied to a problem that needed agentic engineering.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Dimension&lt;/th&gt;
&lt;th&gt;Vibe coding&lt;/th&gt;
&lt;th&gt;Agentic engineering&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Primary goal&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Speed and exploration&lt;/td&gt;
&lt;td&gt;Correctness and scale&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Your role&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Conversational partner&lt;/td&gt;
&lt;td&gt;Architect, mission control&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Verification&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&quot;Does it seem to work?&quot;&lt;/td&gt;
&lt;td&gt;Automated tests plus peer-agent diff review&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Structure&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Loose, improvised&lt;/td&gt;
&lt;td&gt;Written specs, repeatable SOPs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Best for&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Prototypes, spikes, one-offs&lt;/td&gt;
&lt;td&gt;Production SaaS, Shopify Plus apps&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Tech debt&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;High and invisible&lt;/td&gt;
&lt;td&gt;Managed and visible&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Failure mode&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Silent wrongness&lt;/td&gt;
&lt;td&gt;Slow starts, over-specification&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Note the last row. Agentic engineering has its own failure mode: you can spend an hour writing a spec for something you could have vibed in ten minutes and thrown away. Discipline applied to a throwaway prototype is just waste with better manners.&lt;/p&gt;
&lt;h2&gt;The hybrid workflow: mornings vibe, afternoons engineer&lt;/h2&gt;
&lt;p&gt;The people shipping fastest in 2026 don&apos;t pick a side. They split the day. Creative exploration in the high-energy hours, engineering rigor in the disciplined ones. If you want the rung-by-rung version of how a workflow gets there, I mapped &lt;a href=&quot;https://ansezz.com/blog/ai-coding-workflow-levels/&quot;&gt;the levels from gateway prompts to agentic systems&lt;/a&gt; separately.&lt;/p&gt;
&lt;h3&gt;Morning: the vibe&lt;/h3&gt;
&lt;p&gt;Start with exploration. If I&apos;m adding a RAG-backed feature, I don&apos;t open a spec document — I open the editor and vibe out the retrieval logic. Try pgvector, try a hosted index, see how the chunking feels against real documents. Wire &lt;a href=&quot;https://ansezz.com/blog/claude-mcp-dev-tools/&quot;&gt;Claude and MCP&lt;/a&gt; into the local environment and let it poke at the database directly.&lt;/p&gt;
&lt;p&gt;The output of the morning is not code you keep. It&apos;s a decision you keep. Nothing from this session is allowed to merge.&lt;/p&gt;
&lt;h3&gt;Afternoon: the rigor&lt;/h3&gt;
&lt;p&gt;Once the shape is proven, the vibes end. The prototype becomes the input to a spec: schema, API contract, error semantics, test coverage. Then the agents refactor that morning code into something documented and tested, and a review agent tries to break it.&lt;/p&gt;
&lt;p&gt;The hard rule that makes this work: &lt;strong&gt;the morning branch never merges&lt;/strong&gt;. It gets read, summarized into a spec, and deleted. If you let vibe-coded code merge &quot;just this once,&quot; you&apos;ve quietly reverted to mode one — and today&apos;s speed becomes next month&apos;s outage.&lt;/p&gt;
&lt;h2&gt;Building the agentic stack on Laravel and Shopify Plus&lt;/h2&gt;
&lt;p&gt;Agents need predictable ground to stand on. My stack is Laravel for application logic and Shopify Plus when the surface is commerce, and that&apos;s not nostalgia — it&apos;s because both are legible to a model.&lt;/p&gt;
&lt;p&gt;Laravel&apos;s conventions are the point. Service providers, policies, form requests, and a canonical directory layout mean an agent can answer &quot;where does this belong?&quot; without guessing. Shopify&apos;s GraphQL Admin API is similarly self-describing: typed, introspectable, versioned. An agent can discover the mutation it needs instead of hallucinating a REST route that was deprecated two versions ago. That predictability is most of what makes &lt;a href=&quot;https://ansezz.com/blog/agentic-commerce-shopify/&quot;&gt;agentic commerce on Shopify&lt;/a&gt; tractable.&lt;/p&gt;
&lt;p&gt;The other half is memory. Your agents need your codebase, your ADRs, and your incident history — otherwise every session starts from zero and you re-litigate decisions you settled in March. A vector store over your own repository and docs is the cheapest leverage in the stack, provided the retrieval layer is honest. Garbage context produces confident garbage code, which is exactly the failure pattern behind most &lt;a href=&quot;https://ansezz.com/blog/7-rag-mistakes-production/&quot;&gt;RAG mistakes in production&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/vibe-coding-vs-agentic-engineering/agentic-stack.webp&quot; alt=&quot;Architecture diagram showing a Laravel application, a pgvector store, and an agent orchestration layer feeding a CI pipeline&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Deploying with confidence&lt;/h2&gt;
&lt;p&gt;The last place vibes leak into production is the deploy. If a human is manually clicking ship after an agent wrote the change, the human is the weakest link — they&apos;re approving a diff they didn&apos;t write against tests they didn&apos;t read.&lt;/p&gt;
&lt;p&gt;Push the verification into the pipeline instead: migrations checked, feature tests green, a smoke pass against a preview environment, error rate watched for a window after rollout, automatic rollback if it moves. Docker plus Coolify gets you this on your own boxes without a platform bill, which is the whole premise of &lt;a href=&quot;https://ansezz.com/blog/coolify-self-hosted-saas/&quot;&gt;self-hosting your SaaS&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The goal isn&apos;t zero human involvement. It&apos;s that a bad vibe can&apos;t reach customers without something automated objecting first.&lt;/p&gt;
&lt;h2&gt;Takeaways&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Vibe coding is a discovery tool. Use it for prototypes, UI spikes, and messy ideas — then throw the code away and keep the decision.&lt;/li&gt;
&lt;li&gt;Agentic engineering is a production tool. Specs, tests, and multi-agent review are what make AI-written code safe to charge money for.&lt;/li&gt;
&lt;li&gt;The skill that matters in 2026 is writing unambiguous specs. It&apos;s the new senior-engineer bottleneck, and it&apos;s a writing skill more than a coding one.&lt;/li&gt;
&lt;li&gt;Give your agents memory. Vector-indexed code, docs, and incident history stop every session from restarting at zero.&lt;/li&gt;
&lt;li&gt;Split your day, and enforce the boundary. The morning branch does not merge.&lt;/li&gt;
&lt;li&gt;Watch the opposite failure too: over-specifying a throwaway spike is waste, not discipline.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Which part of your workflow is still running on vibes alone — and what would an agent find if it audited that code tomorrow? If you&apos;re trying to get an AI-assisted team from prototypes to production, &lt;a href=&quot;https://ansezz.com/services/&quot;&gt;here&apos;s how I help&lt;/a&gt;.&lt;/p&gt;
</content:encoded><category>ai</category><category>vibe-coding</category><category>agentic-ai</category><category>ai</category><category>claude</category><category>laravel</category><category>shopify</category><category>code-quality</category></item><item><title>Modular monoliths first: why microservices can wait</title><link>https://ansezz.com/blog/modular-monolith-first/</link><guid isPermaLink="true">https://ansezz.com/blog/modular-monolith-first/</guid><description>Distributed systems are the priciest fix for a problem you don&apos;t have. How to build a modular monolith, own data per module, and know when to split.</description><pubDate>Wed, 24 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Distributed systems are the most expensive way to solve a problem you do not have yet.&lt;/p&gt;
&lt;p&gt;Every year a new batch of SaaS founders jumps straight to microservices because they want to &quot;build for scale.&quot; Six months later they are debugging network latency and distributed transactions instead of shipping features. The product has not moved. The infrastructure bill has.&lt;/p&gt;
&lt;p&gt;The modular monolith has quietly won this argument. It gives you the logical separation you actually need without the operational tax of a distributed environment. This post covers why you should start there, how to structure it so it stays modular, and the specific signals that tell you it is finally time to split.&lt;/p&gt;
&lt;p&gt;If you are still weighing the two patterns at a high level, read &lt;a href=&quot;https://ansezz.com/blog/monolith-vs-microservices/&quot;&gt;monolith vs microservices&lt;/a&gt; first. This post assumes you have picked the monolith and want to build one that does not rot.&lt;/p&gt;
&lt;h2&gt;The microservice tax is real&lt;/h2&gt;
&lt;p&gt;Most startups choose microservices for the wrong reasons. They look at Netflix or Uber and assume the infrastructure is the blueprint for success. What they miss is that those companies adopted microservices to solve &lt;strong&gt;organizational&lt;/strong&gt; problems — hundreds of engineers who could not coordinate a single deploy — not purely technical ones.&lt;/p&gt;
&lt;p&gt;Split prematurely and you pay a heavy tax:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Operational overhead.&lt;/strong&gt; You now need service discovery, centralized logging, distributed tracing, and a CI/CD pipeline per repository. None of that ships a feature.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Data consistency.&lt;/strong&gt; Distributed transactions are hard. You end up with eventual consistency problems that need sagas or an outbox pattern to fix — code that exists only because you split.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Network latency.&lt;/strong&gt; Every cross-service call costs milliseconds and can fail. In a monolith these are function calls in memory that cannot time out.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Developer friction.&lt;/strong&gt; Debugging a request that hops through five services is far worse than stepping through one process. Reproducing it locally is worse still.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For a small team this overhead kills velocity. You spend 40% of your time on infrastructure and 60% on product. In a modular monolith that ratio flips back toward the thing customers pay for.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/modular-monolith-first/microservice-tax.webp&quot; alt=&quot;Comparison of monolith and microservice building blocks in pop-art style&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;What a modular monolith actually is&lt;/h2&gt;
&lt;p&gt;A modular monolith is not a big ball of mud with nicer folder names. It is a &lt;strong&gt;single deployable unit&lt;/strong&gt; where code is strictly organized into independent modules with well-defined interfaces between them.&lt;/p&gt;
&lt;p&gt;In a &lt;a href=&quot;https://ansezz.com/blog/category/laravel/&quot;&gt;Laravel&lt;/a&gt; codebase that means each domain gets its own namespace, its own service provider, its own routes, and its own migrations. A &lt;code&gt;Billing&lt;/code&gt; module never reaches into the &lt;code&gt;Users&lt;/code&gt; tables directly. It talks through a contract.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// app/Modules/Billing/Contracts/CustomerDirectory.php
namespace App\Modules\Billing\Contracts;

interface CustomerDirectory
{
    public function find(int $customerId): ?CustomerSummary;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;// app/Modules/Users/Adapters/EloquentCustomerDirectory.php
namespace App\Modules\Users\Adapters;

use App\Modules\Billing\Contracts\CustomerDirectory;
use App\Modules\Billing\Contracts\CustomerSummary;
use App\Modules\Users\Models\User;

final class EloquentCustomerDirectory implements CustomerDirectory
{
    public function find(int $customerId): ?CustomerSummary
    {
        $user = User::query()-&amp;gt;find($customerId);

        return $user
            ? new CustomerSummary($user-&amp;gt;id, $user-&amp;gt;email, $user-&amp;gt;billing_country)
            : null;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;Billing&lt;/code&gt; depends on an interface it owns and a DTO it controls. &lt;code&gt;Users&lt;/code&gt; supplies the implementation. Swap that adapter for an HTTP client tomorrow and &lt;code&gt;Billing&lt;/code&gt; does not change a single line. That is the whole trick: the seam already exists, you just move what is behind it.&lt;/p&gt;
&lt;p&gt;The goal is &lt;strong&gt;logical separation with physical unity&lt;/strong&gt;. You get a clean, decoupled &lt;a href=&quot;https://ansezz.com/blog/category/architecture/&quot;&gt;architecture&lt;/a&gt; without managing a fleet of containers on &lt;a href=&quot;https://ansezz.com/blog/coolify-docker-saas-hosting/&quot;&gt;Coolify&lt;/a&gt; or Kubernetes before you need to.&lt;/p&gt;
&lt;h2&gt;Bounded contexts keep the modules honest&lt;/h2&gt;
&lt;p&gt;The concept doing the real work here is the &lt;strong&gt;bounded context&lt;/strong&gt; — a term from Domain-Driven Design that marks the boundary where one model of a thing applies.&lt;/p&gt;
&lt;p&gt;A &lt;code&gt;Product&lt;/code&gt; in the &lt;code&gt;Inventory&lt;/code&gt; module is not the same object as a &lt;code&gt;Product&lt;/code&gt; in the &lt;code&gt;Marketing&lt;/code&gt; module. One cares about stock levels, SKUs, and warehouse locations. The other cares about SEO titles, hero images, and merchandising rules. Forcing them into one bloated &lt;code&gt;Product&lt;/code&gt; model is how modular monoliths turn into mud.&lt;/p&gt;
&lt;p&gt;Three rules keep boundaries intact:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Enforce them mechanically.&lt;/strong&gt; Human discipline fails under deadline. Add an architecture test that fails the build when &lt;code&gt;Modules\Billing&lt;/code&gt; imports &lt;code&gt;Modules\Inventory\Models&lt;/code&gt;. In PHP, &lt;a href=&quot;https://github.com/qossmic/deptrac&quot;&gt;Deptrac&lt;/a&gt; or a Pest arch test does this in a few lines. Any language has an equivalent.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Keep the shared kernel tiny.&lt;/strong&gt; A little shared code is fine. A giant &lt;code&gt;Common&lt;/code&gt; package that every module depends on is a monolith wearing a costume — you can never split anything out of it.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Prefer events over direct calls.&lt;/strong&gt; When &lt;code&gt;Orders&lt;/code&gt; needs &lt;code&gt;Shipping&lt;/code&gt; to react, emit a domain event instead of calling a method. Replacing an in-process listener with a message queue later becomes a config change, not a rewrite.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;That third rule is the highest-leverage one. Internal events are the cheapest dress rehearsal for &lt;a href=&quot;https://ansezz.com/blog/synchronous-vs-asynchronous-communication/&quot;&gt;asynchronous communication&lt;/a&gt; you will ever get, and they cost nothing while everything still runs in one process.&lt;/p&gt;
&lt;h2&gt;The data ownership trap&lt;/h2&gt;
&lt;p&gt;The database is where most monolith-to-microservice migrations die.&lt;/p&gt;
&lt;p&gt;If your modules share tables or run JOINs across domains, you are coupled at the data layer — the hardest coupling to break. No amount of tidy namespaces saves you when &lt;code&gt;Reporting&lt;/code&gt; joins six tables owned by four other modules.&lt;/p&gt;
&lt;p&gt;So aim for &lt;strong&gt;schema per module&lt;/strong&gt;. Use one physical Postgres or MySQL instance for simplicity, but give each module its own tables and treat them as private. In practice:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;No foreign keys crossing module boundaries. Store the ID and let the application layer resolve the relationship.&lt;/li&gt;
&lt;li&gt;No cross-module JOINs. If &lt;code&gt;Billing&lt;/code&gt; needs a customer email, it calls the contract, not the &lt;code&gt;users&lt;/code&gt; table.&lt;/li&gt;
&lt;li&gt;Read models over reach-ins. If &lt;code&gt;Reporting&lt;/code&gt; needs a wide view, have modules publish into a reporting table they explicitly own.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Here is the test: &lt;strong&gt;if you cannot imagine running your &lt;code&gt;Orders&lt;/code&gt; module against a separate database server tomorrow, your monolith is not modular yet.&lt;/strong&gt; It is a monolith with folders.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/modular-monolith-first/bounded-contexts.webp&quot; alt=&quot;Dashboard visualization of module metrics and boundaries in pop-art style&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;A decision framework for when to split&lt;/h2&gt;
&lt;p&gt;You rarely outgrow a monolith because of traffic. A single well-tuned Laravel box with &lt;a href=&quot;https://ansezz.com/blog/laravel-octane-high-traffic/&quot;&gt;Octane&lt;/a&gt; and a queue handles more load than most SaaS companies ever see. You outgrow it because of &lt;strong&gt;friction&lt;/strong&gt;. Three signals matter:&lt;/p&gt;
&lt;h3&gt;1. Team autonomy&lt;/h3&gt;
&lt;p&gt;Are developers constantly blocking each other? Twenty engineers merging into one codebase with a deployment queue is a real organizational bottleneck, and that alone can justify a split. Five engineers? A monolith is almost always faster. Conway&apos;s Law cuts both ways — do not buy a distributed architecture for a co-located team.&lt;/p&gt;
&lt;h3&gt;2. Divergent scaling profiles&lt;/h3&gt;
&lt;p&gt;Does one part of the app have radically different resource needs? An AI inference module that wants GPUs while your dashboard is plain CRUD is a genuine case for extraction. You stop paying for GPU-sized instances to serve settings pages, and your &lt;a href=&quot;https://ansezz.com/blog/category/devops/&quot;&gt;DevOps&lt;/a&gt; spend tracks actual demand.&lt;/p&gt;
&lt;h3&gt;3. Fault isolation&lt;/h3&gt;
&lt;p&gt;If a bug in non-critical reporting takes down checkout, you have a resilience problem. First try to solve it in-process — better error handling, background jobs, timeouts, a bulkhead around the risky call. If it keeps happening, a separate service acts as a circuit breaker around your core revenue path.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Modular monolith&lt;/th&gt;
&lt;th&gt;Microservices&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Deployment&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Simple (one pipeline)&lt;/td&gt;
&lt;td&gt;Complex (many pipelines)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Testing&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Easy (in-process)&lt;/td&gt;
&lt;td&gt;Hard (end-to-end focused)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Data integrity&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Strong (ACID)&lt;/td&gt;
&lt;td&gt;Eventual consistency&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Debugging&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Stack trace&lt;/td&gt;
&lt;td&gt;Distributed trace&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Operational cost&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Team scaling&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Bottlenecks past ~15–20&lt;/td&gt;
&lt;td&gt;Scales with squads&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Notice what is missing: raw performance. It is almost never the deciding factor, and when it is, a bigger box or a read replica usually beats a rewrite.&lt;/p&gt;
&lt;h2&gt;Extract background workers before services&lt;/h2&gt;
&lt;p&gt;When the split finally makes sense, your first move should not be a full service.&lt;/p&gt;
&lt;p&gt;Start with an &lt;strong&gt;extracted background worker&lt;/strong&gt;. Move heavy, long-running tasks — PDF generation, CSV exports, video transcoding, embedding jobs — into their own process consuming from a queue. You get isolation, independent scaling, and blast-radius reduction without the pain of synchronous API contracts, retries, and versioning. &lt;a href=&quot;https://ansezz.com/blog/scaling-with-rabbitmq/&quot;&gt;RabbitMQ&lt;/a&gt; or a &lt;a href=&quot;https://ansezz.com/blog/event-driven-pubsub/&quot;&gt;Pub/Sub-style event backbone&lt;/a&gt; covers most of this, and the same pattern powers &lt;a href=&quot;https://ansezz.com/blog/message-queues-document-processing/&quot;&gt;queue-based document processing&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Only after that should you consider carving out a synchronous service. When you do, the &lt;a href=&quot;https://ansezz.com/blog/monolith-to-microservices/&quot;&gt;strangler fig migration&lt;/a&gt; is the safe path — route one endpoint at a time and keep the monolith as your fallback.&lt;/p&gt;
&lt;p&gt;One more constraint: &lt;strong&gt;wait until your domain is stable&lt;/strong&gt;. Early-stage products change their data models weekly. Hard service boundaries during that discovery phase turn every small feature into a multi-service orchestration project. Boundaries are cheap to move inside a monolith and expensive to move across a network.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/modular-monolith-first/extraction-path.webp&quot; alt=&quot;Diagram of a module being extracted from a monolith into its own service&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Takeaways&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Start modular.&lt;/strong&gt; Separate business domains with namespaces, modules, and contracts from day one — this costs nothing early and is nearly impossible to retrofit later.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Enforce boundaries with tooling.&lt;/strong&gt; Architecture tests in CI, not code-review vigilance.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Own your data per module.&lt;/strong&gt; No cross-module foreign keys, no cross-domain JOINs. Data coupling is the coupling that traps you.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Emit events internally.&lt;/strong&gt; In-process listeners today become queue consumers tomorrow with a config change.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Split on friction, not fashion.&lt;/strong&gt; Deployment bottlenecks, divergent scaling profiles, and repeat fault cascades are reasons. A conference talk is not.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Extract workers first.&lt;/strong&gt; Async background processes give you most of the isolation benefits at a fraction of the operational cost.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If your architecture lets you change your mind later without a rewrite, you have already won. That is the whole point of deferring the decision.&lt;/p&gt;
&lt;p&gt;How often are you actually checking for cross-module coupling in your codebase? If the answer is &quot;never,&quot; that is the cheapest audit you can run this week — and &lt;a href=&quot;https://ansezz.com/contact/&quot;&gt;I am happy to look at it with you&lt;/a&gt;.&lt;/p&gt;
</content:encoded><category>architecture</category><category>architecture</category><category>microservices</category><category>laravel</category><category>scaling</category><category>devops</category></item><item><title>Stateless vs stateful apps: the architecture split</title><link>https://ansezz.com/blog/stateless-vs-stateful-apps/</link><guid isPermaLink="true">https://ansezz.com/blog/stateless-vs-stateful-apps/</guid><description>Stateful servers remember; stateless servers forget by design. Why horizontal scaling, sticky sessions, and shared Redis decide the architecture split.</description><pubDate>Tue, 23 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Scaling a web application often feels like trying to upgrade a jet engine while the plane is mid-flight. You start with a single server that handles every request perfectly. But as your traffic grows, you add more servers, and suddenly everything breaks. Users are getting logged out randomly. Shopping carts are disappearing into thin air. The database is screaming under the pressure of redundant session checks. This chaos usually stems from a fundamental architectural decision made in the early stages of development. You are likely struggling with the friction between stateless and stateful design patterns.&lt;/p&gt;
&lt;p&gt;If your application relies on the server remembering who a user is based on local memory, you are building a stateful system. This works great for small projects but becomes a nightmare when you need to scale horizontally. On the other hand, moving to a fully stateless model requires a complete rethink of how you handle authentication and data persistence. Understanding the trade-offs between stateless vs stateful apps is not just an academic exercise. It is a prerequisite for building robust, high-performance software in a modern cloud environment.&lt;/p&gt;
&lt;h2&gt;Defining state in the digital world&lt;/h2&gt;
&lt;p&gt;Before comparing the two architectures, we must define what &quot;state&quot; actually means in a web context. State is any data that the application needs to remember between different interactions. This could be a user&apos;s login status, the items in a Shopify cart, or the progress of a multi-step form.&lt;/p&gt;
&lt;p&gt;In a traditional web application, the server manages this state by creating a unique session for every visitor. The server stores a small file or a piece of memory linked to a specific session ID. When the user moves from one page to another, the server looks up that ID and retrieves the state. This makes the application &quot;stateful&quot; because the server&apos;s response to a request depends on what happened in previous requests.&lt;/p&gt;
&lt;h2&gt;The stateless architecture: the forgetful master&lt;/h2&gt;
&lt;p&gt;A stateless application treats every single HTTP request as a completely new event. The server does not remember anything about the client once the request is finished. A user clicks a button, the server processes the data, sends a response, and immediately forgets that user ever existed.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/stateless-vs-stateful-apps/stateless-flow.webp&quot; alt=&quot;Stateless flow diagram showing a client and a server using a Redis cache for state&quot; /&gt;&lt;/p&gt;
&lt;p&gt;For this to work, the client must provide all the necessary information with every single request. This is why stateless apps often use JSON Web Tokens (JWT) or API keys. The token contains the user&apos;s identity and permissions. When the server receives the token, it validates it, performs the requested action, and sends back the result.&lt;/p&gt;
&lt;p&gt;The primary advantage of this approach is horizontal scalability. Since the server does not store any local session data, any server in your cluster can handle any request. You can add ten more servers to your stack using a tool like &lt;a href=&quot;https://ansezz.com/blog/coolify-docker-saas-hosting/&quot;&gt;Coolify or Docker&lt;/a&gt; and the load balancer can distribute traffic without worrying about where the user&apos;s session is located. This same property is what lets a stateless tier sit cleanly behind an &lt;a href=&quot;https://ansezz.com/blog/api-gateway-ai-stack/&quot;&gt;API gateway&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;The stateful architecture: the memory expert&lt;/h2&gt;
&lt;p&gt;Stateful applications are built on the premise of continuity. The server maintains a persistent connection or a session record for every active user. This was the default way of building the web for decades. If you are using Laravel with standard Blade templates and the built-in authentication guard, you are likely running a stateful application.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/stateless-vs-stateful-apps/stateful-flow.webp&quot; alt=&quot;Stateful flow diagram showing a server pulling data from local memory&quot; /&gt;&lt;/p&gt;
&lt;p&gt;In a stateful system, the server&apos;s internal memory or local disk stores the user&apos;s context. This makes development faster and easier for certain features. For example, &quot;flashing&quot; a success message to the next page is trivial in a stateful app. The server just puts the message in the session and reads it on the next load.&lt;/p&gt;
&lt;p&gt;However, statefulness introduces a &quot;sticky session&quot; problem. If you have three servers (A, B, and C) and a user&apos;s session is stored on Server A, the load balancer must ensure that every subsequent request from that user goes back to Server A. If the user hits Server B, that server will have no record of them, and they will appear logged out. This makes scaling significantly more complex and creates a single point of failure. If Server A crashes, all users &quot;stuck&quot; to it lose their session data.&lt;/p&gt;
&lt;h2&gt;Stateless vs stateful apps: key differences at a glance&lt;/h2&gt;
&lt;p&gt;Choosing between these two paths depends on your specific technical requirements. Below is a breakdown of how they compare across critical engineering metrics.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Stateless Apps&lt;/th&gt;
&lt;th&gt;Stateful Apps&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Data storage&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;State is stored on the client or a shared database/cache.&lt;/td&gt;
&lt;td&gt;State is stored in the server&apos;s memory or local storage.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Scalability&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;High. Any server can process any request.&lt;/td&gt;
&lt;td&gt;Low. Requires sticky sessions or complex synchronization.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Resilience&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;High. If a server dies, another one takes over instantly.&lt;/td&gt;
&lt;td&gt;Moderate. Server failure often leads to session loss.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Complexity&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Higher initial setup for token management and external caching.&lt;/td&gt;
&lt;td&gt;Lower initial setup for small-scale projects.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Performance&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Potential overhead from passing tokens or fetching data from Redis.&lt;/td&gt;
&lt;td&gt;Faster local memory access but slower overall scaling.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;Practical implementation in Laravel&lt;/h2&gt;
&lt;p&gt;In the world of PHP development, Laravel gives you the flexibility to choose either path. By default, the &lt;code&gt;web&lt;/code&gt; middleware group uses stateful sessions. It stores a session cookie on the browser and matches it to a server-side entry, which since Laravel 11 lands in the &lt;code&gt;database&lt;/code&gt; driver out of the box. This is perfect for traditional dashboards where you need simple state management and cookie-based CSRF protection.&lt;/p&gt;
&lt;p&gt;To make your Laravel app stateless, you move toward token-based authentication using Laravel Sanctum or Passport. You skip session cookies on your API routes and let every request carry its own bearer token, validating the user against a shared store like Redis or the database. This shift is essential when building backends for mobile apps or complex React/Vue frontends.&lt;/p&gt;
&lt;p&gt;This is also where the stateless model pays off as you grow: because no node owns a session, you can lean on &lt;a href=&quot;https://ansezz.com/blog/horizontal-vs-vertical-scaling/&quot;&gt;horizontal scaling&lt;/a&gt; and add capacity behind the load balancer without any session-affinity gymnastics.&lt;/p&gt;
&lt;h2&gt;Scaling with Shopify and modern e-commerce&lt;/h2&gt;
&lt;p&gt;Shopify provides an interesting look at how these architectures live together. The Shopify platform itself is a massive distributed system. While the &quot;state&quot; of a store (orders, inventory, customers) is preserved in giant databases, the web tier that handles incoming traffic is designed to be stateless. This allows Shopify to handle massive spikes during Black Friday by spinning up thousands of web workers that don&apos;t need to share local memory.&lt;/p&gt;
&lt;p&gt;If you are building &lt;a href=&quot;https://ansezz.com/blog/agentic-commerce-shopify/&quot;&gt;agentic commerce solutions on Shopify&lt;/a&gt;, your app architecture should lean toward statelessness. Your app will receive webhooks and API calls from various Shopify nodes. If your app is stateful and relies on local memory to track a multi-step checkout process, you will likely encounter race conditions or lost data as Shopify&apos;s infrastructure scales. Using a shared Redis instance to store temporary job state is the professional way to handle this.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/stateless-vs-stateful-apps/horizontal-scaling.webp&quot; alt=&quot;Horizontal scaling diagram showing multiple server nodes connected to one cloud database&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;DevOps and deployment considerations&lt;/h2&gt;
&lt;p&gt;Your choice between stateless vs stateful apps will dictate your DevOps strategy. Stateless apps are the &quot;gold standard&quot; for containerization. Since they have no local dependencies, you can run them in Docker containers and deploy them to any cloud provider without friction. You don&apos;t need to worry about preserving the &quot;health&quot; of a specific server instance.&lt;/p&gt;
&lt;p&gt;Stateful apps require much more care. If you are deploying a stateful Laravel app, you must ensure that your session driver is set to something central like Redis or a database. Laravel 11 and later default to the &lt;code&gt;database&lt;/code&gt; driver, but if you switch it back to &lt;code&gt;file&lt;/code&gt;, each node writes sessions to its own disk and your horizontal scaling will fail. You also need to consider how you handle deployments. A &quot;zero-downtime&quot; deployment is harder when you have thousands of active sessions living in server memory. You often have to wait for sessions to drain before taking a node offline.&lt;/p&gt;
&lt;h2&gt;Takeaways&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Stateless apps&lt;/strong&gt; treat every request as a new event, requiring the client to provide context (tokens) every time.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Stateful apps&lt;/strong&gt; remember user interactions through local server memory or session files, making them easier to build but harder to scale.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Horizontal scaling&lt;/strong&gt; is the primary driver for choosing a stateless architecture. It allows you to add servers without session conflicts.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Laravel&lt;/strong&gt; supports both. Use sessions for simple web apps and tokens (Sanctum) for scalable APIs.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Shared storage&lt;/strong&gt; (like Redis) is the bridge that allows an application to feel stateful to the user while remaining stateless at the server level.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Shopify developers&lt;/strong&gt; should prioritize stateless designs to handle the platform&apos;s distributed nature and high-volume webhooks.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;How are you managing session persistence in your current production environment to ensure horizontal scaling?&lt;/p&gt;
</content:encoded><category>architecture</category><category>architecture</category><category>scaling</category><category>laravel</category><category>redis</category><category>devops</category><category>shopify</category></item><item><title>Docker vs Kubernetes: containers vs orchestration</title><link>https://ansezz.com/blog/docker-vs-kubernetes/</link><guid isPermaLink="true">https://ansezz.com/blog/docker-vs-kubernetes/</guid><description>Docker packages your app into a portable box; Kubernetes manages a fleet of them across a cluster. How they differ and when you need orchestration.</description><pubDate>Mon, 22 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Shipping software is hard. You build an app that works perfectly on your machine, but the moment it hits production, everything breaks. This &quot;it works on my machine&quot; syndrome is exactly why understanding the difference between Docker vs Kubernetes is essential for modern engineers. Environment inconsistencies create friction, delay releases, and lead to late-night debugging sessions that could have been avoided with the right tooling.&lt;/p&gt;
&lt;p&gt;The gap between local development and production scale is wide. One tool packages your application into a portable box; the other manages a fleet of those boxes across a cluster of servers. If you are shipping modern web or e-commerce applications, you eventually need both, and knowing where each one stops saves you a lot of wasted effort.&lt;/p&gt;
&lt;h2&gt;The container revolution with Docker&lt;/h2&gt;
&lt;p&gt;Docker changed how we think about software delivery by introducing the concept of containerization. A container is a lightweight, standalone package that includes everything needed to run an application. This includes the code, runtime, system tools, libraries, and settings.&lt;/p&gt;
&lt;p&gt;When you use Docker, you are essentially creating an immutable image of your software. This image behaves exactly the same whether it is running on your laptop, a colleague&apos;s machine, or a staging server. It eliminates the need to manually configure environments, which is a major win for developer productivity.&lt;/p&gt;
&lt;p&gt;Docker operates on a single-host level. It manages the lifecycle of individual containers. You use the Docker CLI to build, run, and stop these containers. For many small-scale projects or early-stage startups, Docker on a single VPS is often enough. In fact, tools like &lt;a href=&quot;https://ansezz.com/blog/coolify-docker-saas-hosting/&quot;&gt;Coolify for Docker SaaS hosting&lt;/a&gt; have made it incredibly easy to manage these deployments without needing a massive orchestration layer.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/docker-vs-kubernetes/containerization.webp&quot; alt=&quot;Diagram of a Docker container packaging application code, runtime, libraries, and config into one portable image&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Understanding Kubernetes orchestration&lt;/h2&gt;
&lt;p&gt;If Docker is about the container, Kubernetes is about the cluster. Kubernetes, often abbreviated as K8s, is an open-source platform designed to automate the deployment, scaling, and management of containerized applications. It does not compete with Docker. Docker builds the image; Kubernetes schedules and runs it at scale through whatever container runtime sits on each node.&lt;/p&gt;
&lt;p&gt;Imagine you have a high-traffic Shopify app that suddenly experiences a surge in users. Managing fifty individual Docker containers across five different servers manually would be a nightmare. You would have to track which server has space, handle load balancing, and manually restart any container that crashes.&lt;/p&gt;
&lt;p&gt;Kubernetes handles this automatically. It uses a declarative approach where you define the &quot;desired state&quot; of your system in YAML files. The Kubernetes control plane then works tirelessly to ensure the actual state matches your requirements. If a container dies, Kubernetes restarts it. If a node fails, it reschedules the work elsewhere.&lt;/p&gt;
&lt;h2&gt;Docker vs Kubernetes: key technical differences&lt;/h2&gt;
&lt;p&gt;Comparing Docker vs Kubernetes is not about choosing one over the other. They operate at different layers of the stack. Docker is the runtime that builds and runs the container. Kubernetes is the orchestration layer that schedules those containers across a network.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Docker&lt;/th&gt;
&lt;th&gt;Kubernetes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Primary goal&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Containerizing an application&lt;/td&gt;
&lt;td&gt;Managing a cluster of containers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Scaling&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Manual scaling on a single host&lt;/td&gt;
&lt;td&gt;Automated horizontal scaling&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Self-healing&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Restart policies, single host&lt;/td&gt;
&lt;td&gt;Probe-based restarts and rescheduling&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Networking&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Basic host-level networking&lt;/td&gt;
&lt;td&gt;Advanced cluster-wide networking&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Complexity&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Low to medium&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Docker focuses on the &quot;how&quot; of running an app. Kubernetes focuses on the &quot;where&quot; and &quot;how many&quot; of running those apps across a distributed system. If the unit of scheduling is new to you, it helps to understand the &lt;a href=&quot;https://ansezz.com/blog/container-vs-pod/&quot;&gt;difference between a container and a pod&lt;/a&gt; before you reach for orchestration.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/docker-vs-kubernetes/kubernetes-orchestration.webp&quot; alt=&quot;Kubernetes control plane scheduling and self-healing container workloads across multiple worker nodes&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;How they work together in production&lt;/h2&gt;
&lt;p&gt;In a professional DevOps workflow, Docker and Kubernetes are teammates. The process usually starts with a Dockerfile. You build a Docker image and push it to a private container registry. This image is your &quot;source of truth&quot; for the application.&lt;/p&gt;
&lt;p&gt;Next, you create Kubernetes manifests. These files tell the cluster which image to use, how many replicas are needed, and what environment variables to inject. When you apply these manifests, the kubelet on each node calls the container runtime to pull the image and start the container. One caveat that trips people up: since version 1.24, Kubernetes removed dockershim, so it no longer talks to Docker Engine directly. Nodes now run a CRI-compliant runtime such as containerd or CRI-O. The image you built with Docker still runs fine because it follows the OCI image spec, but Docker Engine itself is no longer the thing executing it.&lt;/p&gt;
&lt;p&gt;This synergy allows for seamless updates. You can perform rolling deployments where Kubernetes replaces old pods with new ones in a controlled rollout. This keeps the service available throughout. If the new version has a bug, Kubernetes can automatically roll back to the previous stable image.&lt;/p&gt;
&lt;h2&gt;Choosing the right path for your project&lt;/h2&gt;
&lt;p&gt;Not every project needs the overhead of Kubernetes. If you are an entrepreneur or a small team building a simple web application, starting with Docker Compose or a &lt;a href=&quot;https://ansezz.com/blog/coolify-self-hosted-saas/&quot;&gt;self-hosted SaaS platform&lt;/a&gt; might be more efficient. It allows you to ship fast without managing a complex cluster.&lt;/p&gt;
&lt;p&gt;However, if your business requires high availability, handles massive amounts of data, or uses a microservices architecture, Kubernetes is the industry standard. It provides the &lt;a href=&quot;https://ansezz.com/blog/horizontal-vs-vertical-scaling/&quot;&gt;horizontal scaling&lt;/a&gt; needed to grow from a few hundred users to millions without changing your core infrastructure.&lt;/p&gt;
&lt;p&gt;Shopify itself is a good example of the payoff: its platform runs on Kubernetes across hundreds of clusters on Google Kubernetes Engine, autoscaling stateless workloads to absorb the brutal traffic spikes of Black Friday and Cyber Monday. Most merchants never touch that layer because Shopify owns it, but if you are building custom apps or your own infrastructure at that scale, the ability to declare capacity and let the cluster reconcile it is the competitive advantage that outweighs the setup complexity.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/docker-vs-kubernetes/docker-vs-kubernetes.webp&quot; alt=&quot;Side-by-side comparison of Docker as the container runtime and Kubernetes as the orchestration layer&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Takeaways&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Docker&lt;/strong&gt; is for creating, packaging, and running individual containers on a host.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Kubernetes&lt;/strong&gt; is for orchestrating and managing fleets of containers across a cluster of servers.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Synergy&lt;/strong&gt;: You use Docker to build the images and Kubernetes to deploy and scale them.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Scale matters&lt;/strong&gt;: Small projects should stick to Docker to avoid unnecessary complexity. Enterprise-grade apps require Kubernetes for self-healing and autoscaling.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Declarative power&lt;/strong&gt;: Kubernetes uses YAML to maintain a desired state, making infrastructure management more predictable.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;At what stage of growth did your infrastructure move from a single server to a distributed cluster? If you&apos;re weighing that jump right now, &lt;a href=&quot;https://ansezz.com/services/&quot;&gt;here&apos;s how I help teams get the call right&lt;/a&gt;.&lt;/p&gt;
</content:encoded><category>devops</category><category>docker</category><category>kubernetes</category><category>devops</category><category>scaling</category></item><item><title>Forward proxy vs reverse proxy: the technical guide</title><link>https://ansezz.com/blog/forward-proxy-vs-reverse-proxy/</link><guid isPermaLink="true">https://ansezz.com/blog/forward-proxy-vs-reverse-proxy/</guid><description>A forward proxy hides the client; a reverse proxy hides the server. How traffic direction shapes your load balancing, SSL termination, and security design.</description><pubDate>Mon, 22 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Most developers interact with proxies every single day without realizing which direction the traffic is actually flowing. The choice between a forward proxy vs reverse proxy comes down to one question: whose side is the middleman on? You set up Nginx for a Laravel app, and it works. You route outbound traffic through a corporate proxy, and it works. But when the site goes down under heavy load or your IP gets blacklisted, that distinction becomes the difference between a quick fix and hours of infrastructure debt.&lt;/p&gt;
&lt;p&gt;In modern web architecture, a proxy is essentially a &quot;middleman&quot; server. It sits between two parties to facilitate communication. However, the side it represents determines its name, its function, and its security benefits. This guide breaks down the core technical differences to help you choose the right pattern for your next deployment.&lt;/p&gt;
&lt;h2&gt;The forward proxy: protecting the client&lt;/h2&gt;
&lt;p&gt;A forward proxy sits in front of one or more client machines and acts as their representative to the open internet. When a user makes a request to a website, the request first hits the forward proxy. The proxy then evaluates the request, sends it to the web server, and passes the response back to the user.&lt;/p&gt;
&lt;p&gt;From the perspective of the internet, the forward proxy is the one making the request. The original client&apos;s IP address remains hidden behind the proxy&apos;s IP. This is a common setup in corporate environments or for users seeking anonymity.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/forward-proxy-vs-reverse-proxy/forward-proxy.webp&quot; alt=&quot;Forward proxy diagram: client requests pass through a proxy that masks the client IP before reaching the internet&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;Why use a forward proxy?&lt;/h3&gt;
&lt;p&gt;Forward proxies are primarily used to manage and secure outbound traffic. Here are the most common engineering use cases:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Anonymity and privacy&lt;/strong&gt;: By masking the client IP, forward proxies prevent websites from tracking individual users based on their network location.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Content filtering&lt;/strong&gt;: Organizations use forward proxies to block access to specific URLs or categories of websites. If a request violates a security policy, the proxy simply drops it.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Bypassing geo-restrictions&lt;/strong&gt;: If you need to access a service only available in another region, a forward proxy located in that region can make your request appear local.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Caching for performance&lt;/strong&gt;: If multiple clients in the same network request the same external resource, the forward proxy can cache that response locally. This saves bandwidth and reduces latency for subsequent requests.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;The reverse proxy: protecting the server&lt;/h2&gt;
&lt;p&gt;While a forward proxy represents the client, a reverse proxy represents the server. It sits at the edge of your infrastructure and intercepts all incoming requests from the internet before they reach your backend application.&lt;/p&gt;
&lt;p&gt;The client believes they are communicating directly with the origin server. In reality, they are talking to the reverse proxy. The proxy then decides which backend server should handle the request. This architecture is a cornerstone of &lt;a href=&quot;https://ansezz.com/blog/category/devops/&quot;&gt;modern cloud infrastructure&lt;/a&gt; and high-availability systems.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/forward-proxy-vs-reverse-proxy/reverse-proxy.webp&quot; alt=&quot;Reverse proxy diagram: a single edge entry point receiving internet traffic and distributing it across multiple backend servers&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;Why use a reverse proxy?&lt;/h3&gt;
&lt;p&gt;Reverse proxies are essential for scaling and securing web applications. They handle several &quot;heavy lifting&quot; tasks that your application code shouldn&apos;t have to manage.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Load balancing&lt;/strong&gt;: This is the most common use case. A reverse proxy like Nginx or HAProxy distributes incoming traffic across multiple app nodes so no single server becomes a bottleneck. The overlap here is real enough that it&apos;s worth reading &lt;a href=&quot;https://ansezz.com/blog/load-balancer-vs-reverse-proxy/&quot;&gt;load balancer vs reverse proxy&lt;/a&gt; to see where the two roles diverge.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;SSL termination&lt;/strong&gt;: Managing TLS certificates on every backend server is a nightmare. A reverse proxy decrypts incoming HTTPS at the edge and forwards plain HTTP across a trusted internal network. If compliance demands end-to-end encryption, you re-encrypt to the backend with &lt;code&gt;proxy_pass https://&lt;/code&gt; and &lt;code&gt;proxy_ssl_verify on&lt;/code&gt; instead.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Security and WAF&lt;/strong&gt;: Reverse proxies hide the existence and characteristics of your backend servers. You can run a Web Application Firewall (WAF) at the proxy level to filter SQL injection and XSS before they ever hit your code, and absorb volumetric DDoS traffic at the edge.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Caching&lt;/strong&gt;: By caching static assets or even entire HTML responses, a reverse proxy serves requests without touching the database or the application server. Push that cache out to the network edge and you are effectively running a &lt;a href=&quot;https://ansezz.com/blog/cdn-vs-cache/&quot;&gt;CDN — which is why high-traffic sites need both&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Forward proxy vs reverse proxy: visibility and flow&lt;/h2&gt;
&lt;p&gt;The easiest way to remember the difference is to look at who is being &quot;hidden&quot; from whom.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Forward Proxy&lt;/th&gt;
&lt;th&gt;Reverse Proxy&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Who it protects&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;The client&lt;/td&gt;
&lt;td&gt;The server&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;IP hiding&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Hides client IP from server&lt;/td&gt;
&lt;td&gt;Hides server IP from client&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Placement&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Internal network (client-side)&lt;/td&gt;
&lt;td&gt;Edge of the network (server-side)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Primary goal&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Privacy, filtering, egress control&lt;/td&gt;
&lt;td&gt;Load balancing, security, performance&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Example tool&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Squid, Privoxy&lt;/td&gt;
&lt;td&gt;Nginx, HAProxy, Cloudflare&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;In a forward proxy setup, the origin server has no idea who the actual client is. In a reverse proxy setup, the client has no idea which backend server actually processed their request. Both are essential for &lt;a href=&quot;https://ansezz.com/blog/category/architecture/&quot;&gt;clean architecture&lt;/a&gt; but solve entirely different problems.&lt;/p&gt;
&lt;h2&gt;Engineering implementations: Nginx and Docker&lt;/h2&gt;
&lt;p&gt;When building modern web applications, you are most likely to interact with reverse proxies. If you are using a tool like &lt;a href=&quot;https://ansezz.com/blog/coolify-docker-saas-hosting/&quot;&gt;Coolify or Docker&lt;/a&gt;, the reverse proxy is often the component that manages your containers.&lt;/p&gt;
&lt;p&gt;For instance, an Nginx reverse proxy configuration for a Laravel application might look like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;server {
    listen 80;
    server_name myapp.com;

    location / {
        proxy_pass http://app_container:8000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this setup, Nginx acts as the reverse proxy. It receives the request from the user and forwards it to the &lt;code&gt;app_container&lt;/code&gt;. It also passes along the &lt;code&gt;X-Forwarded-For&lt;/code&gt; header so your application can still know the original user&apos;s IP address if needed. This is a critical pattern when building an &lt;a href=&quot;https://ansezz.com/blog/api-gateway-ai-stack/&quot;&gt;API gateway for an AI stack&lt;/a&gt; or managing Shopify app extensions.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/forward-proxy-vs-reverse-proxy/nginx-docker.webp&quot; alt=&quot;Architecture diagram of Nginx acting as a reverse proxy in front of a Laravel app container in a Docker environment&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Advanced use cases: the mesh approach&lt;/h2&gt;
&lt;p&gt;In complex microservices environments, the lines blur. Service meshes like Istio and Linkerd typically deploy a &quot;sidecar&quot; proxy next to every service, and each sidecar acts as both a forward and a reverse proxy depending on whether the traffic is ingress (incoming) or egress (outgoing).&lt;/p&gt;
&lt;p&gt;This allows for incredibly granular control over how services talk to each other. You can implement automatic retries, circuit breaking, and mutual TLS without writing a single line of application code. While this adds complexity, it provides the level of observability and reliability required for enterprise-grade systems.&lt;/p&gt;
&lt;h2&gt;Takeaways&lt;/h2&gt;
&lt;p&gt;Understanding which direction traffic flows is the first step toward building resilient systems. The points worth keeping:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Forward proxies protect the user.&lt;/strong&gt; Use them when you want to control how your internal network accesses the internet or to maintain user anonymity.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Reverse proxies protect the infrastructure.&lt;/strong&gt; Use them to scale your applications through load balancing, improve performance via caching, and secure your backend.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Direction matters.&lt;/strong&gt; A forward proxy handles outbound requests. A reverse proxy handles inbound requests.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Nginx is the industry standard.&lt;/strong&gt; It is the most common tool for implementing reverse proxies in &lt;a href=&quot;https://ansezz.com/blog/category/laravel/&quot;&gt;Laravel and Docker environments&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Security is centralized.&lt;/strong&gt; Both types of proxies allow you to centralize security policies rather than implementing them on every individual device or server.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you are managing a growing web application, you should focus on your reverse proxy configuration. It is the single most powerful tool in your DevOps kit for ensuring uptime and performance.&lt;/p&gt;
&lt;p&gt;Have you ever found yourself troubleshooting an &quot;IP mismatch&quot; error caused by a misconfigured proxy header?&lt;/p&gt;
</content:encoded><category>devops</category><category>networking</category><category>devops</category><category>infrastructure</category><category>security</category><category>docker</category></item><item><title>DNS vs service discovery</title><link>https://ansezz.com/blog/dns-vs-service-discovery/</link><guid isPermaLink="true">https://ansezz.com/blog/dns-vs-service-discovery/</guid><description>DNS was built for servers that live for years; microservices live for minutes. Why service discovery beats stale DNS records for high-churn systems.</description><pubDate>Sun, 21 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;You deploy a new version of your microservice and the old containers are killed instantly. You update your internal DNS records to point to the new IP addresses. However, for the next five minutes, your API gateway continues to send 40 percent of your traffic to the dead containers. Your logs are filled with connection timeouts and your error rate is spiking. This is the classic DNS staleness trap that plagues modern distributed systems.&lt;/p&gt;
&lt;p&gt;The problem lies in the fundamental design of how we locate resources. Traditional DNS was built for a world where servers lived for years. In a modern cloud environment, servers might only live for minutes. Relying on static naming systems to manage high-churn infrastructure creates a gap between where your traffic is going and where your healthy services actually live.&lt;/p&gt;
&lt;p&gt;This article explores the technical differences between &lt;strong&gt;DNS vs service discovery&lt;/strong&gt;, why the distinction matters for your uptime, and how to choose the right strategy for your &lt;a href=&quot;https://ansezz.com/blog/category/devops/&quot;&gt;DevOps architecture&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;DNS vs service discovery: defining the map and the radar&lt;/h2&gt;
&lt;p&gt;At its core, DNS is a distributed, hierarchical naming system. It acts like the Yellow Pages for the internet. You give it a hostname, and it returns an IP address. It is incredibly efficient and globally ubiquitous. Every programming language and operating system knows how to talk to it.&lt;/p&gt;
&lt;p&gt;Service discovery is a more specialized control plane. Instead of just a static map, it acts like a live radar system. It tracks the dynamic state of every service instance in your cluster. While DNS tells you where a service &lt;em&gt;should&lt;/em&gt; be, service discovery tells you where it &lt;em&gt;is&lt;/em&gt; right now.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/dns-vs-service-discovery/catalog-comparison.webp&quot; alt=&quot;Bento grid comparing a bare DNS record to a richer service discovery catalog entry with health status, version, and tags&quot; /&gt;&lt;/p&gt;
&lt;p&gt;A service discovery system like Consul or the internal registry in Kubernetes maintains a &quot;service catalog.&quot; This catalog is a live database of every running instance. When a new container starts, it registers itself with the catalog. When it shuts down, it is removed. This happens in milliseconds, far faster than traditional DNS propagation.&lt;/p&gt;
&lt;h2&gt;The TTL problem and DNS caching&lt;/h2&gt;
&lt;p&gt;The biggest hurdle in using DNS for microservices is Time-To-Live (TTL). DNS relies heavily on caching to prevent every single request from hammering the root name servers. Your operating system caches the result. Your browser caches the result. Your &lt;a href=&quot;https://ansezz.com/blog/api-gateway-ai-stack/&quot;&gt;API gateway&lt;/a&gt; caches the result.&lt;/p&gt;
&lt;p&gt;If you set a high TTL, your service changes are slow to propagate. If you set a very low TTL (like 0 or 1 second), you might overwhelm your DNS server with lookups. Even worse, some runtimes ignore the TTL entirely. The JVM, for example, caches a successful lookup for 30 seconds by default and forever when a security manager is set, so it can keep a stale IP until the process restarts.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;DNS&lt;/th&gt;
&lt;th&gt;Service Discovery&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Primary Goal&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Human-readable name to IP&lt;/td&gt;
&lt;td&gt;Dynamic instance tracking&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Propagation&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Slow (TTL dependent)&lt;/td&gt;
&lt;td&gt;Near-instant (Gossip/Streaming)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Metadata&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Very limited (TXT records)&lt;/td&gt;
&lt;td&gt;Rich (tags, version, region)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Client Support&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Native in all OS&lt;/td&gt;
&lt;td&gt;Often requires API/Agent&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Health Awareness&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;None (Static)&lt;/td&gt;
&lt;td&gt;Active monitoring&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Service discovery solves this by moving away from deep-level caching. Clients or load balancers often maintain a streaming connection to the registry. When a service instance moves, the registry pushes a notification to all subscribers. There is no waiting for a cache to expire. The system converges on the new state almost instantly.&lt;/p&gt;
&lt;h2&gt;Health awareness: the core differentiator&lt;/h2&gt;
&lt;p&gt;A standard DNS server is oblivious to the health of the IP addresses it returns. If a server rack loses power, the DNS record still points to those IPs. You have to manually update the record or run a custom script to change it.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/dns-vs-service-discovery/dns-caching-problem.webp&quot; alt=&quot;Comic-style scene of the DNS staleness trap: a client routed to a dead server because a cached, stale DNS record outlived the instance&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Service discovery systems treat health as a first-class citizen. They perform active health checks on every registered instance. These checks can be as simple as a TCP probe or as complex as a custom HTTP endpoint that checks database connectivity.&lt;/p&gt;
&lt;p&gt;If a health check fails, the instance is automatically marked as &quot;unhealthy&quot; in the registry. The discovery system then stops returning that IP address to clients. This provides a level of self-healing that is impossible with vanilla DNS, and it pairs naturally with the signals you already collect through &lt;a href=&quot;https://ansezz.com/blog/logging-vs-monitoring/&quot;&gt;logging and monitoring&lt;/a&gt;. If you are &lt;a href=&quot;https://ansezz.com/blog/coolify-docker-saas-hosting/&quot;&gt;hosting a SaaS on Docker&lt;/a&gt;, automated health-aware routing is the difference between a minor blip and a major outage.&lt;/p&gt;
&lt;h2&gt;Metadata and rich discovery&lt;/h2&gt;
&lt;p&gt;DNS is built to return a single type of data: an IP address. While you can use SRV records to include port numbers or TXT records for strings, the interface is clunky. It does not support complex queries.&lt;/p&gt;
&lt;p&gt;Service discovery allows for metadata-driven routing. You can ask the registry for &quot;all healthy instances of the &apos;orders&apos; service running version 2.1 in the &apos;us-east-1&apos; region.&quot; This unlocks advanced deployment patterns like:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Canary Deployments&lt;/strong&gt;: Routing only 5 percent of traffic to a specific version tag.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Blue/Green Deployments&lt;/strong&gt;: Swapping traffic between service sets by changing a metadata flag.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Locality-Aware Routing&lt;/strong&gt;: Directing traffic to the instance physically closest to the requester to reduce latency.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This level of granularity is essential for scaling complex systems where simple round-robin load balancing is not enough.&lt;/p&gt;
&lt;h2&gt;How modern orchestrators bridge the gap&lt;/h2&gt;
&lt;p&gt;You might notice that in &lt;a href=&quot;https://ansezz.com/blog/docker-vs-kubernetes/&quot;&gt;Kubernetes&lt;/a&gt;, you still use DNS names to talk to services. You might call &lt;code&gt;http://order-service.default.svc.cluster.local&lt;/code&gt;. Does this mean Kubernetes just uses DNS?&lt;/p&gt;
&lt;p&gt;Not exactly. Kubernetes uses a hybrid approach. It runs a service called CoreDNS, but CoreDNS is not backed by static zone files. It is plugged directly into the Kubernetes API. When a Pod starts or dies, the control plane updates the Service and EndpointSlice objects. CoreDNS watches those objects through the API and updates its responses in real time.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/dns-vs-service-discovery/health-check-code.webp&quot; alt=&quot;Bento card showing a code snippet for a service discovery health check configuration with an interval and HTTP endpoint&quot; /&gt;&lt;/p&gt;
&lt;p&gt;In this model, DNS is just the &quot;wire protocol.&quot; It is the interface that the application uses because it is easy and standard. However, the backend is a fully dynamic Service Discovery engine. This gives you the best of both worlds: the simplicity of DNS and the speed of Service Discovery.&lt;/p&gt;
&lt;h2&gt;Implementation strategies: when to use which?&lt;/h2&gt;
&lt;p&gt;Choosing between &lt;strong&gt;DNS vs service discovery&lt;/strong&gt; depends on your infrastructure scale and churn rate.&lt;/p&gt;
&lt;p&gt;If you are running a monolithic application on a few virtual machines that rarely change, DNS is perfectly fine. You can manage your records with simple automation or even manually. The complexity of a dedicated discovery system would outweigh the benefits.&lt;/p&gt;
&lt;p&gt;However, if you are using containers, serverless functions, or microservices, you need a dynamic solution. If your IPs change every time you deploy or scale, DNS will eventually break your traffic flow.&lt;/p&gt;
&lt;p&gt;For many developers, the best path is to use a tool that provides a DNS interface on top of a dynamic registry. HashiCorp Consul is a popular choice for this. It allows you to query the registry via a standard DNS lookup, but it handles the health checking and registration logic behind the scenes.&lt;/p&gt;
&lt;h2&gt;Takeaways&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;DNS is for stability&lt;/strong&gt;: Use it for external traffic, long-lived resources, and cross-company communication where caching is an advantage.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Service Discovery is for churn&lt;/strong&gt;: Use it for internal microservices, containers, and autoscaling groups where instances appear and disappear frequently.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Mind the Cache&lt;/strong&gt;: Never rely on vanilla DNS for sub-minute failover because client-side caching will almost always outlive your records.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Health is Binary&lt;/strong&gt;: Without active health checks, your discovery system is just a list of guesses. Always integrate automated liveness and readiness probes.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Hybrid is Best&lt;/strong&gt;: Use a discovery engine (like Kubernetes or Consul) that exposes a DNS interface. This simplifies your application code while keeping your infrastructure dynamic.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;When was the last time a stale DNS record caused a production incident in your environment? If you&apos;re untangling service routing for a high-churn stack, &lt;a href=&quot;https://ansezz.com/services/&quot;&gt;here&apos;s how I help teams ship it&lt;/a&gt;.&lt;/p&gt;
</content:encoded><category>devops</category><category>networking</category><category>kubernetes</category><category>microservices</category><category>devops</category><category>infrastructure</category></item><item><title>Bandwidth vs throughput: a wider pipe won&apos;t fix lag</title><link>https://ansezz.com/blog/bandwidth-vs-throughput/</link><guid isPermaLink="true">https://ansezz.com/blog/bandwidth-vs-throughput/</guid><description>Bandwidth is capacity, throughput is delivery. Why a wider pipe never fixes a throughput bottleneck, and the latency and protocol levers that do.</description><pubDate>Sat, 20 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;You pay for a 1 Gbps fiber line yet your API calls take 500ms to resolve and your database backups crawl at a fraction of that speed. Most engineering teams mistake capacity for performance. They assume that widening the pipe automatically solves the lag. This fundamental misunderstanding leads to over-provisioned cloud bills and under-performing applications. You are throwing money at a bandwidth problem when you actually have a throughput bottleneck.&lt;/p&gt;
&lt;p&gt;Bandwidth vs throughput is the difference between a theoretical promise and a practical delivery. In high-performance web development and Shopify ecosystems, knowing which one you are actually limited by is what lets you scale without overpaying.&lt;/p&gt;
&lt;h2&gt;The capacity promise of bandwidth&lt;/h2&gt;
&lt;p&gt;Bandwidth represents the maximum theoretical amount of data that can pass through a network path in a given time. Think of it as the number of lanes on a highway. If you have a twelve-lane highway, you have massive bandwidth. You have the potential to move thousands of cars simultaneously.&lt;/p&gt;
&lt;p&gt;In technical terms, bandwidth is a measure of capacity. It is usually expressed in bits per second (bps), Megabits per second (Mbps), or Gigabits per second (Gbps). When a cloud provider like Google Cloud or AWS quotes you a network speed, they are selling you bandwidth. It is a hard ceiling on how much data can move.&lt;/p&gt;
&lt;p&gt;However, bandwidth is passive. It does not account for the speed of the individual cars or whether there is a massive traffic jam at the exit ramp. You can have a 100 Gbps connection, but if the protocol or the destination server cannot keep up, that bandwidth remains largely unused.&lt;/p&gt;
&lt;h2&gt;The reality check of throughput&lt;/h2&gt;
&lt;p&gt;Throughput is the actual amount of data that successfully travels from point A to point B in a specific timeframe. If bandwidth is the number of lanes, throughput is the actual number of cars that arrive at the destination every minute.&lt;/p&gt;
&lt;p&gt;Throughput is almost always lower than bandwidth. Several factors degrade the actual flow of data. These include protocol overhead, network congestion, hardware limitations, and packet loss. While you might have a 1 Gbps link, your application might only achieve a throughput of 200 Mbps due to internal processing delays or inefficient code.&lt;/p&gt;
&lt;p&gt;Measuring throughput gives you the real picture of your system health. It tells you how your Shopify store or your Laravel backend is actually performing under load. If your throughput is significantly lower than your bandwidth, you are experiencing a bottleneck that more &quot;lanes&quot; will not fix.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/bandwidth-vs-throughput/metrics-comparison.webp&quot; alt=&quot;Bento grid contrasting bandwidth as link capacity, throughput as delivered data, and latency as round-trip delay&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;The invisible hand of latency&lt;/h2&gt;
&lt;p&gt;You cannot discuss bandwidth and throughput without addressing the elephant in the room: latency. Latency is the time it takes for a single packet of data to travel from the source to the destination and back. It is the &quot;lag&quot; you feel in a video call or a gaming session.&lt;/p&gt;
&lt;p&gt;High bandwidth does not equal low latency. Consider a geostationary satellite link. It may have massive bandwidth (great for downloading large files), but the round trip is roughly 500ms because the signal has to climb to ~35,786km and back. For a &quot;chatty&quot; application that fires many sequential calls, like a &lt;a href=&quot;https://ansezz.com/blog/picking-the-right-rag-stack/&quot;&gt;RAG-based AI system&lt;/a&gt; chaining retrieval and generation, high latency kills throughput.&lt;/p&gt;
&lt;p&gt;Every time your application waits for an acknowledgment from the server, it is sitting idle. Even if you have infinite bandwidth, your throughput is capped by how many round trips your data has to make. This is why optimizing for latency is often more impactful than upgrading your network plan.&lt;/p&gt;
&lt;h2&gt;Why bandwidth vs throughput matters for e-commerce&lt;/h2&gt;
&lt;p&gt;In a Shopify Plus environment, the gap between bandwidth and throughput directly impacts conversion rates. When a customer hits your store, their browser makes dozens of requests for images, scripts, and API data.&lt;/p&gt;
&lt;p&gt;If your images are unoptimized, they eat up bandwidth. If your Shopify apps are poorly coded, they introduce latency. The result is a drop in throughput. The data isn&apos;t getting to the customer fast enough. You can have the fastest hosting in the world, but if your &lt;a href=&quot;https://ansezz.com/blog/shopify-liquid-vs-headless/&quot;&gt;liquid templates&lt;/a&gt; are doing heavy lifting on every page load, the actual delivery speed stalls.&lt;/p&gt;
&lt;p&gt;For businesses scaling their digital presence, focusing on throughput means focusing on the user experience. It involves minimizing the payload size and reducing the number of round trips. It is about making sure the &quot;cars&quot; on your highway are moving at top speed and arriving without delay.&lt;/p&gt;
&lt;h2&gt;Bottlenecks in the Laravel and DevOps stack&lt;/h2&gt;
&lt;p&gt;In a typical Laravel environment managed with tools like Docker or &lt;a href=&quot;https://ansezz.com/blog/scaling-with-coolify/&quot;&gt;Coolify&lt;/a&gt;, throughput bottlenecks often hide in the database or the cache layer. You might have a 10 Gbps internal network between your app server and your database server. On paper, your bandwidth is huge.&lt;/p&gt;
&lt;p&gt;However, if your queries are not indexed or if you are fetching thousands of unnecessary rows, your throughput will tank. The network is fast, but the application is slow to process and deliver the data. This is where clean &lt;a href=&quot;https://ansezz.com/blog/category/architecture/&quot;&gt;architecture&lt;/a&gt; and efficient query design become network optimization tools.&lt;/p&gt;
&lt;p&gt;DevOps engineers also face throughput issues during CI/CD deployments. Moving a large Docker image across a network requires bandwidth. But the time it takes to unzip, layer, and verify that image is a throughput concern. Optimizing your Dockerfiles to reduce layer size is a direct way to improve the &quot;actual flow&quot; of your deployment pipeline.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/bandwidth-vs-throughput/network-dashboard.webp&quot; alt=&quot;Pop-art SaaS dashboard tracking bandwidth utilization against actual application throughput to spot the gap&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Optimizing for performance: a practical approach&lt;/h2&gt;
&lt;p&gt;Improving network performance requires a two-pronged strategy. You must manage your capacity and optimize your flow. Here are the technical levers you can pull:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Compression and minification&lt;/strong&gt;: Reducing the size of the data packets (the cars) allows more of them to fit through the pipe simultaneously. Use Gzip or Brotli for your web assets.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;CDN implementation&lt;/strong&gt;: Moving data closer to the user reduces latency. Shorter distances mean faster round trips and higher throughput, see &lt;a href=&quot;https://ansezz.com/blog/cdn-vs-cache/&quot;&gt;CDN vs cache&lt;/a&gt; for where each layer belongs.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Connection pooling&lt;/strong&gt;: Reusing existing connections for database or API calls eliminates the overhead of the &quot;three-way handshake&quot; required for every new TCP connection.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Asynchronous processing&lt;/strong&gt;: In Laravel, &lt;a href=&quot;https://ansezz.com/blog/scaling-with-rabbitmq/&quot;&gt;using queues&lt;/a&gt; allows you to handle heavy tasks in the background. This keeps your main application throughput high for the end-user.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Protocol upgrades&lt;/strong&gt;: HTTP/2 multiplexes many requests over one TCP connection. HTTP/3 goes further, running over QUIC to remove transport-level head-of-line blocking, so a single lost packet no longer stalls every other stream. On lossy mobile links the throughput gain is real.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Measuring the right metrics&lt;/h2&gt;
&lt;p&gt;Stop looking at your ISP&apos;s speed test as the sole measure of success. To truly understand your system, you need to monitor both bandwidth utilization and application throughput. Tools like Google Cloud Monitoring or Datadog provide granular insights into these metrics.&lt;/p&gt;
&lt;p&gt;Look for patterns where bandwidth is high but throughput is low. These gaps are where your technical debt lives. They represent inefficient protocols, unoptimized assets, or server-side delays. Closing this gap is how you achieve a &quot;snappy&quot; feel for your web applications.&lt;/p&gt;
&lt;p&gt;Architecture matters more than raw speed. A well-designed system on a moderate 100 Mbps link will often outperform a bloated system on a 1 Gbps link. Focus on the efficiency of the data flow rather than the width of the pipe.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/bandwidth-vs-throughput/architecture-diagram.webp&quot; alt=&quot;Architecture diagram showing the flow between Laravel, GraphQL, and Shopify&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Takeaways&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Bandwidth is capacity&lt;/strong&gt;: It is the maximum potential of your network link, not the actual speed of your data.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Throughput is reality&lt;/strong&gt;: It is the successful delivery rate of data, always limited by overhead and congestion.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Latency is the silent killer&lt;/strong&gt;: Even with high bandwidth, high latency will cap your throughput by forcing wait times.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Optimize the payload&lt;/strong&gt;: Smaller data sizes and fewer round trips are the most effective ways to increase throughput.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Monitor the gap&lt;/strong&gt;: Use dashboard metrics to identify when your actual performance falls far below your theoretical capacity.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Protocol choice matters&lt;/strong&gt;: A modern transport like HTTP/3 raises effective throughput over lossy links; query layers like GraphQL help separately by cutting over-fetching and round trips.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you had to choose between doubling your bandwidth or halving your latency for a real-time AI application, which would yield the better ROI for your specific infrastructure? If you&apos;re chasing a throughput bottleneck in production, &lt;a href=&quot;https://ansezz.com/services/&quot;&gt;here&apos;s how I help teams ship the fix&lt;/a&gt;.&lt;/p&gt;
</content:encoded><category>devops</category><category>networking</category><category>performance</category><category>devops</category><category>laravel</category><category>shopify</category></item><item><title>Container vs pod: the building blocks</title><link>https://ansezz.com/blog/container-vs-pod/</link><guid isPermaLink="true">https://ansezz.com/blog/container-vs-pod/</guid><description>A container is the package; a Pod is the execution environment. How Kubernetes Pods share networking and storage, and when sidecars earn their keep.</description><pubDate>Sat, 20 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;When you move from a simple Docker setup to an orchestrated environment like Kubernetes, the terminology starts to blur. You wonder why you can&apos;t just deploy a container directly, and why Kubernetes insists on wrapping your perfectly good Docker image in something called a Pod. The container vs Pod distinction is the first thing that trips people up.&lt;/p&gt;
&lt;p&gt;Treat a Pod as merely a &quot;fancy container&quot; and you miss the shared networking and storage that make Kubernetes powerful. You end up with monolithic containers that are hard to scale, or sidecars that can&apos;t actually reach the main application. Understanding how a container relates to a Pod is the difference between a brittle deployment and a scalable, resilient system.&lt;/p&gt;
&lt;h2&gt;The atomic unit: what is a container?&lt;/h2&gt;
&lt;p&gt;A container is a lightweight, standalone executable package. It includes everything needed to run an application: code, runtime, system tools, system libraries, and settings. At its core, a container is an isolated process running on a host operating system. It shares the host&apos;s kernel but maintains its own filesystem and environment variables.&lt;/p&gt;
&lt;p&gt;In the world of &lt;a href=&quot;https://ansezz.com/blog/coolify-docker-saas-hosting/&quot;&gt;DevOps and Docker&lt;/a&gt;, containers solved the &quot;it works on my machine&quot; problem. They provide a consistent environment from development to production. Whether you are running a Laravel backend or a Shopify app extension, the container remains the same. However, a container is inherently solitary. By default, it has its own IP address and its own isolated filesystem. Communication between two containers usually requires explicit networking configuration or external links.&lt;/p&gt;
&lt;h2&gt;The logical host: why Kubernetes needs Pods&lt;/h2&gt;
&lt;p&gt;Kubernetes does not manage containers directly. This is a common point of friction for beginners. Instead, it manages Pods. A Pod is the smallest deployable unit in Kubernetes. Think of a Pod as a &quot;logical host&quot; for one or more containers.&lt;/p&gt;
&lt;p&gt;The Pod acts as a wrapper. It provides a shared execution environment for the containers inside it. While most Pods only contain a single container, the ability to group multiple containers is a core feature. These containers are always co-located and co-scheduled. They run on the same physical or virtual machine within the cluster. This coupling allows them to behave as if they were processes running on the same server, despite being isolated in their own containers.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/container-vs-pod/pod-wrapper.webp&quot; alt=&quot;A pop-art comic diagram showing two distinct container characters inside a larger bubble representing a pod, connected by a phone line labeled &amp;quot;localhost&amp;quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Shared network and localhost: the magic of one IP&lt;/h2&gt;
&lt;p&gt;The most significant technical advantage of a Pod is the shared network namespace. Every Pod is assigned a unique IP address within the Kubernetes cluster. Every container inside that specific Pod shares this IP address.&lt;/p&gt;
&lt;p&gt;This shared network space changes how services communicate. In a standard Docker setup, Container A would need to know the IP of Container B to send a request. Inside a Kubernetes Pod, Container A can talk to Container B simply by using &lt;code&gt;localhost&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;This has a few technical implications:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Port management&lt;/strong&gt;: Since they share an IP, containers in the same Pod cannot bind to the same port. If one container uses port 8080, another container in that same Pod must use a different port.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Performance&lt;/strong&gt;: Communication over &lt;code&gt;localhost&lt;/code&gt; is extremely fast. There is no need for complex routing or service discovery between the containers within the same Pod.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Simplicity&lt;/strong&gt;: You can build modular systems where a helper container provides a service to the main application without exposing that service to the rest of the cluster.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Shared storage: passing files between containers&lt;/h2&gt;
&lt;p&gt;Just as they share a network, containers in a Pod can share storage. While a container&apos;s internal filesystem is ephemeral and isolated, a Pod can define shared Volumes. These volumes are mounted into the filesystem of every container that needs them.&lt;/p&gt;
&lt;p&gt;This is essential for applications that need to pass data between processes. For example, a main application might generate log files, while a separate log-processing container reads those files and ships them to a central server. Without the Pod abstraction, you would have to deal with complex network mounts or external storage providers. In a Pod, it is as simple as mounting a shared directory.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/container-vs-pod/yaml-dockerfile.webp&quot; alt=&quot;A bento-grid pop-art layout showing a clean Kubernetes Pod YAML code block alongside a Dockerfile on a dotted white background&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;The sidecar pattern: real-world multi-container Pods&lt;/h2&gt;
&lt;p&gt;The &quot;sidecar pattern&quot; is the primary reason we use multi-container Pods. It allows you to add functionality to an application without changing the application code itself. The &quot;sidecar&quot; container sits next to the &quot;main&quot; container and performs a supporting task.&lt;/p&gt;
&lt;p&gt;Common sidecar examples include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Logging agents&lt;/strong&gt;: A sidecar that watches a log file on a shared volume and sends the data to Elasticsearch or CloudWatch.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Proxies&lt;/strong&gt;: A service mesh sidecar (like Istio or Envoy) that handles all incoming and outgoing network traffic for the main application.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Config reloaders&lt;/strong&gt;: A sidecar that watches a configuration source (like a Git repo or ConfigMap) and signals the main application to reload when changes occur.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Security&lt;/strong&gt;: A sidecar that handles SSL/TLS termination so the main application only has to deal with plain HTTP.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;By separating these concerns into different containers within the same Pod, you keep your main application container clean and focused. This modularity is a pillar of &lt;a href=&quot;https://ansezz.com/blog/docker-vs-kubernetes/&quot;&gt;containers versus orchestration&lt;/a&gt; thinking.&lt;/p&gt;
&lt;p&gt;A practical note: historically you added a sidecar by listing a second entry under &lt;code&gt;spec.containers&lt;/code&gt;, which gave you no control over startup or shutdown order. Kubernetes now has first-class sidecar support — an init container with &lt;code&gt;restartPolicy: Always&lt;/code&gt; starts before the main containers, stays running for the Pod&apos;s lifetime, and shuts down after them. This feature reached stable in Kubernetes v1.33, so reach for native sidecars over plain co-containers when ordering matters.&lt;/p&gt;
&lt;h2&gt;Technical comparison: container vs Pod&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Standalone Container&lt;/th&gt;
&lt;th&gt;Containers in a Pod&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Unit of Deployment&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Single process image&lt;/td&gt;
&lt;td&gt;Kubernetes Pod object&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Networking&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Unique IP per container&lt;/td&gt;
&lt;td&gt;Unique IP per Pod (shared)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Localhost Access&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Isolated to the container&lt;/td&gt;
&lt;td&gt;Shared across all containers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Storage&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Isolated filesystem&lt;/td&gt;
&lt;td&gt;Shared Volumes available&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Scheduling&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Manual or via runtime&lt;/td&gt;
&lt;td&gt;Automatic by K8s scheduler&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Lifecycle&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Tied to the process&lt;/td&gt;
&lt;td&gt;Tied to the Pod&apos;s status&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Scaling&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Scale individual instances&lt;/td&gt;
&lt;td&gt;Scale the entire Pod unit&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;DevOps deployment: GCP and AWS context&lt;/h2&gt;
&lt;p&gt;When you move your workloads to managed cloud providers like Google Cloud (GKE) or AWS (EKS), the container vs Pod distinction becomes operational. These platforms manage the infrastructure, but you still define the Pod specifications.&lt;/p&gt;
&lt;p&gt;The line gets blurrier on serverless platforms. EKS on Fargate still runs real Pods, so multi-container Pods and sidecars work exactly as they do on a self-managed node — Fargate just provisions the compute per Pod instead of per node. Cloud Run started as single-container only, but since May 2023 it supports multi-container deployments where sidecars share the network namespace and communicate over &lt;code&gt;localhost&lt;/code&gt;, just like a Pod. So you no longer have to jump to full Kubernetes the moment you need a sidecar — though you do once you need &lt;a href=&quot;https://ansezz.com/blog/serverless-vs-containers/&quot;&gt;richer orchestration&lt;/a&gt; like DaemonSets, complex scheduling, or fine-grained networking.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/container-vs-pod/k8s-dashboard.webp&quot; alt=&quot;A pop-art-style Kubernetes dashboard UI screenshot listing Pods with green &amp;quot;Running&amp;quot; status icons on a light dotted background&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Takeaways&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;A container is the packaging format, while a Pod is the execution environment.&lt;/li&gt;
&lt;li&gt;Containers in a Pod share the same IP address and can communicate via &lt;code&gt;localhost&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Shared Volumes allow containers within a Pod to exchange data through the filesystem.&lt;/li&gt;
&lt;li&gt;The sidecar pattern uses multiple containers in one Pod to separate concerns like logging and security.&lt;/li&gt;
&lt;li&gt;Kubernetes schedules and scales Pods as a single unit, ensuring all containers land on the same node.&lt;/li&gt;
&lt;li&gt;Understanding Pods is crucial for managing complex deployments on GCP, AWS, or &lt;a href=&quot;https://ansezz.com/blog/coolify-self-hosted-saas/&quot;&gt;self-hosted tools like Coolify&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;At what point does the overhead of managing a multi-container Pod outweigh the benefits of process separation in your current infrastructure? If you&apos;re architecting workloads that have outgrown plain Docker, &lt;a href=&quot;https://ansezz.com/services/&quot;&gt;here&apos;s how I help teams ship it&lt;/a&gt;.&lt;/p&gt;
</content:encoded><category>devops</category><category>kubernetes</category><category>docker</category><category>devops</category><category>infrastructure</category></item><item><title>AI vs machine learning: an engineering deep dive</title><link>https://ansezz.com/blog/ai-vs-machine-learning/</link><guid isPermaLink="true">https://ansezz.com/blog/ai-vs-machine-learning/</guid><description>AI is the umbrella, machine learning is the engine. See how the distinction shapes your RAG pipelines, agentic systems, and Laravel or Shopify architecture.</description><pubDate>Fri, 19 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;The tech industry often treats &quot;AI&quot; and &quot;machine learning&quot; as interchangeable. This confusion leads to real architectural debt when teams build intelligent features without understanding the underlying mechanics. You end up trying to build a predictive engine out of static rule-based systems or, conversely, over-engineering a simple decision tree with a neural network.&lt;/p&gt;
&lt;p&gt;Misidentifying the right tool produces bloated cloud bills, slow performance, and systems that fail to generalize to real-world data. When you conflate the umbrella field of artificial intelligence with the specific mechanics of machine learning, you lose the ability to choose the most efficient path for your product.&lt;/p&gt;
&lt;p&gt;This guide draws the line between the two concepts and shows how they manifest in modern stacks like Laravel and Shopify, with a focus on RAG and agentic systems. The goal: a clear framework for deciding when to reach for rule-based logic and when to deploy a learned model.&lt;/p&gt;
&lt;h2&gt;The AI umbrella: broad intelligent systems&lt;/h2&gt;
&lt;p&gt;Artificial Intelligence (AI) is the broad field of creating systems capable of performing tasks that typically require human intelligence. This is the &quot;umbrella&quot; term. It encompasses everything from simple &quot;if-then&quot; logic to the most complex large language models (LLMs).&lt;/p&gt;
&lt;p&gt;In engineering terms, AI is about the system&apos;s behavior. If a software system can reason, plan, or solve problems, it is an AI system. This does not mean it has to &quot;learn.&quot; Early AI systems, often referred to as symbolic AI or expert systems, relied on hardcoded rules and logic gates. These systems are highly predictable and easy to debug because every decision path is explicitly defined.&lt;/p&gt;
&lt;p&gt;For example, a sophisticated shipping calculator in a Shopify Plus store that adjusts prices based on complex regional taxes and carrier API responses is a form of narrow AI. It mimics human decision-making based on a set of logical parameters. However, it is not machine learning because it does not improve its performance based on past data unless a developer manually updates the code.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/ai-vs-machine-learning/ai-ml-deep-learning.webp&quot; alt=&quot;Nested-circles diagram showing deep learning inside machine learning inside the broader field of AI&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Machine learning: the engine of pattern recognition&lt;/h2&gt;
&lt;p&gt;Machine Learning (ML) is a subset of AI that focuses on the development of algorithms that allow computers to learn from and make predictions based on data. Instead of being explicitly programmed to perform a task, an ML model is &quot;trained&quot; on a dataset to find patterns and hidden structures.&lt;/p&gt;
&lt;p&gt;When you are building a product that needs to handle high-dimensional data where rules are too complex to write by hand, ML is the solution. Think about image recognition or fraud detection. You cannot write enough &quot;if&quot; statements to account for every possible pixel arrangement in a photo of a cat. Instead, you feed an ML model thousands of images, and it learns the statistical representation of a &quot;cat.&quot;&lt;/p&gt;
&lt;p&gt;In a production environment, ML involves a lifecycle that is distinct from traditional software development. It requires data collection, cleaning, feature engineering, model training, and evaluation. You can read more about how this differs from standard workflows in our post on &lt;a href=&quot;https://ansezz.com/blog/ai-vs-traditional-development/&quot;&gt;AI vs traditional development&lt;/a&gt;. For the deeper discriminative-vs-generative comparison inside ML itself, see &lt;a href=&quot;https://ansezz.com/blog/ml-vs-genai/&quot;&gt;machine learning vs generative AI&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;AI vs machine learning in RAG systems&lt;/h2&gt;
&lt;p&gt;The distinction becomes critical when you move into modern AI Engineering, particularly with Retrieval-Augmented Generation (RAG). A RAG system is a perfect example of an AI system that incorporates multiple ML components to achieve a goal.&lt;/p&gt;
&lt;p&gt;In a RAG pipeline, the system behavior is the AI part. The orchestration of how a user query is received, how a search is performed against a vector database, and how the results are fed into a prompt is a design problem. The components doing the heavy lifting, however, are ML models.&lt;/p&gt;
&lt;h3&gt;Embedding models and vector databases&lt;/h3&gt;
&lt;p&gt;The &quot;retrieval&quot; part of RAG relies on embedding models. These are ML models (specifically deep learning models) that transform text into high-dimensional vectors. When you store these vectors in a tool like pgvector or a dedicated vector database, you are using the output of a machine learning process.&lt;/p&gt;
&lt;h3&gt;The generator (LLM)&lt;/h3&gt;
&lt;p&gt;The &quot;generation&quot; part is handled by a large language model. This is a massive ML model trained on trillions of tokens of text. While you interact with it via an API or a local deployment, the model itself is a static file of weights learned through intensive training.&lt;/p&gt;
&lt;p&gt;When you refine your RAG system, you are often doing AI engineering by changing the chunking strategy or the system prompt. You are only doing ML engineering if you decide to fine-tune the embedding model or the LLM itself on your domain data — a trade-off covered in &lt;a href=&quot;https://ansezz.com/blog/rag-vs-fine-tuning/&quot;&gt;RAG vs fine-tuning&lt;/a&gt;. Understanding these layers helps prevent &lt;a href=&quot;https://ansezz.com/blog/7-rag-mistakes-production/&quot;&gt;common RAG mistakes in production&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/ai-vs-machine-learning/rag-architecture.webp&quot; alt=&quot;RAG architecture diagram showing documents flowing into a vector database and retrieved context feeding an LLM&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Agentic systems and autonomous workflows&lt;/h2&gt;
&lt;p&gt;Agentic systems take the AI vs machine learning comparison a step further. An &quot;agent&quot; is an AI system that uses an ML model as its &quot;brain&quot; to determine a sequence of actions toward a goal — the jump from a static model to an autonomous one is the focus of &lt;a href=&quot;https://ansezz.com/blog/llm-vs-ai-agent/&quot;&gt;LLM vs AI agent&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;In an agentic workflow, the ML model (the LLM) is just one tool in the agent&apos;s belt. The agent might also have access to a search engine, a calculator, or a database. The logic that handles the &quot;loop&quot; (Observe -&amp;gt; Think -&amp;gt; Act) is the AI system design.&lt;/p&gt;
&lt;p&gt;For developers working with Shopify, &lt;a href=&quot;https://ansezz.com/blog/agentic-commerce-shopify/&quot;&gt;agentic commerce&lt;/a&gt; allows for autonomous customer service or inventory management. The agent uses machine learning to understand the customer&apos;s intent (NLP) but uses traditional software APIs to execute a refund or check stock levels.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// A conceptual example of an AI Agent &apos;thought&apos; process in a Laravel controller
public function handleAgentRequest(Request $request)
{
    $userInput = $request-&amp;gt;input(&apos;query&apos;);

    // ML Component: Identify intent using an LLM
    $intent = $this-&amp;gt;llmService-&amp;gt;identifyIntent($userInput);

    // AI System Logic: Choose the tool based on intent
    if ($intent === &apos;check_order_status&apos;) {
        return $this-&amp;gt;orderService-&amp;gt;getStatus($request-&amp;gt;user());
    }

    // AI System Logic: Fallback to RAG if intent is informational
    return $this-&amp;gt;ragService-&amp;gt;queryKnowledgeBase($userInput);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Practical implementation: Laravel and Shopify contexts&lt;/h2&gt;
&lt;p&gt;Integrating AI and ML into existing frameworks like Laravel or Shopify requires a clear understanding of where the data lives and how the models are served.&lt;/p&gt;
&lt;h3&gt;Laravel and ML orchestration&lt;/h3&gt;
&lt;p&gt;Laravel is an excellent framework for building the &quot;AI system&quot; around an ML model. You can use Laravel&apos;s robust queue system to handle long-running ML tasks or use its HTTP client to communicate with Python-based ML microservices. Many developers use Laravel to manage the &quot;human-in-the-loop&quot; aspects of AI, such as reviewing model outputs before they are published.&lt;/p&gt;
&lt;h3&gt;Shopify and AI&lt;/h3&gt;
&lt;p&gt;Shopify has integrated AI features directly into its platform via Shopify Magic. However, for custom Shopify Plus builds, you might implement your own ML-driven product recommendation engine. This involves capturing user behavior data, training a model (likely off-platform on Google Cloud or AWS), and serving those recommendations via a custom app or a Liquid block.&lt;/p&gt;
&lt;h2&gt;Cloud infrastructure for AI and machine learning&lt;/h2&gt;
&lt;p&gt;Deploying these systems requires different infrastructure strategies. AI systems (the orchestration code) can usually run on standard web servers or serverless functions. Machine learning models, especially for training or high-throughput inference, often require GPUs or specialized hardware.&lt;/p&gt;
&lt;p&gt;Using Docker and tools like Coolify can simplify the deployment of these modular systems. You might host your Laravel application on a standard VPS while your vector database and ML inference server run in separate containers. For more on this, check out our guide on &lt;a href=&quot;https://ansezz.com/blog/coolify-docker-saas-hosting/&quot;&gt;Coolify and Docker for SaaS hosting&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Monitoring is also different. For an AI system, you monitor latency and error rates. For an ML model, you monitor &quot;drift&quot; (when the real-world data starts to look different from the training data) and accuracy.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/ai-vs-machine-learning/monitoring-dashboard.webp&quot; alt=&quot;SaaS dashboard panels tracking AI system latency and error rates next to ML model drift and accuracy&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Takeaways&lt;/h2&gt;
&lt;p&gt;Distinguishing between AI and machine learning is not just about semantics. It is about choosing the right architecture and team skill sets for your project.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;AI is the &quot;what&quot;:&lt;/strong&gt; It describes the goal of creating intelligent behavior. It includes both hardcoded logic and learned models.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Machine Learning is the &quot;how&quot;:&lt;/strong&gt; It is a specific method for achieving AI by training models on data.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;RAG and agents are hybrid systems:&lt;/strong&gt; They use ML components (LLMs, embeddings) inside an AI system designed with code and prompts.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Framework integration matters:&lt;/strong&gt; Laravel is great for AI orchestration, while Shopify Plus provides a robust environment for ML-driven commerce.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Infrastructure needs differ:&lt;/strong&gt; AI code is lightweight. ML models require specialized environments for training and inference.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Are you building a system that needs to follow strict regulatory rules, or one that needs to adapt to unpredictable user behavior?&lt;/p&gt;
</content:encoded><category>ai</category><category>ai</category><category>machine-learning</category><category>rag</category><category>agentic-ai</category><category>laravel</category><category>shopify</category></item><item><title>Training vs inference: scaling AI systems</title><link>https://ansezz.com/blog/training-vs-inference/</link><guid isPermaLink="true">https://ansezz.com/blog/training-vs-inference/</guid><description>How training and inference differ in compute, cost, and hardware, and how to architect each phase so your AI app stays fast and affordable at scale.</description><pubDate>Thu, 18 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Most engineering teams treat AI models as a single black box. They allocate a massive GPU budget and hope for the best. But when the application hits production, latency spikes, costs spiral out of control, and the &quot;intelligent&quot; features start timing out. This happens because developers fail to distinguish between the two distinct phases of an AI lifecycle: training and inference. Without a clear understanding of how these stages consume resources, your AI strategy is essentially a shot in the dark.&lt;/p&gt;
&lt;h2&gt;The anatomy of training: learning from data&lt;/h2&gt;
&lt;p&gt;Training is the &quot;learning&quot; phase of a machine learning model. During this stage, you feed an algorithm a massive dataset. The model looks at the data, makes a guess, compares its guess to the actual answer, and adjusts its internal parameters (weights) to get closer to the truth next time. This process is repeated millions or even billions of times.&lt;/p&gt;
&lt;p&gt;The primary goal of training is to minimize error. Because this requires massive matrix multiplications and constant backpropagation, it is extremely compute-intensive. You are effectively asking a computer to solve a giant calculus problem over and over again. This is why training typically happens on large clusters of high-end GPUs or TPUs.&lt;/p&gt;
&lt;p&gt;Training is usually a burst-heavy, high-upfront-cost activity. You might spend two weeks and $50,000 to train a custom model for your specific industry. Once the model reaches a satisfactory level of accuracy, the training phase ends. The model&apos;s weights are &quot;frozen,&quot; and it is ready to be used in the real world.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/training-vs-inference/training-flow.webp&quot; alt=&quot;Data processing flow showing books and files entering a brain-shaped machine in pop-art style&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;The power of inference: applying knowledge&lt;/h2&gt;
&lt;p&gt;Inference is the &quot;execution&quot; phase. This is what happens when a user interacts with your application. A customer types a query into your Shopify store&apos;s AI assistant, the model processes that query using its frozen weights, and it returns an answer. No learning happens during inference. The model is simply applying what it already knows.&lt;/p&gt;
&lt;p&gt;In technical terms, inference is a &quot;forward pass.&quot; You provide an input, the data flows through the layers of the model, and an output is generated. There is no backpropagation and no weight updates. This makes inference much faster and less compute-intensive than training on a per-request basis.&lt;/p&gt;
&lt;p&gt;However, inference is where the &quot;unbounded cost&quot; problem lives. While training is a one-time or periodic expense, inference happens every time a user makes a request. If you have a million users making ten queries a day, you are running ten million inferences. Over the lifetime of a successful product, inference costs often dwarf training costs by a factor of 10x or more.&lt;/p&gt;
&lt;h2&gt;The classroom metaphor: student vs. graduate&lt;/h2&gt;
&lt;p&gt;To simplify these complex technical ideas, think of a student in medical school.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Training is the years of study.&lt;/strong&gt; The student reads thousands of textbooks, attends lectures, and takes practice exams. This process is slow, expensive, and requires intense focus. The &quot;parameters&quot; of the student&apos;s brain are being adjusted as they learn how to diagnose diseases.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Inference is the doctor in the clinic.&lt;/strong&gt; A patient walks in with symptoms. The doctor uses their existing knowledge to provide a diagnosis. The doctor doesn&apos;t go back to medical school for every patient. They simply apply the patterns they have already learned. The diagnosis happens in minutes, not years.&lt;/p&gt;
&lt;p&gt;If you are building an &lt;a href=&quot;https://ansezz.com/blog/ai-vs-traditional-development/&quot;&gt;AI vs traditional development&lt;/a&gt; strategy, you need to decide if you are training a new doctor (custom training) or simply hiring one that already exists (using a pre-trained model via API).&lt;/p&gt;
&lt;h2&gt;Hardware and infrastructure: GPUs vs. the rest&lt;/h2&gt;
&lt;p&gt;The hardware requirements for these two phases are fundamentally different. Understanding this can save you thousands in infrastructure costs.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Training&lt;/th&gt;
&lt;th&gt;Inference&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Primary Goal&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;High Throughput&lt;/td&gt;
&lt;td&gt;Low Latency&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Compute Pattern&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Burst-heavy / Parallel&lt;/td&gt;
&lt;td&gt;Steady / Sequential&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Hardware&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Multi-GPU Clusters (H100, A100)&lt;/td&gt;
&lt;td&gt;Single GPU, CPU, or Edge (T4, L4, Apple Silicon)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Optimization&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Gradient Descent / Backprop&lt;/td&gt;
&lt;td&gt;Quantization / Model Compression&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Cost Type&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;CAPEX (Upfront)&lt;/td&gt;
&lt;td&gt;OPEX (Recurring)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;For training, you need massive VRAM and high-speed interconnects (like NVLink) between GPUs. For inference, you often prioritize energy efficiency and &quot;cost per token.&quot; In many cases, a well-optimized model can run inference on standard CPUs or specialized &quot;edge&quot; chips, which is much cheaper than maintaining a fleet of high-end GPUs. If you manage your own servers, you can split the workloads: put training tasks on high-perf clusters and move inference to smaller, distributed nodes closer to your users.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/training-vs-inference/hardware-dashboard.webp&quot; alt=&quot;Pop-art comic style dashboard showing training throughput versus inference latency&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Integrating inference into Laravel applications&lt;/h2&gt;
&lt;p&gt;When building custom web solutions, you usually deal with the inference side of the equation. You aren&apos;t training a frontier model from scratch. You are calling an API or a self-hosted model to perform a task. Here is a typical pattern for handling AI inference inside a Laravel controller.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Http;

class AIInferenceController extends Controller
{
    /**
     * Handle an AI inference request.
     */
    public function generate(Request $request)
    {
        $prompt = $request-&amp;gt;input(&apos;prompt&apos;);

        // We use a high-performance inference endpoint
        // This could be OpenAI, Anthropic, or a self-hosted vLLM server
        $response = Http::withHeaders([
            &apos;Authorization&apos; =&amp;gt; &apos;Bearer &apos; . config(&apos;services.ai.key&apos;),
        ])-&amp;gt;post(&apos;https://api.inference-provider.com/v1/completions&apos;, [
            &apos;model&apos; =&amp;gt; &apos;llama-3-70b&apos;,
            &apos;prompt&apos; =&amp;gt; $prompt,
            &apos;max_tokens&apos; =&amp;gt; 150,
            &apos;temperature&apos; =&amp;gt; 0.7,
        ]);

        if ($response-&amp;gt;successful()) {
            return response()-&amp;gt;json([
                &apos;result&apos; =&amp;gt; $response-&amp;gt;json(&apos;choices.0.text&apos;),
                &apos;latency&apos; =&amp;gt; $response-&amp;gt;header(&apos;X-Inference-Time&apos;),
            ]);
        }

        return response()-&amp;gt;json([&apos;error&apos; =&amp;gt; &apos;Inference failed&apos;], 500);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This simple setup highlights a key architectural point: the &lt;a href=&quot;https://ansezz.com/blog/api-gateway-ai-stack/&quot;&gt;API Gateway for your AI stack&lt;/a&gt; must be able to handle the specific latency requirements of inference. Users expect a response in milliseconds, not minutes.&lt;/p&gt;
&lt;h2&gt;The role of RAG: inference with a memory&lt;/h2&gt;
&lt;p&gt;One way to bridge the gap between training and inference is retrieval-augmented generation (RAG). Instead of re-training a model every time your data changes (which is expensive and slow), you provide the model with &quot;context&quot; during the inference phase. That tradeoff is its own decision, covered in &lt;a href=&quot;https://ansezz.com/blog/rag-vs-fine-tuning/&quot;&gt;RAG vs fine-tuning&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;In a RAG system, you search a vector database for relevant information and &quot;stuff&quot; it into the prompt. The model then uses its pre-trained reasoning capabilities to answer based on that new data. This is inference acting like it has a temporary memory. However, be careful. If you don&apos;t optimize your vector search, your inference latency will explode. You can read more about avoiding &lt;a href=&quot;https://ansezz.com/blog/7-rag-mistakes-production/&quot;&gt;RAG mistakes in production&lt;/a&gt; to keep your systems lean.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/training-vs-inference/inference-production.webp&quot; alt=&quot;AI model in production showing a chat bubble going into a model box and producing a lightbulb result&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Takeaways&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Training is about learning.&lt;/strong&gt; It is a one-time or periodic compute-heavy process that builds the model&apos;s intelligence.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Inference is about acting.&lt;/strong&gt; It is the real-time application of that intelligence and represents the majority of long-term costs.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Hardware matters.&lt;/strong&gt; Don&apos;t use a massive GPU cluster for inference if a smaller, quantized model can run on a single T4 or even a CPU.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Optimize for your phase.&lt;/strong&gt; If you are training, focus on throughput (tokens per second). If you are serving users, focus on latency (time to first token).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;RAG is your friend.&lt;/strong&gt; It allows you to give &quot;new knowledge&quot; to a frozen model during inference without the massive cost of re-training.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;At what point in your product&apos;s growth do you anticipate inference costs will exceed your initial development budget? If you&apos;re architecting an AI stack that has to stay fast and affordable at scale, &lt;a href=&quot;https://ansezz.com/services/&quot;&gt;here&apos;s how I help teams ship it&lt;/a&gt;.&lt;/p&gt;
</content:encoded><category>ai</category><category>llm</category><category>llm-inference</category><category>devops</category><category>rag</category><category>infrastructure</category><category>ai-engineering</category></item><item><title>Vector search vs graph search for RAG</title><link>https://ansezz.com/blog/vector-search-vs-graph-search/</link><guid isPermaLink="true">https://ansezz.com/blog/vector-search-vs-graph-search/</guid><description>Compare vector search and graph search for RAG. When to use embeddings via pgvector vs relationship-based knowledge graphs — and why GraphRAG often wins.</description><pubDate>Thu, 18 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Retrieval-Augmented Generation (RAG) forces a question most teams put off: how is data actually retrieved? Most production systems start with vector search because it is accessible and effective for basic similarity. But as queries get more complex, the limits of semantic similarity show. A system that finds &quot;similar&quot; things often fails when asked to explain &quot;connected&quot; things. That gap is where the choice between vector search and graph search decides whether your RAG strategy holds up.&lt;/p&gt;
&lt;p&gt;Relying solely on vector embeddings can lead to a phenomenon known as the semantic trap. You might retrieve five document chunks that all mention a specific topic, but if those chunks do not explicitly link to each other, the LLM will struggle to synthesize a multi-hop answer. This often results in hallucinations or incomplete reasoning. If your RAG pipeline cannot traverse relationships, it is essentially working with a deck of shuffled cards rather than an organized map.&lt;/p&gt;
&lt;p&gt;The fix starts with understanding the trade-offs between vector-based and relationship-based retrieval. Vector search excels at high-dimensional similarity; graph search offers structured traversals that preserve the logic of your data. Combining the two into a hybrid architecture is usually the most robust path forward.&lt;/p&gt;
&lt;h2&gt;Understanding vector search and embeddings&lt;/h2&gt;
&lt;p&gt;Vector search operates by converting unstructured data into dense numerical arrays called embeddings. These embeddings represent the semantic meaning of the text in a multi-dimensional space. When a user submits a query, that query is also converted into a vector. The system then calculates the mathematical distance between the query vector and the stored vectors using algorithms like cosine similarity or Euclidean distance.&lt;/p&gt;
&lt;p&gt;For developers working within the Laravel or Node.js ecosystems, tools like pgvector have made this incredibly accessible. By adding a vector column to a standard Postgres database, you can perform similarity searches directly alongside your relational data. This approach is highly efficient for &quot;fuzzy&quot; matching. It can find a product description for a &quot;crimson summer dress&quot; even if the query only mentions a &quot;red lightweight gown.&quot;&lt;/p&gt;
&lt;p&gt;However, vector search is fundamentally limited by its lack of structural awareness. It treats every chunk of data as an independent point in space. It has no inherent understanding that &quot;User A&quot; is the &quot;CEO&quot; of &quot;Company B&quot; unless those specific words are clustered together in a single text chunk.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/vector-search-vs-graph-search/vector-embeddings.webp&quot; alt=&quot;A multi-dimensional vector embedding space with clustered points&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;The mechanics of graph search&lt;/h2&gt;
&lt;p&gt;Graph search takes a completely different approach by modeling data as nodes (entities) and edges (relationships). Instead of looking for similarity in a coordinate system, graph search traverses defined paths. A node might represent a person, a place, or a concept, while an edge defines how they interact. For example, an edge labeled &quot;works_at&quot; might connect a person node to a company node.&lt;/p&gt;
&lt;p&gt;This structure allows for multi-hop queries that are nearly impossible for pure vector search. You can ask the system to &quot;find all employees at companies that use Laravel and were founded after 2015.&quot; A graph database like Neo4j or a graph-capable extension can follow these links precisely. In the context of RAG, this means the retrieval engine can pull in a coherent chain of facts rather than a disjointed set of similar-sounding paragraphs.&lt;/p&gt;
&lt;p&gt;The challenge with graph search is the ingestion process. Unlike vector search, which only requires a simple embedding model, graph search requires entity extraction and schema mapping. You must identify which entities exist in your text and how they relate to one another before they can be stored in the graph. This adds complexity to your data pipeline but pays dividends in reasoning accuracy.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/vector-search-vs-graph-search/knowledge-graph.webp&quot; alt=&quot;A knowledge graph of nodes and labeled relationship edges&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Comparison: vector vs graph search&lt;/h2&gt;
&lt;p&gt;Choosing the right approach depends on the nature of your data and the questions your users are asking. The table below outlines the primary technical differences.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Vector Search (pgvector)&lt;/th&gt;
&lt;th&gt;Graph Search (Knowledge Graph)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Data Model&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;High-dimensional dense vectors&lt;/td&gt;
&lt;td&gt;Nodes, edges, and properties&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Query Type&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;K-Nearest Neighbors (k-NN)&lt;/td&gt;
&lt;td&gt;Traversals and pattern matching&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Strengths&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Semantic similarity, fuzzy match&lt;/td&gt;
&lt;td&gt;Complex relationships, multi-hop logic&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Weaknesses&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Struggles with structural reasoning&lt;/td&gt;
&lt;td&gt;High ingestion complexity&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Ideal For&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;FAQ bots, general search, broad Q&amp;amp;A&lt;/td&gt;
&lt;td&gt;Fraud detection, supply chain, reasoning&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Scaling&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Efficient with ANN indexing (HNSW)&lt;/td&gt;
&lt;td&gt;Sensitive to graph density and depth&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;For a deeper look at avoiding common pitfalls in this space, see our guide on &lt;a href=&quot;https://ansezz.com/blog/7-rag-mistakes-production/&quot;&gt;7 RAG mistakes in production&lt;/a&gt;. Many teams find that they start with vectors and only introduce graphs when they hit a &quot;reasoning wall.&quot;&lt;/p&gt;
&lt;h2&gt;GraphRAG: the hybrid evolution&lt;/h2&gt;
&lt;p&gt;The most advanced RAG systems are moving toward a hybrid model often referred to as GraphRAG. This architecture does not choose one over the other. Instead, it uses both to provide the LLM with a richer context. In a GraphRAG setup, the system first performs a vector search to identify relevant starting points in the knowledge base. Once those starting points are found, it uses graph traversals to pull in related entities and contextual relationships.&lt;/p&gt;
&lt;p&gt;Imagine a medical research application. A vector search might find a paper about a specific drug. The graph search then identifies the chemical compounds in that drug, the clinical trials associated with it, and the known side effects reported in other related papers. The resulting context provided to the LLM is a structured &quot;subgraph&quot; of knowledge. This significantly reduces the risk of hallucinations because the LLM is working with explicitly linked facts.&lt;/p&gt;
&lt;p&gt;Implementing this hybrid approach requires a solid DevOps foundation. Tools like Docker and Coolify can help manage the multiple services involved, including your vector store, graph database, and the extraction services that keep them in sync. If you are exploring how to host these complex stacks, our article on &lt;a href=&quot;https://ansezz.com/blog/coolify-self-hosted-saas/&quot;&gt;Coolify and self-hosted SaaS&lt;/a&gt; provides a good starting point for infrastructure management.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/vector-search-vs-graph-search/graphrag-performance.webp&quot; alt=&quot;A GraphRAG pipeline combining vector discovery with graph context expansion&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Technical implementation with pgvector and SQL&lt;/h2&gt;
&lt;p&gt;For teams already using Postgres, pgvector is the logical starting point for adding vector capabilities. It integrates seamlessly into existing SQL workflows. You can store your embeddings in a &lt;code&gt;vector&lt;/code&gt; column and use the &lt;code&gt;&amp;lt;-&amp;gt;&lt;/code&gt; (Euclidean) or &lt;code&gt;&amp;lt;=&amp;gt;&lt;/code&gt; (cosine) operators to query them.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;-- Example: Finding similar document chunks in Postgres
SELECT content, 1 - (embedding &amp;lt;=&amp;gt; &apos;[0.1, 0.2, 0.3, ...]&apos;) AS similarity
FROM document_chunks
WHERE 1 - (embedding &amp;lt;=&amp;gt; &apos;[0.1, 0.2, 0.3, ...]&apos;) &amp;gt; 0.8
ORDER BY similarity DESC
LIMIT 5;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To approximate a graph without a dedicated graph database, you can model simple one-to-many or many-to-many relationships with relational joins. But as the number of hops grows, recursive joins and &lt;code&gt;WITH RECURSIVE&lt;/code&gt; queries get expensive fast, and the planner struggles to keep them efficient. That is the point to introduce a specialized graph layer such as Neo4j or the Apache AGE extension, which adds openCypher graph queries directly to Postgres. For the broader question of which retrieval components to assemble, see &lt;a href=&quot;https://ansezz.com/blog/picking-the-right-rag-stack/&quot;&gt;picking the right RAG stack&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Choosing your path&lt;/h2&gt;
&lt;p&gt;The decision between vector and graph search is not binary. It is a spectrum of technical complexity versus reasoning capability. For simple semantic search over a collection of PDFs, vector search with pgvector is almost always the right answer. It is fast, easy to implement, and requires minimal maintenance.&lt;/p&gt;
&lt;p&gt;If your application requires high precision, multi-step logic, or the ability to explain &quot;why&quot; a result was chosen, the investment in a knowledge graph is necessary. The architectural overhead of building an extraction pipeline is the price you pay for a system that truly understands the relationships within your data.&lt;/p&gt;
&lt;p&gt;Most production-grade AI systems will eventually land in the middle. They will use vectors for broad discovery and graphs for deep reasoning. By designing your system with a modular approach today, you can ensure that your infrastructure is ready to evolve as your AI needs grow.&lt;/p&gt;
&lt;h2&gt;Takeaways&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Vector search is best for semantic similarity and is easy to implement using tools like pgvector.&lt;/li&gt;
&lt;li&gt;Graph search excels at multi-hop reasoning and mapping complex relationships between entities.&lt;/li&gt;
&lt;li&gt;Vector search treats data points as isolated, while graph search treats them as connected nodes.&lt;/li&gt;
&lt;li&gt;GraphRAG is a hybrid approach that uses vector search for discovery and graph search for context expansion.&lt;/li&gt;
&lt;li&gt;Start with vector search for most projects, but prepare for graph search if your queries require complex structural reasoning.&lt;/li&gt;
&lt;li&gt;Monitor retrieval metrics like recall and precision to spot when a vector-only strategy starts failing.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;How is your current RAG pipeline handling multi-hop queries that connect data points across documents? If you&apos;re building one for production, &lt;a href=&quot;https://ansezz.com/services/&quot;&gt;here&apos;s how I help teams ship it&lt;/a&gt;.&lt;/p&gt;
</content:encoded><category>ai</category><category>rag</category><category>pgvector</category><category>vector-search</category><category>architecture</category><category>ai</category></item><item><title>Terraform vs Ansible: the real difference in DevOps</title><link>https://ansezz.com/blog/terraform-vs-ansible/</link><guid isPermaLink="true">https://ansezz.com/blog/terraform-vs-ansible/</guid><description>Provisioning vs configuration management: the real difference between Terraform and Ansible, declarative vs procedural logic, and the hybrid workflow.</description><pubDate>Wed, 17 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;You spend six hours manually clicking through the AWS console to set up a VPC, three subnets, and a load balancer, only to realize you missed a single security group rule that breaks the entire staging environment. Then you have to do it all over again for production. This manual toil is a ticking time bomb for technical debt and human error. Infrastructure as Code (IaC) is the industry standard solution, but the market is split between two titans: Terraform and Ansible. Choosing the wrong one for the wrong task leads to fragile pipelines and &quot;snowflake&quot; servers that no one dares to touch.&lt;/p&gt;
&lt;h2&gt;Terraform vs Ansible: provisioning vs configuration&lt;/h2&gt;
&lt;p&gt;The most common misunderstanding in DevOps is treating Terraform and Ansible as interchangeable tools. They are not. At their core, they solve two distinct stages of the infrastructure lifecycle.&lt;/p&gt;
&lt;p&gt;Terraform is a &lt;strong&gt;provisioning tool&lt;/strong&gt;. Its primary job is to talk to cloud providers like AWS, Google Cloud, or Azure to create the virtualized &quot;hardware&quot; of your stack: VPCs, subnets, IAM roles, and managed databases. It builds the foundation your software will live on.&lt;/p&gt;
&lt;p&gt;Ansible is a &lt;strong&gt;configuration management&lt;/strong&gt; tool. Once the server exists, Ansible takes over to install Nginx, configure the PHP-FPM pool, or deploy your latest Laravel build. It is designed to manage the internal state of the operating system and the applications running on it.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/terraform-vs-ansible/provisioning.webp&quot; alt=&quot;Infrastructure provisioning illustrated as a pop-art bento grid&quot; /&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Terraform&lt;/th&gt;
&lt;th&gt;Ansible&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Primary Goal&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Provisioning infrastructure&lt;/td&gt;
&lt;td&gt;Configuration management&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Language&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;HCL (declarative)&lt;/td&gt;
&lt;td&gt;YAML (procedural/imperative)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;State&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Managed (state file)&lt;/td&gt;
&lt;td&gt;Stateless&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Architecture&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Client-only (API-based)&lt;/td&gt;
&lt;td&gt;Agentless (SSH/WinRM)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Best For&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;VPCs, clusters, databases&lt;/td&gt;
&lt;td&gt;Installing apps, OS hardening&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;Declarative vs procedural logic&lt;/h2&gt;
&lt;p&gt;The way you talk to these tools defines how you manage your stack. Terraform is strictly &lt;strong&gt;declarative&lt;/strong&gt;. You describe the &quot;end state&quot; you want. If you tell Terraform you want five EC2 instances, it looks at your current environment. If you have three, it adds two. If you have seven, it deletes two. You don&apos;t tell it &lt;em&gt;how&lt;/em&gt; to do it; you tell it what you want the result to be.&lt;/p&gt;
&lt;p&gt;Ansible is primarily &lt;strong&gt;procedural&lt;/strong&gt; (or imperative), though it uses declarative modules. You write playbooks that list a series of steps:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Update apt-get.&lt;/li&gt;
&lt;li&gt;Install Docker.&lt;/li&gt;
&lt;li&gt;Copy the config file.&lt;/li&gt;
&lt;li&gt;Restart the service.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;While Ansible modules are idempotent (meaning they won&apos;t re-install Docker if it&apos;s already there), the workflow follows a specific sequence of commands. This makes Ansible exceptionally good at complex application deployments where the order of operations matters.&lt;/p&gt;
&lt;h2&gt;Immutable vs mutable infrastructure&lt;/h2&gt;
&lt;p&gt;The choice between Terraform vs Ansible often dictates your architectural philosophy. Terraform leans heavily toward &lt;strong&gt;immutable infrastructure&lt;/strong&gt;. In an immutable world, you don&apos;t &quot;fix&quot; a server. If you need to change the instance type or update the base OS, you destroy the old server and provision a new one from a fresh image. This is the same philosophy behind &lt;a href=&quot;https://ansezz.com/blog/docker-vs-kubernetes/&quot;&gt;containers and orchestration&lt;/a&gt;, and it eliminates &quot;configuration drift&quot; where servers that started identical become different over time due to manual patches.&lt;/p&gt;
&lt;p&gt;Ansible is the king of &lt;strong&gt;mutable infrastructure&lt;/strong&gt;. It is designed to go into existing, long-lived servers and modify them. This is often necessary for legacy systems or complex environments where spinning up a fresh cluster for every minor config change is too slow or expensive. For teams using self-hosted solutions like &lt;a href=&quot;https://ansezz.com/blog/coolify-docker-saas-hosting/&quot;&gt;Coolify for SaaS hosting&lt;/a&gt;, Ansible can be a powerful ally in managing the underlying VPS environment.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/terraform-vs-ansible/configuration-management.webp&quot; alt=&quot;Configuration management and software installation in pop-art style&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;State management: the source of truth&lt;/h2&gt;
&lt;p&gt;Terraform&apos;s superpower (and its greatest complexity) is the &lt;strong&gt;state file&lt;/strong&gt;. This JSON file acts as a map of your real-world infrastructure. When you run a command, Terraform compares your HCL code against this state file to determine what needs to change. This allows Terraform to detect &quot;drift&quot;: when someone manually changes a setting in the AWS console, Terraform will see the discrepancy and offer to revert it.&lt;/p&gt;
&lt;p&gt;Ansible is &lt;strong&gt;stateless&lt;/strong&gt;. It doesn&apos;t keep a record of what it did yesterday. It simply connects to the IP addresses in your inventory and attempts to execute the playbook. If a server is down, Ansible reports a failure but doesn&apos;t have a &quot;global view&quot; of your infrastructure health in the same way Terraform does. This makes Ansible easier to start with but harder to use for tracking the total lifecycle of high-level cloud resources.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/terraform-vs-ansible/state-management.webp&quot; alt=&quot;Terraform state management illustration in pop-art style&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Better together: the hybrid workflow&lt;/h2&gt;
&lt;p&gt;In a professional &lt;a href=&quot;https://ansezz.com/blog/category/devops/&quot;&gt;DevOps&lt;/a&gt; pipeline, you rarely choose just one. The most robust engineering teams use a &quot;best of breed&quot; approach.&lt;/p&gt;
&lt;p&gt;A typical workflow looks like this:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Terraform&lt;/strong&gt; provisions the network (VPC), the security groups, and the base EC2 instances using a clean Ubuntu AMI.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Terraform&lt;/strong&gt; outputs the IP addresses of those new instances.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Ansible&lt;/strong&gt; picks up those IPs and runs a playbook to install the LEMP stack, configure SSL certificates, and tune the firewall.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;By separating the &lt;strong&gt;cloud fabric&lt;/strong&gt; (Terraform) from the &lt;strong&gt;software layer&lt;/strong&gt; (Ansible), you create a modular system that is easy to debug and scale. If you need to move from AWS to Google Cloud, you rewrite your Terraform providers but keep your Ansible playbooks largely the same. Wiring both stages into a single &lt;a href=&quot;https://ansezz.com/blog/ci-vs-cd/&quot;&gt;CI/CD&lt;/a&gt; pipeline turns this into a one-command, reproducible build.&lt;/p&gt;
&lt;p&gt;One 2026 caveat worth flagging: HashiCorp moved Terraform to the Business Source License in August 2023, and the community forked it into &lt;a href=&quot;https://opentofu.org/&quot;&gt;OpenTofu&lt;/a&gt; (now a CNCF project under the Linux Foundation). OpenTofu is a near drop-in replacement using the same HCL syntax and state format, so &quot;Terraform&quot; in this article applies equally to it. IBM completed its acquisition of HashiCorp in February 2025, which has only sharpened that licensing question for teams.&lt;/p&gt;
&lt;h2&gt;Takeaways&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Terraform&lt;/strong&gt; is for building the house (infrastructure). It is declarative, immutable, and stateful.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Ansible&lt;/strong&gt; is for decorating and maintaining the house (software configuration). It is procedural, mutable, and stateless.&lt;/li&gt;
&lt;li&gt;Use &lt;strong&gt;Terraform&lt;/strong&gt; to manage resources with a clear lifecycle, like databases and load balancers.&lt;/li&gt;
&lt;li&gt;Use &lt;strong&gt;Ansible&lt;/strong&gt; to manage the &quot;inside&quot; of a VM, such as package updates and application deployments.&lt;/li&gt;
&lt;li&gt;Avoid using Ansible to provision cloud resources; while possible, it lacks the sophisticated state management and dependency graphing of Terraform.&lt;/li&gt;
&lt;li&gt;Combine both tools in a CI/CD pipeline for a fully automated, reproducible environment.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you had to rebuild your entire production environment from scratch in 30 minutes, would your current automation tools be enough to restore both the infrastructure and the application state?&lt;/p&gt;
</content:encoded><category>devops</category><category>devops</category><category>infrastructure</category></item><item><title>SRE vs Platform Engineer: who to hire for scale</title><link>https://ansezz.com/blog/sre-vs-platform-engineer/</link><guid isPermaLink="true">https://ansezz.com/blog/sre-vs-platform-engineer/</guid><description>SRE vs Platform Engineer compared: reliability versus developer experience, what each role owns, and which one your team needs to hire first.</description><pubDate>Tue, 16 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Your production environment is down and the error budget is burning. You have two choices. You can hire someone to fix the immediate fire and ensure it never happens again. Or you can hire someone to build the tools that prevent your developers from starting that fire in the first place. This is the fundamental tension between Site Reliability Engineering (SRE) and Platform Engineering.&lt;/p&gt;
&lt;p&gt;For years, &quot;DevOps&quot; was the catch-all term for anyone who touched a server. But as systems have grown in complexity, the roles have fractured into specialized disciplines. SRE and Platform Engineering are the two most prominent results of this evolution. While they often share tools like Kubernetes, Terraform, and Docker, their missions are worlds apart. One focuses on the runtime reality of the user. The other focuses on the internal experience of the developer.&lt;/p&gt;
&lt;h2&gt;The SRE: the guardian of production&lt;/h2&gt;
&lt;p&gt;Site Reliability Engineering is what happens when you ask a software engineer to design an operations function. Originally pioneered by Google, SRE is a discipline that treats operations as a software problem. The primary mission of an SRE is simple but difficult. They must ensure that production services are reliable, scalable, and performant.&lt;/p&gt;
&lt;p&gt;SREs live and breathe metrics. They define Service Level Indicators (SLIs) and Service Level Objectives (SLOs) to quantify what &quot;up&quot; actually means. If a service has a 99.9% availability target, the SRE manages the &quot;error budget.&quot; This is the remaining 0.1% of downtime allowed before the team must stop shipping features and focus entirely on stability.&lt;/p&gt;
&lt;p&gt;The daily life of an SRE involves on-call rotations, incident response, and post-mortem analysis. They don&apos;t just fix bugs. They build automation to eliminate &quot;toil.&quot; Toil is the manual, repetitive work like scaling clusters or rotating certificates that scales linearly with the size of the system. By writing code to automate these tasks, an SRE ensures that the infrastructure can grow without requiring a proportional increase in headcount.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/sre-vs-platform-engineer/sre-slo-dashboard.webp&quot; alt=&quot;SRE reliability and SLO dashboard tracking error budgets and latency&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;The Platform Engineer: architect of the golden path&lt;/h2&gt;
&lt;p&gt;Platform Engineering is the practice of building and operating internal developer platforms (IDPs). If the SRE is focused on the end-user, the Platform Engineer is focused on the internal developer. Their goal is to improve Developer Experience (DX) by abstracting away the complexity of the underlying infrastructure.&lt;/p&gt;
&lt;p&gt;A Platform Engineer builds &quot;Golden Paths.&quot; These are standardized, self-service workflows that allow a developer to go from &quot;code on my laptop&quot; to &quot;running in production&quot; without needing to open a ticket for the infrastructure team. Instead of every developer learning the nuances of AWS IAM roles or Kubernetes ingress controllers, they use a platform that handles these details automatically.&lt;/p&gt;
&lt;p&gt;The success of a Platform Engineer is measured by lead time to production and developer satisfaction. They treat the platform as a product. They conduct user research with their internal developers to find friction points. They build CI/CD pipelines, environment provisioning tools, and internal portals that make shipping software feel effortless. You can see similar patterns in how tools like &lt;a href=&quot;https://ansezz.com/blog/coolify-docker-saas-hosting/&quot;&gt;Coolify simplify SaaS hosting&lt;/a&gt; by providing a cohesive management layer.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/sre-vs-platform-engineer/platform-engineering-dx.webp&quot; alt=&quot;Platform engineering developer experience and infrastructure abstraction&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;The core divergence: metrics and customers&lt;/h2&gt;
&lt;p&gt;The easiest way to distinguish these roles is to look at who they serve and how they are measured.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;SRE&lt;/th&gt;
&lt;th&gt;Platform Engineer&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Primary Customer&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;End users &amp;amp; product teams&lt;/td&gt;
&lt;td&gt;Internal developers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Success Metric&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;SLOs, latency, MTTR&lt;/td&gt;
&lt;td&gt;Lead time, deployment frequency&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Key Focus&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Production reliability&lt;/td&gt;
&lt;td&gt;Developer productivity&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Artifacts&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Alerting rules, runbooks, SLOs&lt;/td&gt;
&lt;td&gt;IDPs, CLI tools, CI/CD templates&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;An SRE cares about the &quot;now.&quot; Is the site up? Is the latency within limits? If a database query takes 500ms instead of the usual 50ms, the SRE is the first person to notice. They are the frontline defenders of the business&apos;s reputation.&lt;/p&gt;
&lt;p&gt;A Platform Engineer cares about the &quot;how.&quot; How long does it take for a new hire to ship their first line of code? How many manual steps are in the deployment process? They are the industrial engineers of the software factory. They optimize the assembly line so that everyone else can move faster. This role is increasingly important as we move toward &lt;a href=&quot;https://ansezz.com/blog/agentic-commerce-shopify/&quot;&gt;agentic commerce and automated Shopify workflows&lt;/a&gt;, where the speed of iteration is a competitive advantage.&lt;/p&gt;
&lt;h2&gt;A Formula 1 metaphor&lt;/h2&gt;
&lt;p&gt;Imagine a Formula 1 team. The SRE is the pit crew and the race engineer. They are monitoring the car in real-time during the race. They check tire pressure, fuel levels, and engine temperature. If something goes wrong on lap 30, they are the ones making split-second decisions to keep the car on the track. They manage the &quot;reliability&quot; of the race.&lt;/p&gt;
&lt;p&gt;The Platform Engineer is the engineer back at the factory who designed the car and the tools used to build it. They created the wind tunnel, the simulation software, and the standardized parts that make the car fast and safe. They ensure that the driver and the pit crew have the best possible equipment to do their jobs. Without the factory engineer, the car wouldn&apos;t be fast. Without the race engineer, the car wouldn&apos;t finish the race.&lt;/p&gt;
&lt;h2&gt;The tools of the trade&lt;/h2&gt;
&lt;p&gt;While both roles use similar technologies, they apply them differently.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;SRE tooling:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Observability:&lt;/strong&gt; Prometheus, Grafana, Datadog, Honeycomb.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Incident management:&lt;/strong&gt; PagerDuty, Opsgenie.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Resilience:&lt;/strong&gt; Chaos Mesh, Gremlin.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Automation:&lt;/strong&gt; Python, Go, Ansible.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Platform engineering tooling:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Internal portals:&lt;/strong&gt; Backstage, Port, Cortex.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Infrastructure as Code:&lt;/strong&gt; Terraform, Pulumi, Crossplane.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;CI/CD:&lt;/strong&gt; GitHub Actions, GitLab CI, ArgoCD.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Developer environments:&lt;/strong&gt; Loft, DevPod, Docker.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The SRE uses observability tools to find &quot;unknown unknowns&quot; in production. The Platform Engineer uses those same tools to bake monitoring defaults into the platform. If you want to dive deeper into the technical stack for modern applications, check our guide on the &lt;a href=&quot;https://ansezz.com/blog/api-gateway-ai-stack/&quot;&gt;API gateway for the AI stack&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Better together: the synergy loop&lt;/h2&gt;
&lt;p&gt;In a high-performing organization, these two roles form a powerful feedback loop. The SRE discovers a recurring failure mode during an incident. Perhaps developers are forgetting to set proper resource limits on their containers. This causes nodes to run out of memory.&lt;/p&gt;
&lt;p&gt;Instead of just telling developers to &quot;be more careful,&quot; the SRE brings this feedback to the Platform Engineer. The Platform Engineer then updates the &quot;Golden Path&quot; templates to include sensible default resource limits automatically. Now, every new service created on the platform is &quot;reliable by design.&quot;&lt;/p&gt;
&lt;p&gt;This synergy reduces the SRE&apos;s operational load. They spend less time fighting fires and more time on high-level architecture. Simultaneously, the developers are happier because the platform prevents them from making common mistakes. This is the pinnacle of &lt;a href=&quot;https://ansezz.com/blog/category/devops/&quot;&gt;modern DevOps maturity&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/sre-vs-platform-engineer/feedback-loop.webp&quot; alt=&quot;SRE and Platform Engineering feedback loop turning incidents into golden-path defaults&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Which one do you need?&lt;/h2&gt;
&lt;p&gt;The choice depends on your current pain points.&lt;/p&gt;
&lt;p&gt;If your developers are constantly struggling to set up environments, fighting with CI/CD pipelines, or waiting weeks for infrastructure tickets, you need a &lt;strong&gt;Platform Engineer&lt;/strong&gt;. You have a productivity bottleneck. You need to productize your infrastructure to allow your team to scale.&lt;/p&gt;
&lt;p&gt;If your site is frequently down, your performance is unpredictable, and your developers are terrified of shipping on a Friday, you need an &lt;strong&gt;SRE&lt;/strong&gt;. You have a reliability bottleneck. You need someone to instill the discipline of SLOs and incident management into your culture.&lt;/p&gt;
&lt;p&gt;In many startups, a single &quot;DevOps Engineer&quot; handles both. But as you grow, the split is inevitable. Specialized roles allow for deeper expertise. An SRE who doesn&apos;t have to build CI/CD pipelines can focus entirely on making the system indestructible. A Platform Engineer who isn&apos;t on-call for every production blip can focus entirely on making the developer experience world-class.&lt;/p&gt;
&lt;h3&gt;Takeaways&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;SRE focuses on the reliability of production systems for end users.&lt;/li&gt;
&lt;li&gt;Platform Engineering focuses on the developer experience and internal tooling.&lt;/li&gt;
&lt;li&gt;SRE uses SLOs and error budgets to balance speed and stability.&lt;/li&gt;
&lt;li&gt;Platform Engineering uses &quot;Golden Paths&quot; to provide self-service infrastructure.&lt;/li&gt;
&lt;li&gt;The two roles are complementary. SRE provides the feedback, and Platform Engineering provides the abstractions.&lt;/li&gt;
&lt;li&gt;Both roles are essential for scaling modern, complex software organizations.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;How does your team handle the balance between shipping fast and staying up?&lt;/p&gt;
</content:encoded><category>career</category><category>devops</category><category>infrastructure</category><category>kubernetes</category><category>observability</category><category>career</category></item><item><title>Synchronous vs asynchronous communication</title><link>https://ansezz.com/blog/synchronous-vs-asynchronous-communication/</link><guid isPermaLink="true">https://ansezz.com/blog/synchronous-vs-asynchronous-communication/</guid><description>The technical differences between synchronous and asynchronous architectures in Laravel and Shopify, and how queues, jobs, and webhooks help you scale.</description><pubDate>Tue, 16 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Every developer has faced the dreaded &quot;spinning wheel of death.&quot; You click a button, the browser hangs, and you wait five seconds for a confirmation that never feels fast enough. This lag usually stems from a synchronous process holding the entire request-response cycle hostage while it waits for a third-party API or a heavy database query. In high-traffic environments, these blocking calls are not just a nuisance. They are a scalability killer that can bring down your entire infrastructure.&lt;/p&gt;
&lt;p&gt;Architecting for scale requires a deep understanding of when to keep things synchronous and when to move them to the background. This decision impacts everything from user experience to server costs. By shifting heavy lifting to asynchronous workers, you decouple your system. This allows your application to handle thousands of concurrent users without breaking a sweat.&lt;/p&gt;
&lt;h2&gt;The blocking nature of synchronous communication&lt;/h2&gt;
&lt;p&gt;Synchronous communication is the traditional request-response model. When a client sends a request, it waits for the server to process the logic and return a response. This is a &quot;blocking&quot; operation. The execution thread is tied up until the task completes.&lt;/p&gt;
&lt;p&gt;Think of it like a phone call. You dial a number, wait for the other person to pick up, and you cannot do anything else until the conversation is over. In web development, this is perfectly fine for simple operations like fetching a user profile or updating a single database row. The latency is minimal and the user needs the data immediately to continue.&lt;/p&gt;
&lt;p&gt;However, synchronous flows become dangerous when you introduce external dependencies. If your Laravel controller calls the Shopify Admin API to update 500 products during a standard HTTP request, your PHP worker is stuck. If Shopify takes three seconds to respond, that worker cannot serve any other users for those three seconds. Under heavy load, your worker pool will exhaust itself. This leads to 504 Gateway Timeout errors and a broken experience.&lt;/p&gt;
&lt;h2&gt;The fire-and-forget power of asynchronous logic&lt;/h2&gt;
&lt;p&gt;Asynchronous communication decouples the request from the processing. The client sends a message or triggers an event and immediately receives a response. The actual work happens &quot;out of band&quot; in a separate process or at a later time.&lt;/p&gt;
&lt;p&gt;This is more like sending an email. You hit send and immediately go back to your day. The recipient reads and processes the message whenever they are available. In a technical stack, this is achieved using message queues like Redis or Amazon SQS, or a dedicated broker such as &lt;a href=&quot;https://ansezz.com/blog/scaling-with-rabbitmq/&quot;&gt;RabbitMQ&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/synchronous-vs-asynchronous-communication/laravel-queue-architecture.webp&quot; alt=&quot;Laravel job queueing architecture showing dispatched jobs flowing through Redis to workers&quot; /&gt;&lt;/p&gt;
&lt;p&gt;When you move a task to an asynchronous worker, the user gets an instant &quot;Success&quot; message. The heavy processing happens in the background. This architecture is essential for:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Sending transactional emails.&lt;/li&gt;
&lt;li&gt;Generating PDF reports or data exports.&lt;/li&gt;
&lt;li&gt;Communicating with third-party APIs like Shopify or Stripe.&lt;/li&gt;
&lt;li&gt;Running complex AI agents or RAG pipelines.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Laravel queues and background jobs&lt;/h2&gt;
&lt;p&gt;Laravel provides a robust implementation of asynchronous processing through its Queue system. Instead of performing a slow task in your controller, you dispatch a &quot;Job.&quot;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// Synchronous (Bad for slow tasks)
public function store(Request $request) {
    $order = Order::create($request-&amp;gt;all());
    Mail::to($user)-&amp;gt;send(new OrderConfirmed($order)); // Waits for SMTP
    return response()-&amp;gt;json($order);
}

// Asynchronous (Scalable)
public function store(Request $request) {
    $order = Order::create($request-&amp;gt;all());
    ProcessOrderEmail::dispatch($order); // Returns immediately
    return response()-&amp;gt;json($order);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;By using &lt;code&gt;dispatch()&lt;/code&gt;, you push a payload into Redis. A separate process, managed by a tool like &lt;a href=&quot;https://laravel.com/docs/horizon&quot;&gt;Laravel Horizon&lt;/a&gt;, picks up the job and executes it. This keeps your web-facing workers free to handle more incoming traffic. If the email server is down, the job stays in the queue and retries later. This level of resilience is impossible in a strictly synchronous world.&lt;/p&gt;
&lt;h2&gt;Shopify webhooks: the event-driven standard&lt;/h2&gt;
&lt;p&gt;For e-commerce developers, understanding asynchronous communication is mandatory when working with Shopify. Shopify uses webhooks to notify your application of events like &lt;code&gt;orders/create&lt;/code&gt; or &lt;code&gt;products/update&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Webhooks are fundamentally asynchronous. Shopify does not wait for your app to process the entire order. It sends the POST request and expects a &lt;code&gt;2xx&lt;/code&gt; response within five seconds. If your code tries to run complex inventory logic or sync data to an ERP synchronously within that webhook route, you risk timing out. Shopify then retries the failed delivery up to eight times over roughly four hours, which can mean duplicate processing if your handler is not idempotent. Worse, if your endpoint keeps failing, Shopify removes the webhook subscription entirely, and you stop receiving events until you re-register it.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/synchronous-vs-asynchronous-communication/shopify-webhook-integration.webp&quot; alt=&quot;Shopify webhook delivery feeding a SaaS dashboard integration&quot; /&gt;&lt;/p&gt;
&lt;p&gt;The best practice for &lt;a href=&quot;https://ansezz.com/blog/agentic-commerce-shopify/&quot;&gt;agentic commerce on Shopify&lt;/a&gt; is to receive the webhook, validate the signature, dispatch a Laravel Job, and return the response immediately. This ensures your app stays responsive and Shopify remains happy with your delivery rates.&lt;/p&gt;
&lt;h2&gt;Infrastructure impact and DevOps considerations&lt;/h2&gt;
&lt;p&gt;Moving to an asynchronous architecture changes how you manage your infrastructure. In a synchronous world, you scale your web servers to handle peak traffic. In an asynchronous world, you scale your workers.&lt;/p&gt;
&lt;p&gt;Tools like &lt;a href=&quot;https://ansezz.com/blog/coolify-docker-saas-hosting/&quot;&gt;Coolify and Docker&lt;/a&gt; make it easier to manage these separate services. You can run your web container on one set of resources and your worker containers on another. If you have a massive spike in background jobs, you can spin up more workers without affecting the performance of your main website.&lt;/p&gt;
&lt;p&gt;For cross-service communication, Google Cloud Tasks is a strong alternative to a self-hosted queue. It dispatches HTTP callbacks to any endpoint with built-in rate limiting, scheduling, and retries. This is particularly useful when building an &lt;a href=&quot;https://ansezz.com/blog/api-gateway-ai-stack/&quot;&gt;API gateway for AI stacks&lt;/a&gt;, where different microservices need to talk to each other without blocking the main user flow.&lt;/p&gt;
&lt;h2&gt;When to choose sync vs async&lt;/h2&gt;
&lt;p&gt;Choosing the right pattern depends on the user&apos;s expectations and the reliability of the task.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Synchronous&lt;/th&gt;
&lt;th&gt;Asynchronous&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;User Experience&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Immediate feedback required.&lt;/td&gt;
&lt;td&gt;Background processing is okay.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Data Integrity&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Result needed for next step.&lt;/td&gt;
&lt;td&gt;Eventual consistency is okay.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Reliability&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Fails if the connection breaks.&lt;/td&gt;
&lt;td&gt;Retries automatically on failure.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Complexity&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Simple code structure.&lt;/td&gt;
&lt;td&gt;Requires queue management and workers.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Example Use Case&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Login validation, viewing a cart.&lt;/td&gt;
&lt;td&gt;Order fulfillment, image processing.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3&gt;Use synchronous when:&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;The operation is extremely fast (under 100ms).&lt;/li&gt;
&lt;li&gt;The user cannot proceed without the result of the operation.&lt;/li&gt;
&lt;li&gt;You are performing a simple read/write to your primary database.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Use asynchronous when:&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;You are calling an external API.&lt;/li&gt;
&lt;li&gt;The task takes more than 200ms to complete.&lt;/li&gt;
&lt;li&gt;The task can fail and needs to be retried.&lt;/li&gt;
&lt;li&gt;You are performing bulk operations on large datasets.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Takeaways&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/synchronous-vs-asynchronous-communication/takeaways.webp&quot; alt=&quot;Technical architecture takeaways summarized as a neobrutalist checklist&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Designing for high performance means moving away from a single-threaded mindset. By embracing asynchronous communication, you build systems that are more reliable and easier to scale.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Identify bottlenecks&lt;/strong&gt;: Use tools like Laravel Telescope or OpenTelemetry to find slow controller actions.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Offload everything&lt;/strong&gt;: If it doesn&apos;t need to happen &lt;em&gt;right now&lt;/em&gt;, push it to a queue.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Handle webhooks fast&lt;/strong&gt;: Always return a 200 response to Shopify immediately and process the logic later.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Monitor your workers&lt;/strong&gt;: Use dashboards like Laravel Horizon to track queue health and failure rates.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Design for retries&lt;/strong&gt;: Ensure your background jobs are idempotent, meaning they can run multiple times without causing side effects.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;How are you currently handling long-running API tasks in your Laravel or Shopify application?&lt;/p&gt;
</content:encoded><category>architecture</category><category>laravel</category><category>shopify</category><category>devops</category><category>architecture</category><category>messaging</category></item><item><title>Solutions architect vs forward deployed engineer</title><link>https://ansezz.com/blog/solutions-architect-vs-forward-deployed-engineer/</link><guid isPermaLink="true">https://ansezz.com/blog/solutions-architect-vs-forward-deployed-engineer/</guid><description>Solutions architect vs forward deployed engineer: one owns the blueprint, the other owns the outcome. Who carries delivery risk, and which you need.</description><pubDate>Mon, 15 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Engineering is no longer just about writing code in a dark room. It is about where that code sits in the customer&apos;s value chain — and the difference between a solutions architect and a forward deployed engineer decides who is accountable when it lands there.&lt;/p&gt;
&lt;p&gt;Many organizations invest millions in complex software platforms like Shopify Plus or custom Laravel ecosystems only to realize they cannot bridge the gap between a sales demo and a running production system. The implementation phase becomes a graveyard of missed requirements and technical debt. Sales cycles stall because the &quot;how&quot; is missing. This is exactly where the two roles diverge.&lt;/p&gt;
&lt;p&gt;While both roles sit at the intersection of engineering and customer success, they solve fundamentally different problems. One builds the blueprint to ensure the house will stand. The other moves into the construction site to ensure the pipes actually connect to the city main. Understanding which role fits your project — or your career — requires a deep dive into the mechanics of technical delivery.&lt;/p&gt;
&lt;h2&gt;The design blueprint: defining the Solutions Architect&lt;/h2&gt;
&lt;p&gt;The Solutions Architect is the strategic engine of a technical engagement. Their primary goal is to design a scalable, low-risk implementation architecture that maps customer business requirements to technical capabilities. They operate in the space between a &quot;yes&quot; from the buyer and a &quot;go-live&quot; from the developers.&lt;/p&gt;
&lt;p&gt;An architect focuses on the macro. They evaluate how a new &lt;a href=&quot;https://ansezz.com/blog/category/laravel/&quot;&gt;Laravel development&lt;/a&gt; project will integrate with existing legacy databases or how a &lt;a href=&quot;https://ansezz.com/blog/category/shopify/&quot;&gt;Shopify app&lt;/a&gt; will interact with a third-party ERP. Their deliverables are not usually production pull requests. Instead, they produce sequence diagrams, entity-relationship diagrams (ERDs), and security compliance documents.&lt;/p&gt;
&lt;p&gt;They spend their days in design workshops and advisory conversations. They must convince a CTO that the proposed cloud infrastructure is secure while proving to the marketing manager that the data flow will support their holiday campaigns. The Solutions Architect owns the design. They ensure that if the plan is followed, the system will be robust, secure, and performant. However, once the blueprint is approved, they often move on to the next challenge, handing the execution to a delivery team.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/solutions-architect-vs-forward-deployed-engineer/system-architecture.webp&quot; alt=&quot;System architecture illustration of a designed, layered platform&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;The boots on the ground: defining the Forward Deployed Engineer&lt;/h2&gt;
&lt;p&gt;The Forward Deployed Engineer is a hybrid creature born from the world of high-stakes enterprise software. Pioneered by Palantir to embed engineers in messy, sensitive customer data environments, the FDE is a full-stack engineer who works directly inside the customer&apos;s environment. They do not just advise on how to build the system. They write the production code that makes it real.&lt;/p&gt;
&lt;p&gt;If a Solutions Architect is a designer, the FDE is a lead engineer who is also a specialized diplomat. They inherit the &quot;messy&quot; reality of customer data, undocumented APIs, and internal politics. When a &lt;a href=&quot;https://ansezz.com/blog/7-rag-mistakes-production/&quot;&gt;RAG pipeline&lt;/a&gt; fails because the customer&apos;s vector database has inconsistent metadata, the FDE is the one debugging the ingestion script at 2:00 AM.&lt;/p&gt;
&lt;p&gt;The FDE owns the outcome. They are measured by whether the system actually works in production and delivers business value. This role requires a high tolerance for ambiguity and the ability to pivot when a design meets the harsh reality of a production environment. They are the ultimate feedback loop. They find the bugs and missing features in the core product and push those requirements back to the internal product teams.&lt;/p&gt;
&lt;h2&gt;Core differences: strategic design vs production ownership&lt;/h2&gt;
&lt;p&gt;The divide between these roles can be summarized by where they sit in the software development lifecycle (SDLC) and who owns the risk.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Solutions Architect&lt;/th&gt;
&lt;th&gt;Forward Deployed Engineer&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Primary output&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Blueprints, PoCs, design docs&lt;/td&gt;
&lt;td&gt;Production code, integrations, live systems&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Lifecycle stage&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Pre-sales and discovery&lt;/td&gt;
&lt;td&gt;Implementation and post-launch&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Code ownership&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Reference implementations only&lt;/td&gt;
&lt;td&gt;Direct commits to customer repos&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Main stakeholder&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;CTOs and architects&lt;/td&gt;
&lt;td&gt;Engineering managers and devs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Goal&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Risk reduction and scalability&lt;/td&gt;
&lt;td&gt;Successful deployment and value delivery&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;A Solutions Architect might design a multi-region deployment strategy on Google Cloud Platform (GCP) for a global e-commerce brand. They will specify the load balancers, the database replication strategy, and the caching layers. A Forward Deployed Engineer will then take that design and actually write the Terraform scripts, configure the Docker containers, and debug the latency issues that appear when the first 10,000 users hit the site.&lt;/p&gt;
&lt;p&gt;The architect operates at a level of abstraction. The FDE operates at a level of execution. This is why FDEs are often found in companies selling complex, horizontal platforms that require significant customization to be useful.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/solutions-architect-vs-forward-deployed-engineer/production-code.webp&quot; alt=&quot;Production code implementation on a developer&apos;s screen&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Real-world scenarios: from Laravel to AI agents&lt;/h2&gt;
&lt;p&gt;To see how these roles interact, consider the deployment of an agentic AI system for a large retail client. This project involves a complex stack: a Laravel backend, a Shopify frontend, and an AI orchestration layer using Claude and MCP.&lt;/p&gt;
&lt;p&gt;The Solutions Architect starts the project. They define how the &lt;a href=&quot;https://ansezz.com/blog/claude-mcp-dev-tools/&quot;&gt;Claude MCP dev tools&lt;/a&gt; will interface with the client&apos;s private product catalog. They map out the authentication flow to ensure that the AI agent cannot access sensitive customer financial data. They produce a high-level technical document that the client&apos;s security team signs off on.&lt;/p&gt;
&lt;p&gt;Once the &quot;paperwork&quot; is done, the Forward Deployed Engineer moves in. They realize the client&apos;s product catalog API is significantly slower than promised. The FDE writes a custom Redis caching layer in the Laravel middleware to solve this. They spend three days pairing with the client&apos;s frontend team to integrate the AI chat widget into a bespoke Shopify theme. They are the ones who realize the RAG system needs a different chunking strategy for the specific type of PDF manuals the client uses. They fix it in the code.&lt;/p&gt;
&lt;p&gt;In this scenario, the architect ensured the project was safe and technically sound from a high level. The FDE ensured that the specific, messy details of this one client did not sink the entire implementation.&lt;/p&gt;
&lt;h2&gt;Hiring the right talent: which one do you need?&lt;/h2&gt;
&lt;p&gt;Choosing between hiring a Solutions Architect or a Forward Deployed Engineer depends on your product&apos;s complexity and your business model.&lt;/p&gt;
&lt;p&gt;If you are a consultancy or a software vendor with a product that is &quot;plug-and-play&quot; but requires significant integration planning, a Solutions Architect is your best bet. They can handle five or six customers simultaneously, providing the high-level guidance needed to keep projects moving without getting bogged down in any single code repository.&lt;/p&gt;
&lt;p&gt;However, if you are building &quot;deep tech&quot; or complex platforms like AI-driven supply chain tools or custom enterprise ERPs, you need Forward Deployed Engineers. These products are never truly &quot;plug-and-play.&quot; They require an engineer to sit with the customer for months, adapting the product to the data.&lt;/p&gt;
&lt;p&gt;From a candidate perspective, the choice depends on your preference for the &quot;blank page&quot; versus the &quot;broken system.&quot; Architects enjoy the creative challenge of designing from scratch and the variety of working across many industries. FDEs enjoy the satisfaction of shipping code that solves a tangible, immediate problem for a human being they interact with every day. If you are weighing engineering tracks more broadly, the same blueprint-versus-operations split shows up in &lt;a href=&quot;https://ansezz.com/blog/sre-vs-platform-engineer/&quot;&gt;SRE vs platform engineer&lt;/a&gt; and in &lt;a href=&quot;https://ansezz.com/blog/cloud-engineer-vs-devops-engineer/&quot;&gt;cloud engineer vs DevOps engineer&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/solutions-architect-vs-forward-deployed-engineer/architect-vs-engineer.webp&quot; alt=&quot;Comparison illustration of an architect&apos;s blueprint beside an engineer&apos;s keyboard&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Takeaways&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Solutions Architects&lt;/strong&gt; focus on the &quot;what&quot; and &quot;why&quot; of a system. They provide the strategic design and reduce technical risk during the early stages of a project.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Forward Deployed Engineers&lt;/strong&gt; focus on the &quot;how.&quot; They are hands-on coders who live inside the customer&apos;s environment to ensure a system actually reaches production.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Ownership&lt;/strong&gt; is the main divider. Architects own the design document. FDEs own the live production outcome.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Feedback loops&lt;/strong&gt; differ. Architects improve the company&apos;s &quot;standards.&quot; FDEs improve the company&apos;s &quot;product&quot; by finding real-world bugs.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Skill sets&lt;/strong&gt; overlap but diverge at the keyboard. Both need strong communication skills, but the FDE must be a top-tier debugger and implementation specialist.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;When a project fails, it is rarely because of a bad design or a bad implementation in isolation. It is usually because there was a gap between the two. The best technical organizations find a way to make these roles work in tandem, ensuring the blueprint is sound and the builders have the tools they need to execute.&lt;/p&gt;
&lt;p&gt;If you are currently scaling a technical team, are you optimizing for the number of designs approved or the number of systems successfully deployed into production? If you need a Forward Deployed Engineer to close that gap, &lt;a href=&quot;https://ansezz.com/services/&quot;&gt;here&apos;s how I help teams ship&lt;/a&gt;.&lt;/p&gt;
</content:encoded><category>career</category><category>architecture</category><category>devops</category><category>career</category></item><item><title>REST vs gRPC: two API philosophies</title><link>https://ansezz.com/blog/rest-vs-grpc/</link><guid isPermaLink="true">https://ansezz.com/blog/rest-vs-grpc/</guid><description>The technical differences between REST and gRPC: when to use Protocol Buffers over JSON for high-performance microservices, and why good designs use both.</description><pubDate>Sun, 14 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Choosing between REST and gRPC is not just a choice of protocol. It is a choice of architectural philosophy that shapes how your services communicate. Pick the wrong one for a high-traffic microservice environment and you will spend your time fighting network latency and rising cloud costs as JSON payloads bloat under load. Force a binary protocol onto a public-facing web client and you break the very interoperability that makes the modern web work.&lt;/p&gt;
&lt;p&gt;The choice between Representational State Transfer (REST) and gRPC (a Google-built RPC framework whose name is a recursive joke — &quot;gRPC Remote Procedure Calls&quot;) is fundamentally a trade-off between human readability and machine efficiency. While REST has been the undisputed champion of the web for decades, gRPC has rapidly become the preferred choice for internal service-to-service communication. Understanding the nuances of these two philosophies is essential for any engineer building scalable systems in 2026.&lt;/p&gt;
&lt;h2&gt;The mechanical advantage of gRPC&lt;/h2&gt;
&lt;p&gt;At its core, gRPC is built to be fast. It achieves this by moving away from the text-based nature of REST and embracing a binary format called Protocol Buffers (Protobuf). In a typical REST interaction, your server takes an object, serializes it into a JSON string, and sends it over the wire. The receiving server then parses that string back into an object. This process is CPU-intensive and creates large payloads because JSON includes every key name in every single message.&lt;/p&gt;
&lt;p&gt;gRPC bypasses this overhead. Protobuf is a strongly typed binary serialization format. Because the schema is defined in advance using a &lt;code&gt;.proto&lt;/code&gt; file, the messages do not need to include field names. Instead, they use small numeric tags to identify each field. The payload savings depend heavily on data shape — numeric and structured data compresses dramatically, often several times smaller than the equivalent JSON, while large free-text fields see less benefit. Either way, binary Protobuf parsing is typically much faster than JSON parsing, which meaningfully cuts serialization CPU in high-throughput environments.&lt;/p&gt;
&lt;p&gt;Another technical pillar of gRPC is its reliance on HTTP/2. While many REST implementations still run on HTTP/1.1, gRPC requires HTTP/2. This brings several advantages like request multiplexing. In HTTP/1.1, a client effectively sends one request at a time over a connection, which leads to application-level head-of-line blocking. HTTP/2 multiplexes multiple requests and responses over a single TCP connection, removing that HTTP-layer blocking. (TCP-level head-of-line blocking still exists under packet loss — the problem HTTP/3 and QUIC were designed to solve.) This efficiency is critical for modern &lt;a href=&quot;https://ansezz.com/blog/api-gateway-ai-stack/&quot;&gt;API gateway architectures&lt;/a&gt; that handle thousands of concurrent requests.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/rest-vs-grpc/metrics-dashboard.webp&quot; alt=&quot;Bento grid dashboard comparing latency and throughput between REST and gRPC&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;The universality of REST&lt;/h2&gt;
&lt;p&gt;If gRPC is so much faster, why hasn&apos;t it replaced REST entirely? The answer lies in the browser. REST was born from the architecture of the web itself. It treats every piece of data as a resource that can be accessed via standard HTTP verbs like GET, POST, PUT, and DELETE. This resource-oriented approach makes it incredibly intuitive and easy to consume.&lt;/p&gt;
&lt;p&gt;Every web browser, every language, and every command-line tool like &lt;code&gt;curl&lt;/code&gt; understands REST. If you are building a public API that third-party developers will use, REST is the only logical choice. The barrier to entry is almost zero. A developer can open their browser console and immediately see what your API returns in a human-readable format. This &quot;inspectability&quot; is a massive advantage for debugging and onboarding.&lt;/p&gt;
&lt;p&gt;REST is also highly flexible. It does not require a strict contract to function. While tools like OpenAPI (Swagger) provide structure, a REST API can still evolve loosely. This flexibility is perfect for startups and small teams that need to iterate quickly on their digital presence. When you are deploying a new SaaS product on &lt;a href=&quot;https://ansezz.com/blog/coolify-docker-saas-hosting/&quot;&gt;Coolify or Docker&lt;/a&gt;, the simplicity of a RESTful interface often outweighs the performance gains of gRPC during the early stages of growth.&lt;/p&gt;
&lt;h2&gt;Schema-first vs resource-first development&lt;/h2&gt;
&lt;p&gt;One of the most significant differences between these two philosophies is how you actually write code. gRPC forces a &quot;contract-first&quot; approach. You must define your service and your message types in a &lt;code&gt;.proto&lt;/code&gt; file before you write a single line of application logic.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// A typical gRPC service definition
service ProductService {
  rpc GetProduct (ProductRequest) returns (ProductResponse);
}

message ProductRequest {
  string id = 1;
}

message ProductResponse {
  string name = 1;
  double price = 2;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Once this file is defined, gRPC tools generate client and server stubs in the language of your choice. This provides strong typing across different services. If you have a Go backend talking to a Python microservice, the Protobuf contract ensures both sides agree on the data structure. This eliminates a whole class of bugs caused by missing fields or incorrect types in JSON.&lt;/p&gt;
&lt;p&gt;REST typically follows a &quot;resource-first&quot; or &quot;implementation-first&quot; approach. You define your routes and your controllers, and then you might generate documentation later. While this allows for faster prototyping, it can lead to &quot;API drift&quot; where the documentation and the actual implementation become out of sync. In a large &lt;a href=&quot;https://ansezz.com/blog/monolith-vs-microservices/&quot;&gt;microservice ecosystem&lt;/a&gt;, this lack of strict contracts can become a maintenance nightmare.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/rest-vs-grpc/proto-vs-json.webp&quot; alt=&quot;Technical illustration comparing a .proto schema file with a JSON object on a monitor&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Streaming and real-time capabilities&lt;/h2&gt;
&lt;p&gt;Another area where gRPC shines is in its native support for streaming. Because it is built on HTTP/2, gRPC supports four types of communication:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Unary:&lt;/strong&gt; A single request and a single response (the typical REST pattern).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Server streaming:&lt;/strong&gt; The client sends one request and the server sends back a stream of messages.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Client streaming:&lt;/strong&gt; The client sends a stream of messages and the server responds once.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Bidirectional streaming:&lt;/strong&gt; Both client and server send a stream of messages simultaneously.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This makes gRPC the ideal candidate for real-time applications like chat services, stock tickers, or IoT telemetry feeds. In the world of REST, you would typically need to reach for WebSockets or Server-Sent Events (SSE) to achieve similar results. These are often treated as &quot;add-ons&quot; to the API rather than a core part of the protocol. With gRPC, streaming is a first-class citizen.&lt;/p&gt;
&lt;p&gt;Imagine a system that needs to process a massive dataset of vector embeddings for an AI application. A client could stream the data to the server, and the server could stream back the results as they are processed. This reduces the memory footprint on both ends because the entire dataset never needs to be loaded into RAM at once.&lt;/p&gt;
&lt;h2&gt;Choosing between REST and gRPC for 2026&lt;/h2&gt;
&lt;p&gt;The decision between REST and gRPC should be based on your &quot;consumer.&quot; If your consumer is a human developer or a web browser, choose REST. The ecosystem of tools, from Postman to Chrome DevTools, is simply too mature to ignore. REST is the language of the public web.&lt;/p&gt;
&lt;p&gt;If your consumer is another server or a performance-constrained device like a mobile phone or an IoT sensor, gRPC is usually the superior choice. The performance gains in serialization and the reduced network overhead will save you money on bandwidth and compute. In a modern architecture, it is common to see both living together. A RESTful API gateway handles incoming traffic from the internet, but then it communicates with internal microservices using gRPC. This &quot;hybrid&quot; approach gives you the best of both worlds: broad interoperability and extreme internal performance.&lt;/p&gt;
&lt;p&gt;As we move further into the era of &lt;a href=&quot;https://ansezz.com/blog/ai-vs-traditional-development/&quot;&gt;AI-integrated development&lt;/a&gt;, the efficiency of these data pipelines will become even more critical. Large language models and agentic systems require low-latency access to data, making gRPC a natural fit for the &quot;inner loop&quot; of AI infrastructure.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/rest-vs-grpc/hybrid-architecture.webp&quot; alt=&quot;Architecture diagram showing an API gateway routing REST traffic from a browser to gRPC microservices&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Takeaways&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Performance:&lt;/strong&gt; gRPC is significantly faster than REST due to binary serialization (Protobuf) and HTTP/2 multiplexing.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Payload size:&lt;/strong&gt; Protobuf messages are smaller than JSON because they omit redundant field names and use compact binary encoding, though the gain depends on data shape.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Contract-first:&lt;/strong&gt; gRPC requires a strict schema definition, leading to better type safety and less &quot;API drift&quot; in large systems.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Interoperability:&lt;/strong&gt; REST remains the king of the public web and browser-based clients due to its human-readable JSON format and native browser support.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Streaming:&lt;/strong&gt; gRPC supports native bidirectional streaming, making it ideal for real-time data pipelines and IoT.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Hybrid approach:&lt;/strong&gt; The most robust modern architectures use REST for the public-facing edge and gRPC for internal service-to-service communication.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you are building for the web, start with REST. If you are building for the cloud at scale, master gRPC.&lt;/p&gt;
&lt;p&gt;Are you prepared to handle the added complexity of a contract-first architecture in exchange for smaller payloads and faster serialization? If you&apos;re designing that service mesh for production, &lt;a href=&quot;https://ansezz.com/services/&quot;&gt;here&apos;s how I help teams ship it&lt;/a&gt;.&lt;/p&gt;
</content:encoded><category>architecture</category><category>api-design</category><category>microservices</category><category>devops</category></item><item><title>Serverless vs containers: the 2026 engineering guide</title><link>https://ansezz.com/blog/serverless-vs-containers/</link><guid isPermaLink="true">https://ansezz.com/blog/serverless-vs-containers/</guid><description>Compare Serverless vs Containers for performance, cost, and scalability. Learn why hybrid models are winning for Laravel and Shopify applications in 2026.</description><pubDate>Sun, 14 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Developers are drowning in YAML files and server patches while the business demands faster shipping cycles. Managing infrastructure often feels like a full-time job that has nothing to do with writing actual code.&lt;/p&gt;
&lt;p&gt;When your application hits a sudden traffic spike during a flash sale or a viral product launch, the &quot;504 Gateway Timeout&quot; becomes your worst enemy. If your infrastructure cannot scale in seconds, you lose revenue and customer trust. The choice between Serverless vs Containers is no longer just a technical preference. It is a strategic decision that determines your operational overhead and your ability to stay lean in a competitive market.&lt;/p&gt;
&lt;p&gt;In 2026, the lines between these technologies have blurred. We see the rise of serverless containers and event-driven architectures that mix both worlds. This guide breaks down the engineering substance of Serverless vs Containers to help you decide where to host your next Laravel application or Shopify integration.&lt;/p&gt;
&lt;h2&gt;Understanding the architectural split&lt;/h2&gt;
&lt;p&gt;The debate over Serverless vs Containers is often simplified into &quot;no servers&quot; vs &quot;virtual servers.&quot; In reality, the difference lies in the abstraction layer.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Serverless&lt;/strong&gt; (often referred to as Function-as-a-Service or FaaS) abstracts the entire execution environment. You upload code, and the provider (AWS Lambda, Google Cloud Functions) handles the trigger, the scaling, and the underlying OS. It is fundamentally event-driven. Your code stays dormant until an HTTP request, a file upload, or a database change wakes it up.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Containers&lt;/strong&gt; (Docker, Kubernetes) package the entire runtime environment. This includes the application code, libraries, and system dependencies. You have full control over the operating system version and the configuration. While containers run on infrastructure you manage (or semi-manage via Fargate or Cloud Run), they are typically &quot;always on&quot; or require specific scaling rules to spin up and down.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Serverless (FaaS)&lt;/th&gt;
&lt;th&gt;Containers (Docker/K8s)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Control&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Minimal; provider-managed&lt;/td&gt;
&lt;td&gt;High; you define the image&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Scaling&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Automatic per request&lt;/td&gt;
&lt;td&gt;Orchestrator-managed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;State&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Strictly stateless&lt;/td&gt;
&lt;td&gt;Can be stateful&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Runtime&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Limited to provider support&lt;/td&gt;
&lt;td&gt;Anything that can be Dockerized&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Timeout&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Usually &amp;lt; 15 minutes&lt;/td&gt;
&lt;td&gt;No inherent execution limit&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;Serverless: the high-speed execution engine&lt;/h2&gt;
&lt;p&gt;Serverless is the ultimate tool for developers who want to ignore infrastructure. The primary advantage is the &quot;scale-to-zero&quot; model. When no one is using your app, you pay nothing. When traffic surges, the provider spins up new instances of your function automatically — within account concurrency limits (AWS Lambda defaults to 1,000 concurrent executions per region, raisable on request, and scales out at roughly 1,000 new environments every 10 seconds).&lt;/p&gt;
&lt;p&gt;However, this comes with the &quot;cold start&quot; penalty. When a function hasn&apos;t been used recently, the provider must provision a container and boot your runtime. Lightweight runtimes like Node.js and Go start fast natively, and provisioned concurrency keeps instances warm. Heavier runtimes feel it more — which is why AWS built SnapStart (initially for Java, later .NET and Python) to snapshot an initialized runtime and cut cold starts dramatically. For frameworks like PHP, cold starts remain a factor that requires careful architectural planning.&lt;/p&gt;
&lt;p&gt;Serverless is perfect for &quot;glue code.&quot; If you are building a &lt;a href=&quot;https://ansezz.com/blog/agentic-commerce-shopify/&quot;&gt;Shopify app architecture&lt;/a&gt; that needs to process webhooks or resize images on the fly, serverless functions are incredibly efficient. They handle the bursty, unpredictable nature of webhooks without requiring a dedicated server to sit idle 99% of the time.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/serverless-vs-containers/cold-start-latency.webp&quot; alt=&quot;A pop-art comic-style technical dashboard comparing cold start latency for serverless and steady state performance for containers&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Containers: the universal blueprint&lt;/h2&gt;
&lt;p&gt;Containers offer a level of portability that serverless cannot match. A Docker image that runs on your local machine will run exactly the same way on AWS ECS, Google Cloud Run, or a self-hosted &lt;a href=&quot;https://ansezz.com/blog/coolify-docker-saas-hosting/&quot;&gt;Coolify instance&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;For long-running processes or heavy compute tasks, containers are the standard. If your application needs to maintain a persistent WebSocket connection, perform heavy machine learning inference, or run long-running cron jobs that exceed 15 minutes, containers are the only viable path.&lt;/p&gt;
&lt;p&gt;The primary drawback is the &quot;operational tax.&quot; Even with managed services like AWS Fargate, you are still responsible for your Dockerfiles, image security scanning, and ensuring your containers don&apos;t run out of memory. You are managing the environment, even if you aren&apos;t managing the physical hardware.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# A typical Laravel Dockerfile for a containerized approach
FROM php:8.4-fpm-alpine

# Install system dependencies
RUN apk add --no-cache libpng-dev libzip-dev zip unzip git

# Install PHP extensions
RUN docker-php-ext-install pdo_mysql gd zip

# Set working directory
WORKDIR /var/www

# Copy application code
COPY . .

# Install composer dependencies
RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
RUN composer install --no-dev --optimize-autoloader

CMD [&quot;php-fpm&quot;]
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Technical head-to-head: latency and cost&lt;/h2&gt;
&lt;p&gt;When comparing Serverless vs Containers, cost is the most common point of confusion. Serverless looks cheaper because you only pay for what you use. This is true for low-traffic or spiky applications.&lt;/p&gt;
&lt;p&gt;However, at a certain threshold of sustained traffic, containers become more cost-effective. Once your application is processing thousands of requests per minute consistently, the per-invocation cost of serverless starts to exceed the monthly cost of a reserved container instance.&lt;/p&gt;
&lt;p&gt;Latency is another critical trade-off. Containers offer &quot;warm&quot; execution. Since the process is already running, the time-to-first-byte (TTFB) is consistent. Serverless performance can be a &quot;jittery&quot; experience. One request might take 30ms while the next (a cold start) takes several hundred milliseconds. For an interactive admin dashboard, this inconsistency can frustrate users — and it ties directly into how you architect &lt;a href=&quot;https://ansezz.com/blog/stateless-vs-stateful-apps/&quot;&gt;stateless vs stateful apps&lt;/a&gt;, since serverless forces a strictly stateless design.&lt;/p&gt;
&lt;h2&gt;Laravel and Shopify: where to host?&lt;/h2&gt;
&lt;p&gt;For a modern Laravel application, the choice often comes down to &lt;strong&gt;Laravel Vapor&lt;/strong&gt; (Serverless) vs &lt;strong&gt;Docker on Cloud Run/Coolify&lt;/strong&gt; (Containers).&lt;/p&gt;
&lt;h3&gt;The case for serverless Laravel&lt;/h3&gt;
&lt;p&gt;If you are building a SaaS with unpredictable growth, Laravel Vapor is a powerhouse. It handles the complexity of deploying a full-stack PHP framework to AWS Lambda. It manages your assets on S3 and handles database scaling. It is the &quot;easy button&quot; for high-scale Laravel apps that don&apos;t want a dedicated DevOps engineer.&lt;/p&gt;
&lt;h3&gt;The case for containerized Laravel&lt;/h3&gt;
&lt;p&gt;If your application is &quot;always on&quot; and involves heavy background processing (like syncing thousands of products for a Shopify store), containers are superior. Using Docker allows you to run your HTTP layer, your queue workers, and your scheduler in a cohesive environment. This setup matches your local development environment perfectly, reducing the &quot;it works on my machine&quot; bugs.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/serverless-vs-containers/laravel-shopify-architecture.webp&quot; alt=&quot;Pop-art technical illustration showing a Laravel and Shopify application architecture with a modular bento grid layout and code snippets&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;The 2026 hybrid reality&lt;/h2&gt;
&lt;p&gt;Modern engineering has moved past the binary choice of Serverless vs Containers. We are now in the era of &lt;strong&gt;serverless containers&lt;/strong&gt;. Services like Google Cloud Run and AWS App Runner allow you to package your app as a container but enjoy the autoscaling and pay-per-use benefits of serverless.&lt;/p&gt;
&lt;p&gt;You can deploy your main Laravel API as a container on Cloud Run, which scales to zero when idle. Simultaneously, you can use pure serverless functions (like AWS Lambda) to handle specific, isolated tasks like PDF generation or sending transactional emails.&lt;/p&gt;
&lt;p&gt;This hybrid approach allows you to place the right workload in the right environment. Your core business logic stays in a predictable container, while your bursty, auxiliary tasks scale independently in a serverless environment.&lt;/p&gt;
&lt;h2&gt;Takeaways&lt;/h2&gt;
&lt;p&gt;Choosing between Serverless vs Containers requires an honest assessment of your team&apos;s skills and your application&apos;s traffic patterns.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Choose Serverless&lt;/strong&gt; if your traffic is spiky, you have a small team with no DevOps experience, and your tasks are short-lived (under 15 minutes). It is the fastest path from code to production for event-driven systems.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Choose Containers&lt;/strong&gt; if you have steady, high-volume traffic, require specific OS libraries, or need long-running background processes. It offers the best price-to-performance ratio for &quot;always-on&quot; applications.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Go Hybrid&lt;/strong&gt; if you want the best of both worlds. Use a serverless container platform like Cloud Run for your web app and FaaS for your side effects.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Laravel developers:&lt;/strong&gt; Vapor is great for AWS-centric teams, but Dockerized deployments on platforms like Coolify offer more control and lower long-term costs for many regional businesses.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Shopify developers:&lt;/strong&gt; Use serverless functions for webhooks to prevent bottlenecks during peak sales events like Black Friday, but keep your admin UI in a container for consistent speed.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/serverless-vs-containers/cloud-dashboard.webp&quot; alt=&quot;Pop-art comic-style scene depicting a cloud infrastructure management dashboard with AWS, Google Cloud, and Docker logos&quot; /&gt;&lt;/p&gt;
&lt;p&gt;If you had to migrate your core API today, would you prioritize the absolute control of a Dockerfile or the operational freedom of an event-driven function? &lt;a href=&quot;https://ansezz.com/services/&quot;&gt;Here&apos;s how I help teams make that call&lt;/a&gt;.&lt;/p&gt;
</content:encoded><category>architecture</category><category>infrastructure</category><category>docker</category><category>laravel</category><category>shopify</category></item><item><title>Replication vs backup: why Laravel needs both</title><link>https://ansezz.com/blog/replication-vs-backup-laravel/</link><guid isPermaLink="true">https://ansezz.com/blog/replication-vs-backup-laravel/</guid><description>Replication protects you from hardware failure; backups protect you from your own mistakes. How read/write splitting and point-in-time recovery fit in.</description><pubDate>Sat, 13 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;You deploy a breaking database migration that drops a critical column. Your production database has real-time replication set up to three different nodes across two regions. Within milliseconds, that destructive change propagates across every single one of your &quot;safety&quot; copies. You have zero downtime, but you have no data left. This is the moment you realize that replication is not a backup.&lt;/p&gt;
&lt;p&gt;Engineering teams often conflate high availability with data durability. While both are pillars of a robust infrastructure, they solve fundamentally different problems. Replication protects you from hardware failure and latency. Backups protect you from logic errors, corruption, and human mistakes. If you are building a Laravel application or a high-volume Shopify Plus integration, understanding where one ends and the other begins is the difference between a minor incident and a company-ending event.&lt;/p&gt;
&lt;h2&gt;The fundamental trade-off: speed vs history&lt;/h2&gt;
&lt;p&gt;Replication is about continuity. Its primary goal is to ensure that if your main database server vanishes, a secondary node is ready to take over immediately. This is measured in Recovery Time Objective (RTO). In a well-tuned system, failover happens in seconds. You are mirroring every &lt;code&gt;INSERT&lt;/code&gt;, &lt;code&gt;UPDATE&lt;/code&gt;, and &lt;code&gt;DELETE&lt;/code&gt; as they happen.&lt;/p&gt;
&lt;p&gt;Backups are about recovery. Their goal is to provide a safe version of your data from a specific point in history. If a bug corrupts your inventory levels at 2:00 PM, a backup from 1:00 PM is your only lifeline. Backups are usually stored as compressed snapshots on detached storage like Amazon S3 or Google Cloud Storage.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Replication&lt;/th&gt;
&lt;th&gt;Backup&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Primary Goal&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;High Availability (HA)&lt;/td&gt;
&lt;td&gt;Disaster Recovery (DR)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Data State&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Real-time / Live&lt;/td&gt;
&lt;td&gt;Historical / Snapshot&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Propagation&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Immediate (including errors)&lt;/td&gt;
&lt;td&gt;None (isolated from live changes)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Storage Cost&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;High (hot, identical hardware)&lt;/td&gt;
&lt;td&gt;Low (cold, compressed snapshots)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Recovery Speed&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Seconds to Minutes&lt;/td&gt;
&lt;td&gt;Minutes to Hours&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;Why replication faithfully mirrors your mistakes&lt;/h2&gt;
&lt;p&gt;The greatest strength of replication is its greatest weakness. It is designed to be a mirror. If you execute a &lt;code&gt;truncate&lt;/code&gt; command on your primary database, the replication protocol assumes this was intentional. It will purge that data from your replicas before you can even hit &quot;Ctrl+C&quot; on your terminal.&lt;/p&gt;
&lt;p&gt;I have seen developers rely solely on RDS Read Replicas as their &quot;safety net.&quot; When a rogue queue job began overwriting customer email addresses with null values, the replicas updated instantly. Without a point-in-time backup, those original emails were gone forever. Replication provides infrastructure resilience, not data integrity.&lt;/p&gt;
&lt;p&gt;For a production-grade &lt;a href=&quot;https://ansezz.com/blog/coolify-self-hosted-saas/&quot;&gt;Coolify self-hosted SaaS&lt;/a&gt;, you must separate your concerns. Use replication to scale your reads and handle node failures. Use automated, encrypted backups to ensure you can roll back to a known-good state.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/replication-vs-backup-laravel/recovery-tradeoffs.webp&quot; alt=&quot;Bento grid in pop-art comic style — an RTO clock and green active checkmark for replication, a history calendar and yellow archive icon for backup&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Implementing replication in Laravel&lt;/h2&gt;
&lt;p&gt;Laravel handles read/write splitting through native configuration. By defining &lt;code&gt;read&lt;/code&gt; and &lt;code&gt;write&lt;/code&gt; connections in your &lt;code&gt;config/database.php&lt;/code&gt;, the framework automatically routes &lt;code&gt;SELECT&lt;/code&gt; statements to your replicas while sending &lt;code&gt;INSERT&lt;/code&gt; and &lt;code&gt;UPDATE&lt;/code&gt; statements to the primary. This is the same connection layer you tune when designing a &lt;a href=&quot;https://ansezz.com/blog/laravel-multi-tenancy/&quot;&gt;multi-tenant Laravel architecture&lt;/a&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&apos;mysql&apos; =&amp;gt; [
    &apos;read&apos; =&amp;gt; [
        &apos;host&apos; =&amp;gt; [
            &apos;192.168.1.10&apos;, // Replica 1
            &apos;192.168.1.11&apos;, // Replica 2
        ],
    ],
    &apos;write&apos; =&amp;gt; [
        &apos;host&apos; =&amp;gt; [
            &apos;192.168.1.1&apos;, // Primary
        ],
    ],
    &apos;sticky&apos; =&amp;gt; true,
    &apos;driver&apos; =&amp;gt; &apos;mysql&apos;,
    // ... other settings
],
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;sticky&lt;/code&gt; option is critical here. It ensures that if you write a record during a request, any subsequent reads during that same request will come from the primary. This avoids the &quot;read-after-write&quot; lag where a replica might be a few milliseconds behind the primary, causing your application to appear as if the data vanished.&lt;/p&gt;
&lt;h2&gt;Shopify Plus and the sync fallacy&lt;/h2&gt;
&lt;p&gt;In the world of Shopify Plus, many developers build &quot;replication-style&quot; sync engines. They listen for &lt;code&gt;orders/create&lt;/code&gt; or &lt;code&gt;products/update&lt;/code&gt; webhooks and mirror that data into a local Laravel database. This is a powerful pattern for building custom reporting or complex &lt;a href=&quot;https://ansezz.com/blog/agentic-commerce-shopify/&quot;&gt;agentic commerce workflows&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;However, this local database is often mistaken for a backup. If an admin user deletes a collection of products in the Shopify admin panel, Shopify fires a webhook. Your Laravel app receives that webhook and promptly deletes those products from your local DB to stay &quot;in sync.&quot;&lt;/p&gt;
&lt;p&gt;If you do not have a separate backup strategy that creates daily snapshots of your local DB, your local data is just as ephemeral as the live Shopify data. To build true resilience, you must store historical JSON payloads of those Shopify resources in a versioned storage system like S3. This allows you to reconstruct your state even if the live sync deletes everything.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/replication-vs-backup-laravel/architecture.webp&quot; alt=&quot;Technical architecture diagram in pop-art comic style — a Laravel core connected to a red primary database and two blue read replicas, with a yellow S3 bucket storing periodic snapshots&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Cloud infrastructure and the cost of availability&lt;/h2&gt;
&lt;p&gt;Managing replication manually is a DevOps nightmare. For most Laravel applications, using managed services like AWS RDS or Google Cloud SQL is the correct move. These services handle the binary log replication, failover orchestration, and monitoring out of the box.&lt;/p&gt;
&lt;p&gt;When configuring your cloud DB, you will encounter &quot;Multi-AZ&quot; (AWS) or &quot;High Availability&quot; (GCP) settings. A single-standby Multi-AZ deployment uses synchronous replication, and it roughly doubles your database cost because you are running a standby instance in a second Availability Zone that does nothing but wait for the primary to die. Note that this standby cannot serve read traffic — to scale reads you need separate read replicas (or RDS Multi-AZ DB clusters), not the HA standby.&lt;/p&gt;
&lt;p&gt;Backups, by contrast, are extremely cheap. Storing 500GB of compressed dumps in S3 standard runs a few dollars a month; archival tiers like Glacier Deep Archive drop that to cents, at the cost of multi-hour retrieval. The cost of a backup is not in the storage; it is in the testing. A backup that has never been restored is just a collection of random bits. You must automate your restore tests. I recommend using &lt;a href=&quot;https://ansezz.com/blog/coolify-docker-saas-hosting/&quot;&gt;Coolify and Docker&lt;/a&gt; to spin up ephemeral environments where you can periodically test your backup restoration process without touching production.&lt;/p&gt;
&lt;h2&gt;Takeaways&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Replication is for uptime.&lt;/strong&gt; It allows your app to stay online during hardware failures or network partitions.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Backups are for survival.&lt;/strong&gt; They are your only defense against data corruption, malicious attacks, and developer errors.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Replication propagates bugs.&lt;/strong&gt; If your code breaks the data, the replica will break just as fast.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Laravel supports read/write splitting.&lt;/strong&gt; Use the &lt;code&gt;sticky&lt;/code&gt; configuration to prevent consistency issues during web requests.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Shopify webhooks are not backups.&lt;/strong&gt; A synchronized local database is a replica, not a historical record.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Test your restores.&lt;/strong&gt; Automate a process to verify that your backup snapshots actually work.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you had to choose between a system with 99.99% uptime but no backups, and a system with 95% uptime but hourly backups, which one would keep you from losing your business? If you&apos;re architecting that resilience layer for production, &lt;a href=&quot;https://ansezz.com/services/&quot;&gt;here&apos;s how I help teams ship it&lt;/a&gt;.&lt;/p&gt;
</content:encoded><category>devops</category><category>databases</category><category>laravel</category><category>shopify</category><category>infrastructure</category></item><item><title>RAG vs fine-tuning: choosing your AI architecture</title><link>https://ansezz.com/blog/rag-vs-fine-tuning/</link><guid isPermaLink="true">https://ansezz.com/blog/rag-vs-fine-tuning/</guid><description>RAG vs fine-tuning for production LLMs — knowledge vs behavior, latency, cost, privacy, and the hybrid approach. How to pick the right AI architecture.</description><pubDate>Fri, 12 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Large language models are only as good as the information they can access. You build a custom application and realize the base model knows nothing about your internal documentation or yesterday&apos;s product launch. It starts making things up. This hallucination problem is the biggest hurdle for developers moving from a prototype to production, and it is exactly where the RAG vs fine-tuning decision gets made.&lt;/p&gt;
&lt;p&gt;The tension usually boils down to two distinct paths. You can either give the model a search engine to find the right information at runtime, or you can retrain the model to bake that information directly into its weights. These two methods are Retrieval-Augmented Generation (RAG) and fine-tuning.&lt;/p&gt;
&lt;p&gt;Choosing the wrong one leads to wasted engineering hours and high infrastructure bills. A RAG system might be too slow for a high-traffic API. A fine-tuned model might become obsolete the moment you update your pricing page. This guide breaks down the technical nuances of both approaches to help you decide which architecture fits your next build.&lt;/p&gt;
&lt;h2&gt;Understanding RAG as the open-book exam&lt;/h2&gt;
&lt;p&gt;Retrieval-Augmented Generation is essentially giving an LLM an open-book exam. Instead of relying on the model to remember everything from its original training, you provide a set of relevant documents along with the user prompt. The model then uses this provided context to generate an accurate answer.&lt;/p&gt;
&lt;p&gt;The technical workflow involves a few moving parts. First, you convert your documents into numerical representations called embeddings. You store these in a vector database like pgvector or Pinecone. When a user asks a question, your system performs a semantic search to find the most relevant &quot;chunks&quot; of text. These chunks are then injected into the prompt window of the LLM.&lt;/p&gt;
&lt;p&gt;RAG is the gold standard for applications where data changes frequently. If you are building a support bot for a Shopify store, you need it to know about current inventory levels. Retraining a model every time a product sells out is impossible. With RAG, you just update the vector index. You can read more about avoiding common pitfalls in this space in our guide on &lt;a href=&quot;https://ansezz.com/blog/7-rag-mistakes-production/&quot;&gt;7 RAG mistakes in production&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/rag-vs-fine-tuning/rag-architecture.webp&quot; alt=&quot;Architecture diagram showing a Laravel app connecting to pgvector and an LLM&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Fine-tuning as internalized knowledge&lt;/h2&gt;
&lt;p&gt;Fine-tuning is the process of taking a pre-trained model and continuing its training on a smaller, specialized dataset. You are effectively changing the &quot;brain&quot; of the model by adjusting its internal weights. This makes the model inherently understand the patterns, tone, and specific terminology of your domain without needing external documents for every query.&lt;/p&gt;
&lt;p&gt;This approach is less about adding new facts and more about teaching the model how to act. It shines when you need a consistent style, tone, or response pattern that prompting alone cannot pin down reliably. (For strict JSON shape, reach for structured outputs or constrained decoding first — every major provider now guarantees schema-valid output at inference time, no training run required.)&lt;/p&gt;
&lt;p&gt;Fine-tuning is also the go-to for specialized industries like legal or medical tech. The model learns the specific &quot;language&quot; of the field. However, fine-tuning has a major drawback. It creates a static snapshot of knowledge. If the underlying facts change, the model remains stuck with its training data until you run another expensive training cycle.&lt;/p&gt;
&lt;h2&gt;The latency and throughput battle&lt;/h2&gt;
&lt;p&gt;Performance is where these two architectures diverge sharply. RAG adds several steps to every single request. Your application must generate an embedding for the query, search the vector database, and then send a much larger prompt (containing the retrieved text) to the LLM. Each of these steps adds milliseconds or even seconds to the total response time.&lt;/p&gt;
&lt;p&gt;For high-traffic applications, this latency can be a deal-breaker. The retrieval step also means standing up and querying a vector store alongside your app — see &lt;a href=&quot;https://ansezz.com/blog/picking-the-right-rag-stack/&quot;&gt;picking the right RAG stack&lt;/a&gt; for how that choice shapes both speed and cost.&lt;/p&gt;
&lt;p&gt;Fine-tuning offers much lower latency at runtime. There is no external retrieval step. The prompt remains short because the model already &quot;knows&quot; the context. This makes fine-tuned models ideal for real-time applications or high-volume background tasks like sentiment analysis or lead scoring.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/rag-vs-fine-tuning/latency-vs-freshness.webp&quot; alt=&quot;Comparison graphic showing latency vs data-freshness trade-offs&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Infrastructure and cost considerations&lt;/h2&gt;
&lt;p&gt;The cost of RAG is largely operational. You pay for the storage and querying of the vector database. You also pay more for LLM tokens because your prompts are significantly longer. Over millions of requests, these &quot;context tokens&quot; add up quickly. RAG is cheaper to set up but can become more expensive at extreme scales.&lt;/p&gt;
&lt;p&gt;Fine-tuning has a high upfront cost. You need to curate a high-quality dataset, which often requires human labeling. You also need significant GPU power to perform the training. Once the model is trained, however, your per-query costs are lower. The prompts are shorter and you do not need to maintain a complex retrieval pipeline.&lt;/p&gt;
&lt;p&gt;Managing the infrastructure for these systems requires a solid DevOps foundation. The deeper split is operational: fine-tuning lives in the &lt;a href=&quot;https://ansezz.com/blog/training-vs-inference/&quot;&gt;training&lt;/a&gt; world (GPUs, datasets, MLOps), while RAG lives in the inference-time data-pipeline world. Decide which your team is actually equipped to run.&lt;/p&gt;
&lt;h2&gt;Data privacy and governance&lt;/h2&gt;
&lt;p&gt;For enterprise clients, data privacy is often the deciding factor. RAG offers superior data governance. Since the information is retrieved from your own database at runtime, you can apply standard access controls. You can ensure a user only retrieves documents they are authorized to see. The LLM never &quot;learns&quot; the data permanently.&lt;/p&gt;
&lt;p&gt;Fine-tuning is riskier in this regard. If you train a model on sensitive customer data, that information is baked into the model weights. Membership inference and training-data extraction attacks are well-documented ways to pull memorized data back out. This makes it hard to &quot;delete&quot; a user&apos;s data from a fine-tuned model short of retraining, which can lead to compliance issues with regulations like GDPR.&lt;/p&gt;
&lt;h2&gt;The hybrid case: RAG and fine-tuning together&lt;/h2&gt;
&lt;p&gt;In 2026, the most sophisticated systems are not choosing one over the other. They combine both. You might fine-tune a smaller, cheaper model (like a Llama variant) to understand your company&apos;s specific API schemas and brand voice. Then, you use RAG to give that model access to live data.&lt;/p&gt;
&lt;p&gt;This combination allows the model to be both fast and accurate. The fine-tuning handles the &quot;how&quot; (formatting and style) while RAG handles the &quot;what&quot; (current facts and data). This is particularly effective when working with &lt;a href=&quot;https://ansezz.com/blog/claude-mcp-dev-tools/&quot;&gt;Claude and the Model Context Protocol (MCP)&lt;/a&gt;, where the model can use specific tools to fetch data while maintaining a pre-trained understanding of the environment.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/rag-vs-fine-tuning/hybrid-dashboard.webp&quot; alt=&quot;Developer workspace showing a hybrid AI setup pairing a fine-tuned model with a RAG pipeline&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Takeaways&lt;/h2&gt;
&lt;p&gt;Choosing between RAG and fine-tuning depends on your specific goals for data freshness, latency, and budget. Here are the core pillars to remember:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Use RAG for knowledge:&lt;/strong&gt; If your data changes frequently or you need to cite sources, RAG is the standard choice. It is easier to update and more transparent.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Use fine-tuning for behavior:&lt;/strong&gt; If you need specific output formats, a unique brand voice, or low-latency responses, fine-tuning is the way to go.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Evaluate your infrastructure:&lt;/strong&gt; RAG requires a robust data pipeline and vector database. Fine-tuning requires specialized training data and GPU resources.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Consider privacy early:&lt;/strong&gt; RAG is generally safer for handling sensitive or permission-based data because the model does not memorize the information.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Think hybrid for scale:&lt;/strong&gt; Combining a fine-tuned model for reasoning with a RAG pipeline for facts offers the most robust performance for complex applications.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Monitor costs:&lt;/strong&gt; Watch your token usage in RAG systems as context windows grow. Conversely, budget for periodic retraining if you choose the fine-tuning path.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Is your current AI architecture bottlenecked by the speed of retrieval or the accuracy of the model&apos;s internal knowledge? If you&apos;re deciding between these paths for a real product, &lt;a href=&quot;https://ansezz.com/services/&quot;&gt;here&apos;s how I help teams ship it&lt;/a&gt;.&lt;/p&gt;
</content:encoded><category>ai</category><category>rag</category><category>machine-learning</category><category>llm</category><category>architecture</category><category>laravel</category></item><item><title>Rate limiting vs throttling</title><link>https://ansezz.com/blog/rate-limiting-vs-throttling/</link><guid isPermaLink="true">https://ansezz.com/blog/rate-limiting-vs-throttling/</guid><description>The engineering difference between rate limiting and throttling, and how each secures your APIs and keeps infrastructure stable under heavy load.</description><pubDate>Fri, 12 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Your server is screaming, not because of a bug, but because of a success. A sudden viral tweet or a massive batch of automated requests has flooded your API. Without rate limiting or throttling in place, your database locks up, your CPU pegs at 100%, and the site goes down for everyone.&lt;/p&gt;
&lt;p&gt;This is the nightmare scenario for any engineering team. To prevent it, you need to control the flow of traffic. However, engineers often use the terms &quot;rate limiting&quot; and &quot;throttling&quot; interchangeably. While they both deal with traffic management, they solve the problem using different philosophies.&lt;/p&gt;
&lt;p&gt;Choosing the wrong one can lead to a degraded user experience or, worse, a complete system failure under pressure. This guide breaks down the technical nuances of both strategies and how to implement them in modern stacks like Laravel and Shopify.&lt;/p&gt;
&lt;h2&gt;The traffic surge crisis&lt;/h2&gt;
&lt;p&gt;The problem starts with resources. Every server has a finite amount of memory, compute, and bandwidth. When requests come in faster than your system can process them, a queue builds up. Once that queue exceeds the system&apos;s capacity, the service fails.&lt;/p&gt;
&lt;p&gt;This is not just about malicious actors or DDoS attacks. It is often about &quot;noisy neighbors&quot; in a multi-tenant environment or a poorly optimized loop in a client-side application. I have seen production environments crash simply because a mobile app&apos;s &quot;retry&quot; logic was too aggressive during a minor network hiccup.&lt;/p&gt;
&lt;p&gt;Rate limiting and throttling are your tools to enforce discipline. They ensure that no single user or service can monopolize your infrastructure. They are the gatekeepers of your &lt;a href=&quot;https://ansezz.com/blog/coolify-docker-saas-hosting/&quot;&gt;cloud infrastructure&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Defining rate limiting: the hard stop&lt;/h2&gt;
&lt;p&gt;Rate limiting is a policy-based approach. It defines a strict contract: &quot;You are allowed exactly X requests per Y amount of time.&quot; Once a user reaches that limit, the gate shuts.&lt;/p&gt;
&lt;p&gt;When a request exceeds the limit, the server immediately rejects it. This is typically done by returning an HTTP 429 &quot;Too Many Requests&quot; status code. The server does not spend any more resources on that request. It does not look up data in the database. It just says &quot;no.&quot;&lt;/p&gt;
&lt;h3&gt;Why use rate limiting?&lt;/h3&gt;
&lt;p&gt;Rate limiting is primarily about fairness and security. It protects your &lt;a href=&quot;https://ansezz.com/blog/api-gateway-ai-stack/&quot;&gt;API gateway&lt;/a&gt; from abuse. If you are running a SaaS, you might have different tiers. A free user gets 60 requests per minute, while a premium user gets 1,000.&lt;/p&gt;
&lt;p&gt;Rate limiting is easy to communicate to users. They can check their response headers (like &lt;code&gt;X-RateLimit-Limit&lt;/code&gt; and &lt;code&gt;X-RateLimit-Remaining&lt;/code&gt;) to see exactly where they stand. It is a binary state: you are either within your limit or you are blocked.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/rate-limiting-vs-throttling/stop-sign.webp&quot; alt=&quot;A digital stop sign blocking a flood of data packets&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Defining throttling: the gentle brake&lt;/h2&gt;
&lt;p&gt;Throttling is a runtime behavior designed to &quot;shape&quot; traffic. Instead of a hard rejection, throttling slows down the processing of requests. Think of it like a funnel. You can pour a bucket of water into it all at once, but it only drips out at a controlled, steady speed.&lt;/p&gt;
&lt;p&gt;In a throttled system, if you send too many requests, the server might add a delay to each response. It might queue the requests and process them as capacity becomes available. The goal is to smooth out spikes and avoid a &quot;jagged&quot; traffic pattern.&lt;/p&gt;
&lt;h3&gt;Why use throttling?&lt;/h3&gt;
&lt;p&gt;Throttling is excellent for protecting backend resources like databases or third-party APIs. If you know your database can only handle 500 writes per second, you might throttle incoming requests to stay just under that limit.&lt;/p&gt;
&lt;p&gt;It provides a better user experience for occasional spikes. Instead of a 429 error, the user might just see a slightly longer loading time. However, if the traffic remains high for too long, the queue will eventually fill up, and the system will have to start rejecting requests anyway.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/rate-limiting-vs-throttling/hourglass.webp&quot; alt=&quot;An hourglass regulating a stream of data packets&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Key algorithms: token vs leaky bucket&lt;/h2&gt;
&lt;p&gt;To implement these strategies, engineers rely on specific mathematical models. Understanding these is crucial for fine-tuning your system performance.&lt;/p&gt;
&lt;h3&gt;1. Token bucket (common for rate limiting)&lt;/h3&gt;
&lt;p&gt;Imagine a bucket that holds &quot;tokens.&quot; Every time a request comes in, a token is removed. If the bucket is empty, the request is rejected. Tokens are added back to the bucket at a constant rate.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Pros:&lt;/strong&gt; It allows for &quot;burstiness.&quot; If the bucket is full, a user can send a quick burst of requests until the tokens run out.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cons:&lt;/strong&gt; Hard to manage if the burst is too large for your downstream services.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;2. Leaky bucket (common for throttling)&lt;/h3&gt;
&lt;p&gt;Imagine a bucket with a small hole at the bottom. Requests are poured into the bucket. They &quot;leak&quot; out of the hole at a constant rate to be processed. If the bucket overflows, new requests are dropped.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Pros:&lt;/strong&gt; It ensures a completely stable, predictable flow of traffic to your backend.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cons:&lt;/strong&gt; It is very strict. It does not allow for bursts even if the system has idle capacity.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/rate-limiting-vs-throttling/leaky-bucket.webp&quot; alt=&quot;Leaky bucket algorithm diagram showing steady API output&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Practical implementation: Laravel and Shopify&lt;/h2&gt;
&lt;p&gt;How does this look in the real world? Let&apos;s look at two ecosystems where these patterns are vital.&lt;/p&gt;
&lt;h3&gt;Rate limiting in Laravel&lt;/h3&gt;
&lt;p&gt;Laravel makes rate limiting simple through its &lt;code&gt;RateLimiter&lt;/code&gt; facade and the &lt;code&gt;throttle&lt;/code&gt; middleware. The built-in limiter uses a fixed-window counter backed by your cache store.&lt;/p&gt;
&lt;p&gt;In Laravel 11 and 12, you define named limiters in the &lt;code&gt;boot&lt;/code&gt; method of &lt;code&gt;AppServiceProvider&lt;/code&gt; (the old &lt;code&gt;RouteServiceProvider&lt;/code&gt; was dropped from the default skeleton):&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;use Illuminate\Cache\RateLimiting\Limit;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\RateLimiter;

RateLimiter::for(&apos;api&apos;, function (Request $request) {
    return Limit::perMinute(60)-&amp;gt;by($request-&amp;gt;user()?-&amp;gt;id ?: $request-&amp;gt;ip());
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This tells Laravel to allow 60 requests per minute per user ID or IP address. If the limit is hit, Laravel automatically throws a &lt;code&gt;ThrottleRequestsException&lt;/code&gt;, which results in a 429 response. This is a classic &quot;hard stop&quot; rate limit.&lt;/p&gt;
&lt;h3&gt;Throttling in the Shopify API&lt;/h3&gt;
&lt;p&gt;Shopify uses a leaky bucket algorithm for its GraphQL Admin API on every plan, not just Plus. This is a clean example of throttling.&lt;/p&gt;
&lt;p&gt;When you make a GraphQL call, the response carries cost details under &lt;code&gt;extensions.cost&lt;/code&gt;, including the &lt;code&gt;requestedQueryCost&lt;/code&gt; and a &lt;code&gt;throttleStatus&lt;/code&gt;.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;GraphQL cost:&lt;/strong&gt; Each query is assigned a calculated cost based on the fields and connection sizes you request.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The bucket:&lt;/strong&gt; Your app has a bucket of &quot;points&quot; — 100 per second restore on Standard, 200 on Advanced, 1,000 on Plus. Points are spent on each query and restore over time.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The brake:&lt;/strong&gt; If you spend points faster than they restore, Shopify returns a &lt;code&gt;THROTTLED&lt;/code&gt; error.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For high-volume commerce, this means you have to build back-off logic into your application. A well-behaved client reads &lt;code&gt;throttleStatus.currentlyAvailable&lt;/code&gt; and slows down before it hits the wall, rather than firing blindly and retrying on every &lt;code&gt;THROTTLED&lt;/code&gt; error. When you do get throttled, wait long enough for &lt;code&gt;restoreRate&lt;/code&gt; to refill the points your next query needs.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Rate Limiting (Laravel Default)&lt;/th&gt;
&lt;th&gt;Throttling (Shopify API Style)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Primary Action&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Block (HTTP 429)&lt;/td&gt;
&lt;td&gt;Delay or Queue&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Best For&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Security &amp;amp; Quotas&lt;/td&gt;
&lt;td&gt;Resource Stability&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Logic&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Fixed Window&lt;/td&gt;
&lt;td&gt;Leaky Bucket&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;User Experience&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Instant Error&lt;/td&gt;
&lt;td&gt;Latency Spike&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;Architectural impact: security vs experience&lt;/h2&gt;
&lt;p&gt;When designing your system, you must decide where to place these controls.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;At the edge:&lt;/strong&gt; Implement rate limiting at the WAF or API gateway level. This stops malicious traffic before it even touches your application code. This saves money on compute costs.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Inside the application:&lt;/strong&gt; Implement throttling within your service logic. Use it when calling external APIs or writing to a shared database. Use a queue system like Redis or Amazon SQS to hold requests that exceed your immediate capacity.&lt;/p&gt;
&lt;p&gt;I have found that the most resilient systems use both. Rate limiting at the perimeter blocks the &quot;bad actors,&quot; while internal throttling ensures that your internal services don&apos;t melt down during a legitimate traffic surge. This is especially important when building &lt;a href=&quot;https://ansezz.com/blog/agentic-commerce-shopify/&quot;&gt;agentic commerce systems&lt;/a&gt; that may generate many automated API calls in a short period. And if those calls hit a paid LLM, the stakes shift from CPU to your bill — see &lt;a href=&quot;https://ansezz.com/blog/rate-limiting-ai-wallet/&quot;&gt;rate limiting and the denial-of-wallet problem&lt;/a&gt; for the token-cost angle.&lt;/p&gt;
&lt;h2&gt;Takeaways&lt;/h2&gt;
&lt;p&gt;Managing traffic is about balance. You want to provide a fast experience for users while keeping your infrastructure healthy.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Rate limiting is for policy.&lt;/strong&gt; Use it to enforce subscription tiers and block brute-force attacks.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Throttling is for stability.&lt;/strong&gt; Use it to smooth out traffic spikes and protect your database or external API dependencies.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Return 429 codes.&lt;/strong&gt; Always let the client know they are being limited. Include a &lt;code&gt;Retry-After&lt;/code&gt; header so they know when they can try again.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Monitor your limits.&lt;/strong&gt; Use dashboards to track how often users hit their limits. If 20% of your users are being limited, your limits might be too low — or your app might be inefficient.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Use Redis for state.&lt;/strong&gt; Both rate limiting and throttling require a fast, central store to track request counts. Redis is the industry standard for this.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;How do you decide between rejecting a request immediately or making the user wait 500ms longer to maintain system stability? If you&apos;re hardening an API for production, &lt;a href=&quot;https://ansezz.com/services/&quot;&gt;here&apos;s how I help teams ship it&lt;/a&gt;.&lt;/p&gt;
</content:encoded><category>devops</category><category>api-design</category><category>laravel</category><category>shopify</category><category>infrastructure</category><category>devops</category></item><item><title>RAG architectures: traditional, agentic, corrective</title><link>https://ansezz.com/blog/rag-architectures-traditional-agentic-corrective/</link><guid isPermaLink="true">https://ansezz.com/blog/rag-architectures-traditional-agentic-corrective/</guid><description>Compare traditional, agentic, and corrective RAG architectures, with the latency, cost, and accuracy trade-offs that decide which fits your AI app.</description><pubDate>Thu, 11 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Retrieval is no longer just about embeddings. Most developers build a basic RAG system only to find it hallucinating or failing on complex questions within a week of deployment. If your vector search returns the wrong context, your LLM will confidently lie to your face. This gap between basic search and reliable intelligence is why RAG architectures have evolved from simple pipelines into self-correcting, agentic systems.&lt;/p&gt;
&lt;p&gt;The problem with many initial AI implementations is their static nature. You feed a query into a vector database, pull some chunks, and hope the LLM makes sense of them. This is the traditional RAG model. It works for simple FAQs, but it breaks down when a query requires multi-step reasoning or when the retrieved data is irrelevant. The result is &quot;garbage in, garbage out&quot;: the model burns tokens trying to answer from junk context.&lt;/p&gt;
&lt;p&gt;This guide compares three RAG architectures so you can pick the right one for your workload: the speed of a traditional pipeline, the reasoning of agentic RAG, or the verification of corrective RAG (CRAG).&lt;/p&gt;
&lt;h2&gt;Traditional RAG: the linear standard&lt;/h2&gt;
&lt;p&gt;Traditional RAG is the foundation of most AI-driven applications. It follows a strictly linear, one-shot path: retrieve then generate. You start by converting your documents into vector embeddings and storing them in a database like pgvector or Pinecone. When a user asks a question, the system converts that query into an embedding, performs a similarity search, and injects the top results into the prompt.&lt;/p&gt;
&lt;p&gt;This architecture is prized for its low latency and simplicity. If you are building a simple internal search tool for a Shopify store or a basic documentation bot, Traditional RAG is often sufficient. It is cost-effective because it typically involves only one LLM call and one vector search operation.&lt;/p&gt;
&lt;p&gt;However, its simplicity is also its biggest weakness. It assumes that the initial retrieval step is always successful. If the vector search returns noise, the LLM has no mechanism to identify that the context is wrong. It will attempt to answer regardless. This often leads to the &lt;a href=&quot;https://ansezz.com/blog/7-rag-mistakes-production/&quot;&gt;7 RAG mistakes in production&lt;/a&gt; that plague early-stage AI projects.&lt;/p&gt;
&lt;h3&gt;Key components of traditional RAG&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Vector store:&lt;/strong&gt; holds document chunks as embeddings.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Retriever:&lt;/strong&gt; a similarity search function that pulls the top-k relevant chunks.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Generator:&lt;/strong&gt; the LLM that synthesizes an answer from the retrieved chunks.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Agentic RAG: the strategic pivot&lt;/h2&gt;
&lt;p&gt;Agentic RAG transforms the retrieval process from a passive pipeline into an active control loop. Instead of a fixed sequence, an LLM agent acts as a &quot;brain&quot; that manages the entire workflow. The agent can plan its approach, decide which tools to use, and iterate until it finds a satisfactory answer.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/rag-architectures-traditional-agentic-corrective/agentic-loop.webp&quot; alt=&quot;Pop-art comic diagram of an agentic RAG plan-act-observe loop with an LLM agent calling search tools&quot; /&gt;&lt;/p&gt;
&lt;p&gt;In an Agentic RAG system, the agent might decide that a single search is not enough. It might break a complex user request into three sub-queries, search different data sources for each, and then synthesize the final answer. This is particularly useful for &lt;a href=&quot;https://ansezz.com/blog/agentic-commerce-shopify/&quot;&gt;agentic commerce on Shopify&lt;/a&gt; where a user might ask for a comparison between multiple products across different categories.&lt;/p&gt;
&lt;p&gt;The core of this architecture is the plan-act-observe cycle (the same loop behind ReAct-style agents). The agent plans a step, performs an action (like calling a search tool), observes the result, and decides whether it needs more information. This iterative nature lets it solve multi-hop problems, where the answer to the first part of a question provides the search terms for the second.&lt;/p&gt;
&lt;p&gt;While highly powerful, Agentic RAG is more expensive and slower than traditional methods. Each iteration requires another LLM call. This increases both the token cost and the time the user spends waiting for a response. Managing these loops requires robust infrastructure, often involving an &lt;a href=&quot;https://ansezz.com/blog/api-gateway-ai-stack/&quot;&gt;API gateway in the AI stack&lt;/a&gt; to handle the increased traffic and orchestration complexity.&lt;/p&gt;
&lt;h2&gt;Corrective RAG (CRAG): the self-healing layer&lt;/h2&gt;
&lt;p&gt;Corrective RAG, or CRAG, adds a self-correction step to retrieval. Its goal is to cut hallucinations by inserting a lightweight retrieval evaluator between the retrieval and generation steps to judge how relevant the retrieved documents are. The original &lt;a href=&quot;https://arxiv.org/abs/2401.15884&quot;&gt;CRAG paper&lt;/a&gt; uses a fine-tuned T5-large model as that evaluator.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/rag-architectures-traditional-agentic-corrective/crag-critic.webp&quot; alt=&quot;Pop-art comic diagram of a corrective RAG critic scoring retrieved chunks before generation&quot; /&gt;&lt;/p&gt;
&lt;p&gt;The evaluator scores each retrieval into one of three confidence levels: correct, incorrect, or ambiguous. When confidence is high (correct), CRAG keeps the retrieved context but refines it with a decompose-then-recompose step that strips out irrelevant text before generation. When the retrieval is incorrect, CRAG discards it and triggers a large-scale web search to pull in more reliable knowledge. The ambiguous case combines both: refined retrieval plus web search results.&lt;/p&gt;
&lt;p&gt;CRAG suits high-stakes environments where accuracy is non-negotiable. It brings a layer of grounded verification to the otherwise probabilistic behavior of LLMs, deciding whether to trust, refine, or replace retrieved context. By checking whether the ground truth is actually present in the retrieved data, CRAG keeps the model from inventing facts when the database comes up empty.&lt;/p&gt;
&lt;h3&gt;How the CRAG loop works&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Retrieve:&lt;/strong&gt; fetch initial context chunks.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Evaluate:&lt;/strong&gt; a lightweight evaluator scores each chunk&apos;s relevance into correct, incorrect, or ambiguous.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Correct:&lt;/strong&gt; if confidence is low, trigger a secondary retrieval (commonly a web search) and refine the context.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Generate:&lt;/strong&gt; synthesize the final answer only from verified or corrected context.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Comparison: speed vs accuracy vs reasoning&lt;/h2&gt;
&lt;p&gt;Choosing between these RAG architectures is a trade-off between performance, cost, and complexity. Use the table below to guide your decision. If you are still settling on the storage layer underneath, &lt;a href=&quot;https://ansezz.com/blog/picking-the-right-rag-stack/&quot;&gt;picking the right RAG stack&lt;/a&gt; covers the vector database choices in depth.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Traditional RAG&lt;/th&gt;
&lt;th&gt;Agentic RAG&lt;/th&gt;
&lt;th&gt;Corrective RAG&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Flow Type&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Linear&lt;/td&gt;
&lt;td&gt;Cyclic / Iterative&lt;/td&gt;
&lt;td&gt;Evaluative Loop&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Latency&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Cost&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;td&gt;Medium-High&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Use Case&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Simple FAQ, Lookup&lt;/td&gt;
&lt;td&gt;Complex Research, Workflows&lt;/td&gt;
&lt;td&gt;High-Accuracy Q&amp;amp;A&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Reliability&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Moderate&lt;/td&gt;
&lt;td&gt;High (Reasoning)&lt;/td&gt;
&lt;td&gt;Very High (Grounding)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Implementation&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Easy&lt;/td&gt;
&lt;td&gt;Complex&lt;/td&gt;
&lt;td&gt;Moderate&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;For most startups, starting with Traditional RAG and then moving toward a Corrective layer is the most logical path. It allows you to ship quickly while providing a roadmap for increasing reliability as your dataset grows.&lt;/p&gt;
&lt;h2&gt;Implementation in production: Laravel and cloud&lt;/h2&gt;
&lt;p&gt;Building these architectures takes more than an LLM API key. It needs a robust backend to handle state, queues, and data processing. In a Laravel environment, you can run the agentic loops with job queues and dedicated service classes. Laravel&apos;s ecosystem is well-suited for the orchestrator logic that drives an agentic RAG system.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/rag-architectures-traditional-agentic-corrective/code.webp&quot; alt=&quot;Pop-art comic illustration of a Laravel corrective RAG orchestrator code snippet&quot; /&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// Conceptual Laravel Service for a Corrective RAG Flow
class RagOrchestrator {
    public function handleQuery(string $userQuery) {
        // 1. Retrieve initial chunks
        $chunks = $this-&amp;gt;vectorStore-&amp;gt;search($userQuery);

        // 2. Evaluate with a Critic
        $evaluation = $this-&amp;gt;critic-&amp;gt;evaluate($userQuery, $chunks);

        if ($evaluation-&amp;gt;isIrrelevant()) {
            // 3. Corrective action: Web Search or Retry
            $chunks = $this-&amp;gt;webSearch-&amp;gt;search($userQuery);
        }

        // 4. Final generation
        return $this-&amp;gt;llm-&amp;gt;generate($userQuery, $chunks);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When deploying these systems, Docker and cloud infrastructure management become critical. You need to scale your vector database and your orchestration layer independently. Tools like Coolify for self-hosting or managed GCP services help with the compute demands of multi-step agentic loops.&lt;/p&gt;
&lt;h3&gt;Technical takeaways for engineers&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Traditional RAG&lt;/strong&gt; is for speed and simplicity. It is the baseline for all projects.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Agentic RAG&lt;/strong&gt; is for thinking tasks. Use it when the user needs a consultant, not just a search bar.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Corrective RAG&lt;/strong&gt; is for truth tasks. Use it when hallucinations are a business risk.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Caching&lt;/strong&gt; is non-negotiable in agentic RAG. Cache the results of sub-queries to cut cost and latency for recurring questions.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Takeaways&lt;/h2&gt;
&lt;p&gt;RAG is no longer a &quot;one size fits all&quot; solution. The move toward Agentic and Corrective systems signifies a shift in AI engineering where the focus is on reliability rather than just capability. By choosing the right architecture, you ensure that your application provides value instead of just noise.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Start with Traditional RAG to validate your data and basic retrieval quality.&lt;/li&gt;
&lt;li&gt;Implement a Critic layer (Corrective RAG) early if accuracy is your primary KPI.&lt;/li&gt;
&lt;li&gt;Reserve Agentic RAG for workflows that require actual decision-making and tool use.&lt;/li&gt;
&lt;li&gt;Monitor your retrieval hit rate and token usage religiously.&lt;/li&gt;
&lt;li&gt;Use a modular backend structure like Laravel to manage the complexity of multi-loop architectures.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Which architecture currently provides the best balance of cost and accuracy for your production workloads? If you&apos;re deciding which RAG architecture fits your product, &lt;a href=&quot;https://ansezz.com/services/&quot;&gt;here&apos;s how I help teams ship it&lt;/a&gt;.&lt;/p&gt;
</content:encoded><category>ai</category><category>rag</category><category>agentic-ai</category><category>llm</category><category>vector-search</category><category>ai-engineering</category></item><item><title>Monolith vs microservices: how to choose</title><link>https://ansezz.com/blog/monolith-vs-microservices/</link><guid isPermaLink="true">https://ansezz.com/blog/monolith-vs-microservices/</guid><description>Monolith vs microservices: the real technical trade-offs for Laravel and Shopify teams, when each pays off, and why a modular monolith often wins.</description><pubDate>Wed, 10 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Complexity is the silent killer of engineering velocity. You start with a clean codebase and a clear vision. Within months, your deployment times have doubled. Your team is tripping over each other&apos;s pull requests. You are facing the classic architectural crossroads: monolith vs microservices. Choosing the wrong path early on can lead to catastrophic technical debt or unnecessary operational overhead that drains your budget before you find product-market fit.&lt;/p&gt;
&lt;p&gt;In the world of modern web development, particularly for those building with Laravel or Shopify, the pressure to adopt microservices is immense. The industry often treats distributed systems as the default goal. However, for many businesses, a well-structured monolith is not just a starting point. It is often the most efficient way to scale. This guide breaks down the engineering substance behind these two patterns to help you make an informed decision for your next project on Google Cloud or AWS.&lt;/p&gt;
&lt;h2&gt;Defining the monolithic architecture&lt;/h2&gt;
&lt;p&gt;A monolithic architecture is a unified software model where all components of an application are interconnected and interdependent. In a Laravel context, this usually means your routes, controllers, models, and background jobs live in a single repository. It is a single deployable unit. When you push code, the entire application is built and shipped to your server or container.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/monolith-vs-microservices/monolith-architecture.webp&quot; alt=&quot;Monolithic architecture diagram in pop-art comic style showing unified layers&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Monoliths are often unfairly criticized as &quot;legacy.&quot; In reality, they offer significant advantages for rapid development. Because all modules share the same memory space and database, communication between parts of the system is nearly instantaneous. You do not have to worry about network latency, API versioning, or complex distributed transactions. For a Shopify app or a custom web solution, this simplicity translates to faster shipping cycles.&lt;/p&gt;
&lt;h3&gt;The benefits of a single codebase&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Simplified deployment:&lt;/strong&gt; You only need one CI/CD pipeline. Whether you are using GitHub Actions to deploy to a &lt;a href=&quot;https://ansezz.com/blog/coolify-docker-saas-hosting/&quot;&gt;Docker-based host&lt;/a&gt; or a VPS, the process remains straightforward.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cross-cutting concerns:&lt;/strong&gt; Implementing features like authentication, logging, and caching is easier when they are centralized. You don&apos;t have to replicate these services across multiple endpoints.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;End-to-end testing:&lt;/strong&gt; Testing the entire user journey is simpler because you can run the whole stack on a single machine without complex orchestration.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;The microservices shift&lt;/h2&gt;
&lt;p&gt;Microservices break the application into small, independent services that communicate over a network. Each service focuses on a specific business domain, such as billing, user management, or inventory. They are often containerized using Docker and managed with orchestrators like Kubernetes or Google Kubernetes Engine (GKE).&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/monolith-vs-microservices/microservices-mesh.webp&quot; alt=&quot;Microservices mesh diagram in pop-art style showing interconnected services&quot; /&gt;&lt;/p&gt;
&lt;p&gt;The primary driver for microservices is scalability and team autonomy. When your team grows beyond 15 or 20 developers, a monolith can become a bottleneck. Microservices allow different squads to own specific parts of the system. One team can update the billing service in Go while another updates the Shopify sync engine in PHP. This isolation prevents a bug in one module from taking down the entire application.&lt;/p&gt;
&lt;h3&gt;When to consider microservices&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Independent scaling needs:&lt;/strong&gt; If your Shopify webhook processing requires massive CPU resources but your admin dashboard is lightly used, microservices allow you to scale only the heavy components.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Technology diversity:&lt;/strong&gt; You might need a Python service for AI-driven &lt;a href=&quot;https://ansezz.com/blog/7-rag-mistakes-production/&quot;&gt;RAG systems&lt;/a&gt; while keeping your core business logic in Laravel.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Fault isolation:&lt;/strong&gt; A crash in the reporting service should not prevent customers from checking out.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Monolith vs microservices: comparing the trade-offs&lt;/h2&gt;
&lt;p&gt;The choice between monolith and microservices is always a trade-off between simplicity and flexibility. There is no silver bullet. You weigh operational cost against developer experience.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Monolith&lt;/th&gt;
&lt;th&gt;Microservices&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Operational complexity&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Development speed&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Fast (early stage)&lt;/td&gt;
&lt;td&gt;Slow (early stage)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Scalability&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Vertical / horizontal (whole)&lt;/td&gt;
&lt;td&gt;Independent per service&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Data consistency&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Strong (ACID)&lt;/td&gt;
&lt;td&gt;Eventual consistency&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Network latency&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Minimal&lt;/td&gt;
&lt;td&gt;Significant (API hops)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Testing&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Simple&lt;/td&gt;
&lt;td&gt;Complex distributed testing&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3&gt;The hidden cost of distribution&lt;/h3&gt;
&lt;p&gt;Microservices introduce &quot;network tax.&quot; Every time one service calls another, you add latency. You also have to handle partial failures. If the user service is down, how does the order service react? Implementing patterns like &lt;a href=&quot;https://ansezz.com/blog/circuit-breakers-vector-db/&quot;&gt;circuit breakers&lt;/a&gt; and retries becomes mandatory. This adds a layer of code that has nothing to do with your business logic. It is pure infrastructure overhead.&lt;/p&gt;
&lt;h2&gt;Laravel and Shopify context&lt;/h2&gt;
&lt;p&gt;For most &lt;a href=&quot;https://ansezz.com/blog/category/shopify/&quot;&gt;Shopify development&lt;/a&gt; or &lt;a href=&quot;https://ansezz.com/blog/category/laravel/&quot;&gt;Laravel projects&lt;/a&gt;, the &quot;Majestic Monolith&quot; is the superior choice. Shopify provides a robust platform-as-a-service (PaaS) foundation. Your backend&apos;s primary job is often to handle OAuth, process webhooks, and manage a custom database.&lt;/p&gt;
&lt;p&gt;Splitting a Shopify app into microservices prematurely often leads to &quot;distributed monolith&quot; syndrome. This is where you have the complexity of microservices but the components are still tightly coupled. If you cannot deploy Service A without also deploying Service B, you have failed to achieve the benefits of the architecture. You have only added network latency and deployment pain.&lt;/p&gt;
&lt;h2&gt;Cloud strategy on GCP&lt;/h2&gt;
&lt;p&gt;Google Cloud Platform (GCP) offers excellent tools for both patterns. For a monolith, Cloud Run is an exceptional choice. It abstracts away the server management and scales your container based on request traffic. It is cost-effective because you only pay when your code is running.&lt;/p&gt;
&lt;p&gt;For microservices, you might utilize GKE or a series of Cloud Run services connected via &lt;a href=&quot;https://ansezz.com/blog/event-driven-pubsub/&quot;&gt;Pub/Sub&lt;/a&gt;. This setup allows for asynchronous communication. When a Shopify order is created, the &quot;Order Service&quot; publishes an event to Pub/Sub. The &quot;Shipping Service&quot; and &quot;Email Service&quot; both listen for that event and act independently. This architecture is powerful but requires significant investment in observability tools like Cloud Logging and Cloud Trace to debug issues across service boundaries.&lt;/p&gt;
&lt;h2&gt;The hybrid approach: modular monolith&lt;/h2&gt;
&lt;p&gt;You do not have to choose between a &quot;big ball of mud&quot; and a complex mesh of services. The modular monolith is an effective middle ground. In this pattern, you maintain a single codebase and database, but you strictly enforce boundaries within the code.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/monolith-vs-microservices/modular-monolith.webp&quot; alt=&quot;Modular monolith diagram showing bounded modules inside a single deployable codebase&quot; /&gt;&lt;/p&gt;
&lt;p&gt;In Laravel, this means using separate namespaces or even local packages for different domains. Your &quot;Billing&quot; code should not directly call the &quot;Inventory&quot; models. Instead, it should use internal interfaces or events. This approach gives you the deployment simplicity of a monolith while making it easy to extract a specific module into a standalone microservice later. I go deeper on the mechanics — contracts, schema-per-module, and the friction signals that justify a split — in &lt;a href=&quot;https://ansezz.com/blog/modular-monolith-first/&quot;&gt;modular monoliths first&lt;/a&gt;. It is a path of &quot;deferred decision making&quot; which is often the most strategic move in software engineering. When the time finally comes to carve a module out, my guide on going &lt;a href=&quot;https://ansezz.com/blog/monolith-to-microservices/&quot;&gt;from monolith to microservices&lt;/a&gt; walks through the strangler-fig migration step by step.&lt;/p&gt;
&lt;h2&gt;Takeaways&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Start with a monolith.&lt;/strong&gt; Unless you are building for a massive organization with multiple teams, the operational overhead of microservices will slow you down.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Enforce boundaries early.&lt;/strong&gt; Use a modular structure within your monolith to prevent spaghetti code. This makes future scaling possible.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Leverage managed services.&lt;/strong&gt; Use GCP Cloud Run for hosting and Cloud SQL for your database to minimize the DevOps burden.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Scale components, not just apps.&lt;/strong&gt; Use queues (Laravel Horizon) and background workers to handle heavy tasks before splitting into full microservices.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Monitor and trace.&lt;/strong&gt; Regardless of architecture, invest in centralized logging and performance monitoring to identify bottlenecks before they impact users.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Are you finding that your current monolithic deployment is hitting performance ceilings that vertical scaling can no longer solve? If you&apos;re weighing a split, &lt;a href=&quot;https://ansezz.com/services/&quot;&gt;here&apos;s how I help teams make that call&lt;/a&gt;.&lt;/p&gt;
</content:encoded><category>architecture</category><category>devops</category><category>laravel</category><category>shopify</category><category>infrastructure</category><category>cloud-platforms</category></item><item><title>Prompt engineering vs context engineering</title><link>https://ansezz.com/blog/prompt-engineering-vs-context-engineering/</link><guid isPermaLink="true">https://ansezz.com/blog/prompt-engineering-vs-context-engineering/</guid><description>The shift from instruction design to data infrastructure — how context engineering uses RAG and MCP to build robust, accurate AI systems.</description><pubDate>Wed, 10 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;You spent three hours tweaking a system prompt to make your LLM stop hallucinating about customer orders. It worked for ten minutes. Then a user asked a question about a return policy from 2024, and the model confidently invented a 90-day cash-back guarantee that doesn&apos;t exist. You have hit the instruction ceiling. No matter how many &quot;You are a helpful assistant&quot; or &quot;Think step-by-step&quot; phrases you add, the model cannot reason its way out of a lack of data.&lt;/p&gt;
&lt;p&gt;The problem is that you are treating a data problem as a linguistic one. Prompt engineering is about the &quot;how.&quot; Context engineering is about the &quot;what.&quot; As we move from simple chatbots to complex agentic systems, the focus is shifting away from how we talk to models and toward how we feed them.&lt;/p&gt;
&lt;h2&gt;The instruction ceiling&lt;/h2&gt;
&lt;p&gt;Prompt engineering was the first discipline of the AI era. It involves crafting precise instructions, examples, and formatting rules to steer a Large Language Model (LLM). You might spend days testing different verb phrases or adding few-shot examples to get the output exactly right. This works well for creative writing or basic text transformation.&lt;/p&gt;
&lt;p&gt;However, prompt engineering is fundamentally stateless and limited. You are trying to squeeze logic, data, and constraints into a finite context window. When the window gets too crowded, the model loses track of earlier instructions. This is the &quot;lost in the middle&quot; phenomenon. Relying solely on prompts means you are forcing the model to rely on its training data, which is static and often outdated.&lt;/p&gt;
&lt;p&gt;In a production environment, especially for &lt;a href=&quot;https://ansezz.com/blog/agentic-commerce-shopify/&quot;&gt;Shopify development&lt;/a&gt; or custom enterprise software, static instructions are dangerous. If your model doesn&apos;t know the real-time stock levels or the specific technical specs of a new product, no amount of clever phrasing will prevent a hallucination.&lt;/p&gt;
&lt;h2&gt;The infrastructure shift: what is context engineering?&lt;/h2&gt;
&lt;p&gt;Context engineering is the programmatic management of the entire informational environment surrounding the model. It is not just about the text you send; it is about the architecture that decides what data, tools, and memories are available to the model at any given moment.&lt;/p&gt;
&lt;p&gt;If prompt engineering is writing a script for an actor, context engineering is building the entire stage, providing the props, and hiring the research team that whispers facts into the actor&apos;s earpiece.&lt;/p&gt;
&lt;p&gt;Context engineering involves several moving parts:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Retrieval pipelines&lt;/strong&gt;: Dynamically fetching relevant data from external sources.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Tool governance&lt;/strong&gt;: Deciding which APIs or functions the model can call.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Memory management&lt;/strong&gt;: Storing and retrieving past interactions to maintain continuity.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;State orchestration&lt;/strong&gt;: Tracking where the user is in a specific workflow or business process.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;By shifting from &quot;telling&quot; to &quot;showing,&quot; you reduce the cognitive load on the model. Instead of asking it to &quot;remember all our policies,&quot; you build a system that finds the one relevant policy and places it directly in front of the model.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/prompt-engineering-vs-context-engineering/context-architecture.webp&quot; alt=&quot;Architecture diagram showing data flowing from vector DBs and APIs into a central context manager&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;RAG: the context delivery engine&lt;/h2&gt;
&lt;p&gt;Retrieval-augmented generation (RAG) is the most common implementation of context engineering. It solves the &quot;knowledge gap&quot; by connecting your LLM to a vector store — Postgres with the &lt;a href=&quot;https://ansezz.com/blog/picking-the-right-rag-stack/&quot;&gt;pgvector extension&lt;/a&gt;, or a dedicated database like Pinecone, Weaviate, or Qdrant.&lt;/p&gt;
&lt;p&gt;When a user asks a question, the system doesn&apos;t just pass the question to the model. First, it converts the query into an embedding (a mathematical representation of meaning). It then searches a database for the most semantically similar documents. These &quot;context snippets&quot; are then injected into the prompt.&lt;/p&gt;
&lt;p&gt;The difference is subtle but massive. In prompt engineering, you might say: &quot;Answer questions based on our manual.&quot; In context engineering, you say: &quot;Here are the three specific paragraphs from our manual that answer the user&apos;s question. Use them.&quot; This significantly reduces the chance of the model making things up. However, even RAG has pitfalls. To avoid common errors, you need to understand the nuances of &lt;a href=&quot;https://ansezz.com/blog/7-rag-mistakes-production/&quot;&gt;RAG mistakes in production&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;MCP: the universal data plumbing&lt;/h2&gt;
&lt;p&gt;One of the most useful developments in context engineering is the &lt;strong&gt;Model Context Protocol (MCP)&lt;/strong&gt;. Introduced by Anthropic in November 2024 and since adopted as an open standard by OpenAI and Google, MCP serves as a standardized bridge between models and the systems where data lives.&lt;/p&gt;
&lt;p&gt;Imagine you are building an AI agent that needs to check GitHub issues, look up a customer in a CRM, and then write a technical summary. In a traditional setup, you would have to write custom integration code for every single data source. With MCP, you can use &quot;context servers&quot; that expose these data sources in a format the model understands natively.&lt;/p&gt;
&lt;p&gt;MCP allows the model to &quot;pull&quot; context as needed. It transforms the model from a passive receiver of a prompt into an active explorer of a data ecosystem. This is a core part of building a modern &lt;a href=&quot;https://ansezz.com/blog/api-gateway-ai-stack/&quot;&gt;API gateway for AI stacks&lt;/a&gt;. It turns context from a static block of text into a dynamic, queryable interface.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Prompt Engineering&lt;/th&gt;
&lt;th&gt;Context Engineering&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Focus&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Phrasing and Tone&lt;/td&gt;
&lt;td&gt;Data and Tools&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Tooling&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Text Editors, Prompt Playgrounds&lt;/td&gt;
&lt;td&gt;Vector DBs, MCP, RAG Pipelines&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;State&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Mostly Stateless&lt;/td&gt;
&lt;td&gt;Stateful and Persistent&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Scalability&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Hard to maintain as docs grow&lt;/td&gt;
&lt;td&gt;Built for millions of documents&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Primary Goal&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Better behavior&lt;/td&gt;
&lt;td&gt;Better accuracy&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;Memory and state management&lt;/h2&gt;
&lt;p&gt;A major part of context engineering that prompt engineering ignores is the concept of long-term memory. Prompting usually focuses on the &quot;now.&quot; Context engineering looks at the &quot;always.&quot;&lt;/p&gt;
&lt;p&gt;In a Laravel-based application, you might use a database or a Redis store to maintain a &quot;memory&quot; of user preferences or past interactions. When the user returns, the context engineering layer retrieves these fragments and summarizes them for the model.&lt;/p&gt;
&lt;p&gt;This is more than just passing the last five messages. It involves:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Summarization&lt;/strong&gt;: Condensing long histories into high-density tokens.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Metadata filtering&lt;/strong&gt;: Only pulling memories relevant to the current topic.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Hierarchy&lt;/strong&gt;: Deciding which memories are &quot;core&quot; and which are &quot;ephemeral.&quot;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/prompt-engineering-vs-context-engineering/context-window-dashboard.webp&quot; alt=&quot;Dashboard visualizing a model&apos;s context window split into different data blocks&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Engineering the future: agents vs chatbots&lt;/h2&gt;
&lt;p&gt;The shift to context engineering is what separates a basic chatbot from a true AI agent. A chatbot waits for a prompt and responds. An agent lives within a context. It has access to tools (via MCP), knowledge (via RAG), and history (via memory stores).&lt;/p&gt;
&lt;p&gt;When you build with a context-first mindset, your code looks different. You spend less time in the OpenAI playground and more time building robust data connectors. You focus on the &lt;strong&gt;purity of your data&lt;/strong&gt; rather than the &lt;strong&gt;cleverness of your adjectives&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;For developers working with stacks like Laravel and Vue.js, context engineering means building a &quot;context layer&quot; in your middleware. This layer is responsible for gathering all the necessary &quot;props&quot; for the AI before it ever sees the user&apos;s input.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// Example of a simple context orchestrator in Laravel
class AIContextManager
{
    public function buildContext(User $user, string $query): array
    {
        $knowledge = $this-&amp;gt;vectorStore-&amp;gt;search($query);
        $history = $this-&amp;gt;memory-&amp;gt;getRecent($user-&amp;gt;id);
        $tools = $this-&amp;gt;mcp-&amp;gt;getAvailableTools([&apos;inventory&apos;, &apos;shipping&apos;]);

        return [
            &apos;system_prompt&apos; =&amp;gt; view(&apos;prompts.system&apos;)-&amp;gt;render(),
            &apos;retrieved_docs&apos; =&amp;gt; $knowledge,
            &apos;user_history&apos; =&amp;gt; $history,
            &apos;available_tools&apos; =&amp;gt; $tools,
            &apos;user_query&apos; =&amp;gt; $query,
        ];
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/prompt-engineering-vs-context-engineering/context-manager-code.webp&quot; alt=&quot;Code snippet card showing a PHP class for managing AI context&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Takeaways&lt;/h2&gt;
&lt;p&gt;Context engineering is the professional evolution of prompt engineering. While knowing how to talk to a model remains useful, knowing how to build the infrastructure that informs the model is what creates value.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Prompts are instructions; context is knowledge.&lt;/strong&gt; Stop trying to teach the model your entire business logic inside a system prompt.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Invest in RAG early.&lt;/strong&gt; A vector store like Postgres with pgvector gives you a scalable way to handle growing documentation and data.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Adopt MCP for tool-calling.&lt;/strong&gt; Using the Model Context Protocol standardizes how your AI interacts with your existing APIs and databases.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Manage state outside the prompt.&lt;/strong&gt; Use your application backend (Laravel, Node, etc.) to handle memory and session state rather than relying on the LLM&apos;s limited window.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Focus on data quality.&lt;/strong&gt; A perfect RAG system with bad data will still produce bad results. Context engineering is, at its heart, a data engineering discipline.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;How are you balancing complex system prompts against external retrieval pipelines in your current AI implementation? If you&apos;re building that context layer for production, &lt;a href=&quot;https://ansezz.com/services/&quot;&gt;here&apos;s how I help teams ship it&lt;/a&gt;.&lt;/p&gt;
</content:encoded><category>ai</category><category>ai</category><category>rag</category><category>mcp</category><category>vector-search</category><category>laravel</category><category>devops</category></item><item><title>ML engineer vs AI engineer</title><link>https://ansezz.com/blog/ml-engineer-vs-ai-engineer/</link><guid isPermaLink="true">https://ansezz.com/blog/ml-engineer-vs-ai-engineer/</guid><description>ML engineer vs AI engineer: who trains the model, who orchestrates the system, their diverging toolsets, and which role your AI roadmap actually needs.</description><pubDate>Tue, 09 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;You are ready to ship AI features, but your hiring roadmap is a mess of overlapping buzzwords and conflicting job descriptions. Hiring a researcher to build a production chatbot often results in a month of experiments with zero shipped code. Conversely, asking a standard full-stack developer to optimize a vector search index usually ends in a high-latency disaster. The gap between training a model and orchestrating a system is wider than most founders realize. The choice between an ML engineer and an AI engineer determines whether you are building a proprietary brain or a high-performance application powered by existing intelligence.&lt;/p&gt;
&lt;h2&gt;The model scientist: defining the ML engineer&lt;/h2&gt;
&lt;p&gt;The machine learning engineer lives in the world of weights, biases, and data distributions. Their primary goal is to create, train, or fine-tune models that solve specific predictive or generative tasks. They don&apos;t just consume an API. They build the logic that lives inside the API.&lt;/p&gt;
&lt;p&gt;In a typical day, an ML engineer might be working with PyTorch or TensorFlow to design a custom architecture. They handle the &quot;dirty work&quot; of data: cleaning massive datasets, managing feature stores, and dealing with training drift. Their mental model is rooted in applied statistics and experimental iteration. If you need a model to predict fraudulent transactions based on proprietary financial data, you need an ML engineer. They ensure the model generalizes well and doesn&apos;t overfit the training set.&lt;/p&gt;
&lt;p&gt;Their technical stack is heavy on the backend of the data world. You will see them using tools like Spark for data processing, Weights &amp;amp; Biases for experiment tracking, and NVIDIA GPUs for heavy lifting. The output of their work is a serialized model file or a dedicated inference service that other systems can call. This is a deep-tech role focused on the &quot;how&quot; of intelligence — the distinction between the &lt;a href=&quot;https://ansezz.com/blog/training-vs-inference/&quot;&gt;training and inference&lt;/a&gt; phases shapes most of their day.&lt;/p&gt;
&lt;h2&gt;The systems architect: defining the AI engineer&lt;/h2&gt;
&lt;p&gt;The AI engineer is a relatively new breed of developer. They treat the model as a black-box service. Their focus is not on training the LLM, but on building the infrastructure around it to make it useful for users. They are the bridge between raw intelligence and a functional product.&lt;/p&gt;
&lt;p&gt;An AI engineer spends their time on system design and software engineering. They work with foundation models like Claude, GPT, and Gemini through APIs and focus on RAG (retrieval-augmented generation) and agentic workflows. Instead of tweaking hyperparameters, they are tweaking system prompts and designing tool-use schemas.&lt;/p&gt;
&lt;p&gt;The AI engineer is responsible for the user experience of AI. This includes managing latency, implementing guardrails, and ensuring the output is structured correctly for the frontend. If you are building a custom support bot on &lt;a href=&quot;https://ansezz.com/blog/agentic-commerce-shopify/&quot;&gt;Shopify&lt;/a&gt;, you want an AI engineer. They know how to connect the store&apos;s data to the LLM and handle the complexities of multi-turn conversations.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/ml-engineer-vs-ai-engineer/rag-architecture.webp&quot; alt=&quot;Diagram of a RAG pipeline showing a user query flowing through retrieval, a vector database, and a foundation model to a cited response&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Technical stack: a side-by-side comparison&lt;/h2&gt;
&lt;p&gt;Understanding the differences requires looking at the tools used in 2026. While there is overlap, the primary focus areas are distinct.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;ML Engineer&lt;/th&gt;
&lt;th&gt;AI Engineer&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Core Models&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Custom PyTorch/TensorFlow, XGBoost&lt;/td&gt;
&lt;td&gt;Claude, GPT, Llama, Gemini APIs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Primary Task&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Training, Fine-tuning, Evaluation&lt;/td&gt;
&lt;td&gt;Prompt Engineering, RAG, Agents&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Data Focus&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Feature engineering, labeling, drift&lt;/td&gt;
&lt;td&gt;Chunking, indexing, retrieval quality&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Programming&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Python (heavy), C++, CUDA&lt;/td&gt;
&lt;td&gt;Python, TypeScript, PHP&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Infrastructure&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Kubernetes, GPUs, Feature Stores&lt;/td&gt;
&lt;td&gt;Vector DBs (pgvector), MCP, Serverless&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Success Metric&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;F1 Score, Accuracy, Loss curves&lt;/td&gt;
&lt;td&gt;Latency, Hallucination rate, User NPS&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The ML engineer owns the pipeline from raw data to a deployed model. The AI engineer owns the pipeline from a user query to a relevant, cited response. One builds the engine; the other builds the car and chooses the best fuel.&lt;/p&gt;
&lt;h2&gt;The RAG bridge: where the roles meet&lt;/h2&gt;
&lt;p&gt;Retrieval-augmented generation (RAG) is the most common point of collision between these two roles. A robust RAG system requires high-quality embeddings and a fast, scalable vector database.&lt;/p&gt;
&lt;p&gt;An ML engineer might be the one training a domain-specific embedding model or a custom reranker to improve the relevance of search results. They look at the mathematical similarity between vectors and optimize the distance functions.&lt;/p&gt;
&lt;p&gt;However, the AI engineer is usually the one who implements the end-to-end RAG system. They decide how to chunk the documents, how to manage metadata, and how to implement &lt;a href=&quot;https://ansezz.com/blog/vector-search-vs-graph-search/&quot;&gt;hybrid search&lt;/a&gt; using tools like pgvector or Pinecone. They also handle the critical task of context management: ensuring the LLM gets exactly what it needs without exceeding its context window or blowing the budget. If you are just starting, avoid common &lt;a href=&quot;https://ansezz.com/blog/7-rag-mistakes-production/&quot;&gt;RAG mistakes in production&lt;/a&gt; by focusing on retrieval quality before model size.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/ml-engineer-vs-ai-engineer/training-vs-orchestration.webp&quot; alt=&quot;Side-by-side comparison of the ML engineer&apos;s model-training loop versus the AI engineer&apos;s system-orchestration loop&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Agentic systems: the new frontier&lt;/h2&gt;
&lt;p&gt;In 2026, the focus has shifted from simple chatbots to agentic systems. These are AI agents that can use tools, browse the web, and execute code to complete complex tasks.&lt;/p&gt;
&lt;p&gt;AI engineers are the primary drivers of this shift. They use frameworks like LangGraph or the Claude Agent SDK to build multi-step workflows. They are the ones implementing the &lt;a href=&quot;https://ansezz.com/blog/claude-mcp-dev-tools/&quot;&gt;Model Context Protocol (MCP)&lt;/a&gt; to give their agents access to internal databases, local files, and external APIs. This requires deep software engineering skills, as agents need reliable error handling and human-in-the-loop checkpoints to be useful in a business context.&lt;/p&gt;
&lt;p&gt;ML engineers support this by providing the specialized tools that agents call. An agent might call a custom fraud-detection model built by an ML engineer as part of its reasoning loop. The AI engineer orchestrates the logic, while the ML engineer provides the specialized prediction capabilities.&lt;/p&gt;
&lt;h2&gt;DevOps and deployment: from MLOps to LLMOps&lt;/h2&gt;
&lt;p&gt;The deployment cycle for these roles looks very different. ML engineers deal with MLOps — see &lt;a href=&quot;https://ansezz.com/blog/devops-vs-mlops/&quot;&gt;DevOps vs MLOps&lt;/a&gt; for how that discipline diverges from classic ops. This involves setting up specialized infrastructure for model training, managing GPU clusters, and monitoring for model drift over time. They often use tools like Docker and Kubernetes to ensure their models can scale to handle millions of inference requests.&lt;/p&gt;
&lt;p&gt;AI engineers focus on LLMOps, or &quot;AI DevOps.&quot; Their concerns are more about API reliability, cost management, and caching. They need to ensure that their AI applications stay performant and that they can swap out a model version (e.g., bumping a Claude or GPT model to a newer release) without breaking the entire system. Tools like Coolify or specialized cloud setups on Google Cloud are common here for hosting the middleware that connects the LLM to the world.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/ml-engineer-vs-ai-engineer/agentic-workflows.webp&quot; alt=&quot;Visualization of an agentic workflow where an AI engineer&apos;s orchestration layer calls tools, APIs, and an ML engineer&apos;s custom model&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;How to choose the right path for your project&lt;/h2&gt;
&lt;p&gt;The decision to hire or specialize depends on the &quot;intelligence&quot; you need. If your problem is unique and there is no pre-trained model that can solve it, you need an ML engineer. This is true for niche medical imaging, high-frequency trading, or specialized sensor data analysis.&lt;/p&gt;
&lt;p&gt;If your problem can be solved by a very smart assistant that has access to your company data, you need an AI engineer. Most modern software applications fall into this category. You don&apos;t need to train a new model to build an AI-powered project management tool. You need to build a great system around a foundation model. This often comes down to a &lt;a href=&quot;https://ansezz.com/blog/rag-vs-fine-tuning/&quot;&gt;RAG vs fine-tuning&lt;/a&gt; decision — and the answer is usually RAG.&lt;/p&gt;
&lt;p&gt;In many ways, &lt;a href=&quot;https://ansezz.com/blog/ai-vs-traditional-development/&quot;&gt;AI vs traditional development&lt;/a&gt; is becoming less about the code and more about the data orchestration. The AI engineer is essentially a full-stack developer who has mastered the art of managing non-deterministic outputs.&lt;/p&gt;
&lt;h2&gt;Takeaways&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;ML engineers&lt;/strong&gt; are model builders who focus on training, data science, and applied statistics.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;AI engineers&lt;/strong&gt; are system builders who focus on app development, RAG, agents, and model orchestration.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Technical overlap&lt;/strong&gt; exists in Python and data fundamentals, but the daily toolsets are diverging.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;RAG&lt;/strong&gt; is the primary intersection point where both roles contribute to a single production feature.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Agents&lt;/strong&gt; represent the future of AI engineering, requiring strong backend logic and API integration skills.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Choosing the right role&lt;/strong&gt; prevents wasted resources and ensures your AI features actually reach production.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;When you look at your current roadmap, is your biggest bottleneck the lack of a custom model, or the inability to make an existing model work reliably with your data? If you&apos;re building that AI layer for production, &lt;a href=&quot;https://ansezz.com/services/&quot;&gt;here&apos;s how I help teams ship it&lt;/a&gt;.&lt;/p&gt;
</content:encoded><category>ai</category><category>ai</category><category>machine-learning</category><category>llm</category><category>rag</category><category>agentic-ai</category></item><item><title>Load balancer vs reverse proxy: scale vs security</title><link>https://ansezz.com/blog/load-balancer-vs-reverse-proxy/</link><guid isPermaLink="true">https://ansezz.com/blog/load-balancer-vs-reverse-proxy/</guid><description>Reverse proxies handle SSL, caching, and security; load balancers handle scale and availability. The differences — and how to layer both in a Laravel stack.</description><pubDate>Mon, 08 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Your server is gasping for air. A sudden traffic spike from a new marketing campaign or a successful product launch on Shopify has pushed your single Laravel instance to its absolute limit. CPU usage is pinned at 99 percent. Requests are timing out. The database is struggling to keep up with the sheer volume of open connections. You know you need to scale. You know you need a buffer between the public internet and your application. But when you look at the architectural diagrams, you see two terms used almost interchangeably: load balancer and reverse proxy.&lt;/p&gt;
&lt;p&gt;Choosing the wrong one — or failing to understand how they work together — leads to &quot;Frankenstein&quot; architectures. You might end up with redundant layers that add latency without adding value. Or worse, you might leave your application exposed to security risks that a proper proxy would have mitigated. To build a robust &lt;a href=&quot;https://ansezz.com/blog/category/laravel/&quot;&gt;Laravel&lt;/a&gt; application or a high-performance &lt;a href=&quot;https://ansezz.com/blog/category/shopify/&quot;&gt;Shopify&lt;/a&gt; app, you must understand the technical nuances between these two critical infrastructure components.&lt;/p&gt;
&lt;h2&gt;The gatekeeper: understanding the reverse proxy&lt;/h2&gt;
&lt;p&gt;A reverse proxy is a server that sits in front of one or more web servers and intercepts requests from clients. It acts as a shield and a middleman. When a user tries to access your website, they talk to the reverse proxy first, then it decides how to handle that request before passing it along to your backend Laravel or Node.js application. (If you are fuzzy on which direction it faces, see &lt;a href=&quot;https://ansezz.com/blog/forward-proxy-vs-reverse-proxy/&quot;&gt;forward proxy vs reverse proxy&lt;/a&gt; — they sit on opposite ends of the connection.)&lt;/p&gt;
&lt;p&gt;In the world of &lt;a href=&quot;https://ansezz.com/blog/coolify-self-hosted-saas/&quot;&gt;Coolify and self-hosted SaaS&lt;/a&gt;, Nginx is the most common reverse proxy. Its primary job is to simplify the management of incoming traffic. Instead of exposing your application server directly to the wild internet, the reverse proxy handles the &quot;dirty work&quot; of HTTP communication.&lt;/p&gt;
&lt;h3&gt;Key functions of a reverse proxy&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;SSL termination:&lt;/strong&gt; Encrypting and decrypting HTTPS traffic is CPU-intensive. A reverse proxy handles the SSL certificates and offloads this work from your application server. This allows your Laravel workers to focus on executing business logic rather than processing handshakes.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Caching:&lt;/strong&gt; A reverse proxy can store copies of static assets or even dynamic responses. When a second user requests the same data, the proxy serves it straight from its cache instead of hitting your application. (Nginx, for example, keeps cached responses on disk with the keys held in shared memory.) This drastically reduces the load on your backend.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Request routing:&lt;/strong&gt; You can route traffic based on the URL path. For example, your reverse proxy can send all requests starting with &lt;code&gt;/api&lt;/code&gt; to one service and everything else to a different frontend application.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Security and anonymity:&lt;/strong&gt; By hiding the IP addresses of your backend servers, a reverse proxy makes it much harder for attackers to target your infrastructure directly. It can also act as a basic Web Application Firewall (WAF) to block malicious traffic patterns.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/load-balancer-vs-reverse-proxy/reverse-proxy-functions.webp&quot; alt=&quot;Diagram showing reverse proxy functions like SSL termination and caching&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;The traffic controller: understanding the load balancer&lt;/h2&gt;
&lt;p&gt;While a reverse proxy focuses on &lt;em&gt;how&lt;/em&gt; a request is handled, a load balancer focuses on &lt;em&gt;where&lt;/em&gt; it goes. The primary mission of a load balancer is high availability and &lt;a href=&quot;https://ansezz.com/blog/horizontal-vs-vertical-scaling/&quot;&gt;horizontal scaling&lt;/a&gt;. If you have five identical Laravel servers running in a &lt;a href=&quot;https://ansezz.com/blog/coolify-docker-saas-hosting/&quot;&gt;Docker&lt;/a&gt; cluster, the load balancer ensures that no single server gets overwhelmed while others sit idle.&lt;/p&gt;
&lt;p&gt;Load balancers operate at different levels of the networking stack. Layer 4 load balancers work at the transport level (TCP/UDP), making decisions based on IP addresses and ports without looking at the actual content of the request. Layer 7 load balancers work at the application level (HTTP/HTTPS), allowing for much more granular routing based on headers, cookies, or URL parameters.&lt;/p&gt;
&lt;h3&gt;How load balancers distribute work&lt;/h3&gt;
&lt;p&gt;Load balancers use specific algorithms to decide which server gets the next request. Common strategies include:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Round robin:&lt;/strong&gt; Requests are sent to servers in a sequential loop. It is simple but assumes all your backend servers have the same capacity.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Least connections:&lt;/strong&gt; The load balancer tracks how many active requests each server is handling. It sends new traffic to the server that is currently the least busy.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;IP hash:&lt;/strong&gt; The client&apos;s IP address is used to determine which server receives the request. This ensures that a specific user stays connected to the same server, which is vital for applications that store session data locally rather than in Redis.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/load-balancer-vs-reverse-proxy/load-balancer-distribution.webp&quot; alt=&quot;Illustration of a load balancer distributing traffic across multiple healthy server instances&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Technical nuances: comparing the two&lt;/h2&gt;
&lt;p&gt;The confusion often arises because modern tools like Nginx, HAProxy, and Traefik can perform both roles. However, the conceptual difference remains important for system design.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Reverse Proxy&lt;/th&gt;
&lt;th&gt;Load Balancer&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Primary Goal&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Security, routing, and efficiency&lt;/td&gt;
&lt;td&gt;Availability and throughput&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Backend Pattern&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Usually one logical service&lt;/td&gt;
&lt;td&gt;A pool of identical nodes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Caching&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Excellent support for static + dynamic&lt;/td&gt;
&lt;td&gt;Usually minimal or none&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Health Checks&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Basic (is the backend up?)&lt;/td&gt;
&lt;td&gt;Advanced (latency, error rates, load)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;OSI Layer&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Mostly Layer 7 (Application)&lt;/td&gt;
&lt;td&gt;Layer 4 (Transport) or Layer 7&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Both are really reverse-proxy roles wearing different hats: a load balancer emphasizes &lt;em&gt;distribution&lt;/em&gt; (spreading traffic across a pool), while a reverse proxy emphasizes &lt;em&gt;transformation&lt;/em&gt; (SSL, caching, routing, security). The same tool — Nginx, HAProxy, Traefik — can do both. In a sophisticated &lt;a href=&quot;https://ansezz.com/blog/api-gateway-ai-stack/&quot;&gt;API gateway&lt;/a&gt;, these roles are merged into a single entry point that manages both the security and the distribution of traffic across your microservices.&lt;/p&gt;
&lt;h2&gt;Real-world implementation in Laravel and Shopify&lt;/h2&gt;
&lt;p&gt;When building custom web solutions, you rarely choose just one. You layer them. For a production-grade Laravel application, your architecture typically looks like a &quot;sandwich&quot; of these components.&lt;/p&gt;
&lt;h3&gt;The Laravel DevOps stack&lt;/h3&gt;
&lt;p&gt;In a typical cloud infrastructure setup on GCP or AWS, the request flow looks like this:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;The cloud load balancer:&lt;/strong&gt; This is your public entry point. It receives traffic and spreads it across multiple virtual machine instances or Kubernetes nodes.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The local reverse proxy (Nginx):&lt;/strong&gt; Each node runs an Nginx instance. This proxy terminates the SSL, serves static CSS and JS files from the disk, and forwards the PHP requests to PHP-FPM.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The application (Laravel):&lt;/strong&gt; Laravel receives the cleaned-up request from Nginx.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This multi-layer approach provides redundancy. If one Nginx instance fails, the cloud load balancer detects the failure through a health check and stops sending traffic to that specific node. This keeps your service available to users.&lt;/p&gt;
&lt;h3&gt;The Shopify app context&lt;/h3&gt;
&lt;p&gt;If you are developing a Shopify app, your infrastructure requirements are unique. During major events like Black Friday, webhook traffic can spike hard and bursty, and Shopify gives you only a 5-second window to acknowledge each delivery before it counts as failed and gets retried (up to 8 times over roughly 4 hours). You cannot ride that out on a single server.&lt;/p&gt;
&lt;p&gt;You should use a load balancer to ingest these webhooks and distribute them across a fleet of worker nodes, acknowledging fast and processing the payload &lt;a href=&quot;https://ansezz.com/blog/synchronous-vs-asynchronous-communication/&quot;&gt;asynchronously&lt;/a&gt;. A reverse proxy at the edge can help you implement rate limiting. This prevents a single store from monopolizing your app resources and ensures that your &lt;a href=&quot;https://ansezz.com/blog/agentic-commerce-shopify/&quot;&gt;agentic commerce systems&lt;/a&gt; remain responsive for all merchants.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/load-balancer-vs-reverse-proxy/laravel-nginx-stack.webp&quot; alt=&quot;Architectural diagram for a Laravel application stack using Nginx and Docker&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Why &quot;both&quot; is usually the answer&lt;/h2&gt;
&lt;p&gt;Modern web development has moved away from the &quot;one server&quot; model. Even for small startups, the cost of a managed load balancer is negligible compared to the cost of downtime. Using a reverse proxy like Nginx or Traefik is standard practice because it simplifies your application code. You do not want to write SSL handling logic inside your Laravel controllers. You want the infrastructure to handle that for you.&lt;/p&gt;
&lt;p&gt;When you combine a load balancer and a reverse proxy, you gain the ability to perform blue-green deployments. You can spin up a new version of your app, test it, and then tell the load balancer to slowly bleed traffic from the old version to the new one. If something breaks, you flip the switch back. This level of control is impossible without these two components working in tandem.&lt;/p&gt;
&lt;h2&gt;Takeaways&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Use a reverse proxy&lt;/strong&gt; if you have a single server but need to handle SSL, caching, and basic security.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Add a load balancer&lt;/strong&gt; as soon as you need to scale horizontally across multiple servers to handle more traffic or ensure high availability.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Leverage Nginx or HAProxy&lt;/strong&gt; to fulfill both roles in a single software layer for smaller to medium-sized projects.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Offload SSL termination&lt;/strong&gt; to the proxy layer to keep your Laravel or Node.js application responsive.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Implement health checks&lt;/strong&gt; in your load balancer to automatically remove unhealthy server instances from the rotation.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Choose Layer 7 balancing&lt;/strong&gt; if you need to route traffic based on specific HTTP headers or URL paths.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;What is your current bottleneck — scaling the number of concurrent connections, or managing the complexity of your request routing? If you&apos;re hardening an infrastructure layer for production, &lt;a href=&quot;https://ansezz.com/services/&quot;&gt;here&apos;s how I help teams ship it&lt;/a&gt;.&lt;/p&gt;
</content:encoded><category>architecture</category><category>devops</category><category>laravel</category><category>shopify</category><category>infrastructure</category><category>networking</category><category>scaling</category></item><item><title>Logging vs monitoring: a guide for scaling</title><link>https://ansezz.com/blog/logging-vs-monitoring/</link><guid isPermaLink="true">https://ansezz.com/blog/logging-vs-monitoring/</guid><description>Monitoring tells you a system is unhealthy; logging tells you why. How to combine metrics, structured logs, and correlation IDs for fast debugging.</description><pubDate>Mon, 08 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Your production environment is failing and your customers are reporting 500 errors. You check your dashboard and see a spike in latency but the graph does not tell you why the database connection is timing out. You dive into your text files to find the specific stack trace but the logs are unstructured and impossible to search during a crisis. This gap between knowing something is wrong and knowing why it is wrong is the fundamental divide between logging and monitoring. Engineering teams routinely conflate these two pillars of observability, and the result is slow incident response and fragile systems.&lt;/p&gt;
&lt;p&gt;Understanding the distinction is not just about choosing tools. It is about building a robust strategy for your &lt;a href=&quot;https://ansezz.com/blog/category/devops/&quot;&gt;DevOps solutions&lt;/a&gt; that ensures high availability and fast debugging. While both involve data collection, they serve different masters. One is the pulse of your system. The other is the forensic record.&lt;/p&gt;
&lt;h2&gt;Logging vs monitoring: what vs why&lt;/h2&gt;
&lt;p&gt;Monitoring is the process of tracking metrics over time to understand the state of a system. It answers the question &quot;Is the system healthy?&quot; by looking at numbers. You monitor CPU usage, memory consumption, and request latency. If a metric crosses a certain threshold, your monitoring system triggers an alert. It provides a high-level view of performance and availability.&lt;/p&gt;
&lt;p&gt;Logging is the act of recording discrete events that occur within an application or infrastructure. It answers the question &quot;What exactly happened?&quot; by providing context. A log entry might contain a user ID, a timestamp, and a specific error message from a failed &lt;a href=&quot;https://ansezz.com/blog/category/laravel/&quot;&gt;Laravel&lt;/a&gt; job. Logs are granular. They provide the narrative of a single request journey through your stack.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Monitoring&lt;/th&gt;
&lt;th&gt;Logging&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Data type&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Metrics (numbers/counters)&lt;/td&gt;
&lt;td&gt;Events (text/structured data)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Purpose&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Detection and health&lt;/td&gt;
&lt;td&gt;Diagnosis and forensics&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Frequency&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Aggregated over time&lt;/td&gt;
&lt;td&gt;Recorded per occurrence&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Alerting&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Threshold-based (e.g., CPU &amp;gt; 80%)&lt;/td&gt;
&lt;td&gt;Event-based (e.g., Fatal Error)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Storage&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Time-series databases&lt;/td&gt;
&lt;td&gt;Log aggregators/search engines&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;Monitoring: the pulse of your infrastructure&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/logging-vs-monitoring/monitoring-dashboard.webp&quot; alt=&quot;Pop-art comic-style monitoring dashboard showing latency, traffic, error rate, and saturation gauges&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Monitoring focuses on quantitative data. It is the first line of defense in any production environment. By observing trends, you can predict failures before they happen. For example, a slow increase in memory usage over 48 hours might indicate a memory leak in a long-running Docker container.&lt;/p&gt;
&lt;p&gt;In a modern stack using tools like &lt;a href=&quot;https://ansezz.com/blog/coolify-docker-saas-hosting/&quot;&gt;Coolify and Docker&lt;/a&gt;, monitoring ensures your containers are performing as expected. You should focus on the &quot;Four Golden Signals&quot; of monitoring: latency, traffic, errors, and saturation. Latency measures the time it takes to service a request. Traffic measures the demand placed on the system. Errors measure the rate of requests that fail. Saturation measures how &quot;full&quot; your service is.&lt;/p&gt;
&lt;p&gt;Effective monitoring requires meaningful thresholds. An alert that fires every time CPU usage hits 70% is noise if your application is designed to be CPU-intensive. You must define Service Level Objectives (SLOs) that align with user experience. If your users do not notice a 100ms delay, do not wake up an engineer for it.&lt;/p&gt;
&lt;h2&gt;Logging: the black box recorder&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/logging-vs-monitoring/structured-logs.webp&quot; alt=&quot;Pop-art illustration of a stream of structured JSON log events flowing into a central log aggregator&quot; /&gt;&lt;/p&gt;
&lt;p&gt;If monitoring tells you the plane is losing altitude, logging tells you why the engine stalled. Logs provide the forensic evidence needed to debug complex issues. In a distributed system, a single user action might touch multiple services. Without a centralized logging strategy, finding the root cause of a failure is like finding a needle in a haystack.&lt;/p&gt;
&lt;p&gt;The biggest mistake developers make is using unstructured logs. Standard text logs like &lt;code&gt;[2026-06-21] Error: something went wrong&lt;/code&gt; are difficult for machines to parse. Modern engineering teams use structured logging. This involves formatting logs as JSON objects. This allows you to filter and query logs based on specific fields like &lt;code&gt;user_id&lt;/code&gt;, &lt;code&gt;request_id&lt;/code&gt;, or &lt;code&gt;environment&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
  &quot;timestamp&quot;: &quot;2026-06-21T14:30:05Z&quot;,
  &quot;level&quot;: &quot;error&quot;,
  &quot;message&quot;: &quot;Payment gateway timeout&quot;,
  &quot;context&quot;: {
    &quot;user_id&quot;: 4502,
    &quot;order_id&quot;: &quot;ORD-9921&quot;,
    &quot;gateway&quot;: &quot;stripe&quot;,
    &quot;attempt&quot;: 3
  },
  &quot;request_id&quot;: &quot;req-a1b2c3d4&quot;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;By including a &lt;code&gt;request_id&lt;/code&gt; in every log entry, you can trace a single request across your entire infrastructure. This is essential for modern &lt;a href=&quot;https://ansezz.com/blog/category/shopify/&quot;&gt;Shopify development&lt;/a&gt; where webhooks and API calls often chain together.&lt;/p&gt;
&lt;h2&gt;Observability in the Laravel ecosystem&lt;/h2&gt;
&lt;p&gt;Laravel provides a powerful logging system built on the Monolog library. Out of the box, it supports various &quot;channels&quot; such as single files, daily rotated files, syslog, and Slack, plus &lt;code&gt;stack&lt;/code&gt; channels that fan a single message out to several of them at once. For production environments, configure Laravel to ship logs to a centralized service. Sending logs to a service like Logstash, Sentry, or Datadog ensures you do not lose data if a server instance is terminated.&lt;/p&gt;
&lt;p&gt;Monitoring a Laravel application involves more than just checking if the web server is up. You must monitor the health of your background queues. If your Redis queue is backing up, your customers might not receive order confirmation emails or account activation links. Tools like Laravel Pulse or Horizon provide real-time monitoring specifically for these application-level metrics.&lt;/p&gt;
&lt;p&gt;A common pattern is to log exceptions to a dedicated tracker while monitoring the error rate through a dashboard. This creates a feedback loop where a spike in the &quot;Error Rate&quot; metric in your monitoring tool prompts you to check the &quot;Exception Logs&quot; for the actual stack trace.&lt;/p&gt;
&lt;h2&gt;Scaling Shopify apps with metrics and logs&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/logging-vs-monitoring/shopify-integration.webp&quot; alt=&quot;Pop-art comic-style illustration of a Shopify app integration tracking webhook success rate and API rate-limit usage&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Building high-scale Shopify apps requires a specialized approach to logging and monitoring. Shopify apps live at the mercy of external API &lt;a href=&quot;https://ansezz.com/blog/rate-limiting-vs-throttling/&quot;&gt;rate limits&lt;/a&gt; and webhook delivery speeds. If your app handles thousands of webhooks per minute, you cannot afford to log every single successful event to a text file. You will quickly run out of disk space.&lt;/p&gt;
&lt;p&gt;Instead, use monitoring to track the &quot;Webhook Success Rate&quot; and your API rate-limit consumption. Shopify&apos;s GraphQL Admin API meters calls by calculated query cost using a leaky bucket (on a Standard plan, a 1,000-point bucket that refills at 100 points per second), while the REST Admin API uses a request-per-second bucket. Watch how close you run to those ceilings, then use logging to investigate the specific failed payloads when the success rate drops. This is especially important for &lt;a href=&quot;https://ansezz.com/blog/agentic-commerce-shopify/&quot;&gt;agentic commerce&lt;/a&gt; applications where AI agents perform autonomous tasks on behalf of a merchant. If an agent fails to update an inventory count, you need the specific log to understand the logic failure.&lt;/p&gt;
&lt;p&gt;For Shopify Plus merchants, performance is critical. Monitoring the &quot;Time to First Byte&quot; (TTFB) of your app&apos;s embedded components ensures that you are not slowing down the merchant&apos;s admin experience.&lt;/p&gt;
&lt;h2&gt;The DevOps synergy: linking logs and metrics&lt;/h2&gt;
&lt;p&gt;The most mature engineering organizations do not treat logging and monitoring as separate silos. They link them together. When an alert fires in your monitoring tool, it should provide a direct link to the logs associated with that specific timeframe and service.&lt;/p&gt;
&lt;p&gt;This is often achieved through &quot;Correlation IDs.&quot; When a request enters your system, you assign it a unique ID. This ID is attached to every metric collected and every log entry generated during that request. This allows you to jump from a high-level metric spike directly to the low-level events that caused it.&lt;/p&gt;
&lt;p&gt;Furthermore, you should monitor your logs. By counting the frequency of certain log patterns, you can create new metrics. If you see the log message &quot;Database connection lost&quot; appearing more than five times in a minute, that log event should be converted into a metric that triggers a high-priority alert.&lt;/p&gt;
&lt;h2&gt;Best practices for technical teams&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Automate everything&lt;/strong&gt;: Your monitoring agents and logging drivers should be part of your base server image or Dockerfile.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Log levels matter&lt;/strong&gt;: Use &lt;code&gt;debug&lt;/code&gt; for local development, &lt;code&gt;info&lt;/code&gt; for general production events, and &lt;code&gt;error&lt;/code&gt; or &lt;code&gt;critical&lt;/code&gt; for things that require human intervention.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Protect PII&lt;/strong&gt;: Never log personally identifiable information like passwords, credit card numbers, or auth tokens. Use log masking to strip sensitive data.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Set retention policies&lt;/strong&gt;: Metrics are small and can be kept for months. Logs are large and expensive. Set a retention policy that balances cost with your need for historical data.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Monitor the monitors&lt;/strong&gt;: Ensure your monitoring system itself is healthy. A silent monitoring system is a dangerous liability.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Takeaways&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Monitoring detects that a problem exists. Logging explains why the problem occurred.&lt;/li&gt;
&lt;li&gt;Metrics are numerical data points. Logs are detailed event records.&lt;/li&gt;
&lt;li&gt;Use structured JSON logging to make your data searchable and machine-readable.&lt;/li&gt;
&lt;li&gt;In Laravel, prioritize queue monitoring to ensure background tasks are completing.&lt;/li&gt;
&lt;li&gt;In Shopify apps, track API rate limits as a primary health metric.&lt;/li&gt;
&lt;li&gt;Link logs and metrics using Correlation IDs to reduce the Mean Time to Resolution (MTTR).&lt;/li&gt;
&lt;li&gt;Monitoring should be based on user-centric SLOs to avoid alert fatigue.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;How do you differentiate between a noisy alert and a critical system failure in your current production stack? If you&apos;re building that observability layer for production, &lt;a href=&quot;https://ansezz.com/services/&quot;&gt;here&apos;s how I help teams ship it&lt;/a&gt;.&lt;/p&gt;
</content:encoded><category>devops</category><category>observability</category><category>laravel</category><category>shopify</category><category>infrastructure</category></item><item><title>Load balancer vs API gateway</title><link>https://ansezz.com/blog/load-balancer-vs-api-gateway/</link><guid isPermaLink="true">https://ansezz.com/blog/load-balancer-vs-api-gateway/</guid><description>Load balancers distribute traffic; API gateways enforce policy. The real differences, when to use each, and how to layer both in a production stack.</description><pubDate>Sun, 07 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Two boxes sit at the front of almost every production system, and engineers constantly confuse them. One spreads traffic across servers so nothing falls over. The other inspects each request, checks who is calling, and decides where it should go. They look similar on an architecture diagram, but they solve fundamentally different problems.&lt;/p&gt;
&lt;p&gt;Pick the wrong one and you either bolt fragile business logic onto a component that was built to be dumb and fast, or you pay for an expensive policy engine to do a job a simple round-robin could handle. The load balancer vs API gateway decision shapes how you scale, secure, and observe everything behind it.&lt;/p&gt;
&lt;p&gt;This guide breaks down what each component actually does, where they differ, and why mature systems almost always run both.&lt;/p&gt;
&lt;h2&gt;What a load balancer actually does&lt;/h2&gt;
&lt;p&gt;A load balancer has one job: take incoming traffic and spread it across a pool of identical backends so no single server gets overwhelmed. It is the component that lets you run three copies of your app instead of one and survive a traffic spike.&lt;/p&gt;
&lt;p&gt;Load balancers operate at one of two layers. An &lt;strong&gt;L4 (transport layer)&lt;/strong&gt; balancer routes by IP and port. It does not look inside the request — it just forwards TCP/UDP packets, which makes it extremely fast. An &lt;strong&gt;L7 (application layer)&lt;/strong&gt; balancer understands HTTP, so it can route based on the path or host header and terminate SSL/TLS. Algorithms like round-robin, least-connections, and IP-hash decide which backend gets the next request.&lt;/p&gt;
&lt;p&gt;The key trait is that a load balancer is largely &lt;strong&gt;stateless and content-agnostic&lt;/strong&gt;. It cares about availability and distribution, not about who you are or whether you are allowed to make this call. That ignorance is a feature: it keeps the component simple, fast, and reliable. If you have ever wondered how this differs from a reverse proxy, the two overlap heavily — see &lt;a href=&quot;https://ansezz.com/blog/load-balancer-vs-reverse-proxy/&quot;&gt;load balancer vs reverse proxy&lt;/a&gt; for where they diverge.&lt;/p&gt;
&lt;h2&gt;What an API gateway actually does&lt;/h2&gt;
&lt;p&gt;An API gateway is the opposite kind of component. It is opinionated, aware, and full of business rules. It sits in front of your services and acts as a single, managed front door for every API call.&lt;/p&gt;
&lt;p&gt;A gateway handles the &lt;strong&gt;cross-cutting concerns&lt;/strong&gt; you do not want duplicated in every microservice: authentication and JWT validation, &lt;a href=&quot;https://ansezz.com/blog/rate-limiting-ai-wallet/&quot;&gt;rate limiting and quota enforcement&lt;/a&gt;, request/response transformation, protocol translation (exposing internal &lt;a href=&quot;https://ansezz.com/blog/rest-vs-grpc/&quot;&gt;gRPC services&lt;/a&gt; as REST/JSON, for example), caching, and per-client logging. Instead of each service re-implementing auth, the gateway enforces it once at the edge.&lt;/p&gt;
&lt;p&gt;Crucially, a gateway routes by &lt;strong&gt;intent&lt;/strong&gt;, not just availability. It maps &lt;code&gt;/orders&lt;/code&gt; to the orders service and &lt;code&gt;/users&lt;/code&gt; to the users service, applies the right policy to each, and stitches a fleet of microservices into one coherent API. That makes it a natural fit once you &lt;a href=&quot;https://ansezz.com/blog/monolith-vs-microservices/&quot;&gt;move from a monolith to microservices&lt;/a&gt; and need one stable front door over many services. It is also the foundation of a clean &lt;a href=&quot;https://ansezz.com/blog/api-gateway-ai-stack/&quot;&gt;API gateway for an AI stack&lt;/a&gt;, where the gateway governs which model endpoints and tools a request can reach.&lt;/p&gt;
&lt;h2&gt;Key differences at a glance&lt;/h2&gt;
&lt;p&gt;The two components overlap on the surface — both sit at the front, both can route HTTP — but their purpose, intelligence, and state could not be more different.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Aspect&lt;/th&gt;
&lt;th&gt;Load Balancer&lt;/th&gt;
&lt;th&gt;API Gateway&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Primary job&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Distribute traffic across servers&lt;/td&gt;
&lt;td&gt;Enforce policy + route by intent&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Layer&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;L4 (TCP/UDP) or L7 (HTTP)&lt;/td&gt;
&lt;td&gt;L7 (HTTP/API aware)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Awareness&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Content-agnostic&lt;/td&gt;
&lt;td&gt;Inspects auth, headers, payload&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Core features&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Health checks, SSL, algorithms&lt;/td&gt;
&lt;td&gt;Auth, rate limiting, caching, transforms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;State&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Mostly stateless&lt;/td&gt;
&lt;td&gt;Tracks clients, quotas, sessions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Optimizes for&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Availability + throughput&lt;/td&gt;
&lt;td&gt;Security + control&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The rule of thumb: reach for a &lt;strong&gt;load balancer&lt;/strong&gt; when you need raw distribution and uptime, and an &lt;strong&gt;API gateway&lt;/strong&gt; when you need to apply rules — who can call this, how often, and in what shape.&lt;/p&gt;
&lt;h2&gt;The ultimate architecture: using them together&lt;/h2&gt;
&lt;p&gt;In a professional production environment, it is common to see both components working in tandem. This is not redundant. It is a layered defense and distribution strategy.&lt;/p&gt;
&lt;p&gt;A typical request flow looks like this:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;The L4/L7 load balancer:&lt;/strong&gt; Sits at the absolute edge of your network. It handles global traffic distribution and terminates SSL/TLS. It passes clean HTTP traffic to the API gateway.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The API gateway:&lt;/strong&gt; Receives the traffic from the load balancer. It checks the user&apos;s JWT token, validates that they haven&apos;t exceeded their rate limit, and routes the request to the correct microservice.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The backend services:&lt;/strong&gt; These are often fronted by their own internal load balancers. For example, your Laravel API might have three pods. A simple internal load balancer (often built into Docker or Kubernetes) distributes the traffic from the gateway across those three pods.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This separation of concerns lets you scale your infrastructure independently of your business rules. You can rewrite authentication logic in the gateway without touching load balancer configuration, and you can &lt;a href=&quot;https://ansezz.com/blog/horizontal-vs-vertical-scaling/&quot;&gt;add or remove backend replicas&lt;/a&gt; without the gateway noticing.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/load-balancer-vs-api-gateway/request-flow.webp&quot; alt=&quot;Layered request flow diagram: users hit an edge load balancer that terminates TLS, an API gateway checks JWT and rate limits, then backend services sit behind internal load balancers&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Implementing traffic control with Laravel and Docker&lt;/h2&gt;
&lt;p&gt;When developing with Laravel, you often don&apos;t need a dedicated hardware appliance. You can implement these patterns using open-source software like Nginx, Traefik, or Kong. Using &lt;a href=&quot;https://ansezz.com/blog/coolify-docker-saas-hosting/&quot;&gt;Docker containerization&lt;/a&gt; makes this setup highly portable.&lt;/p&gt;
&lt;p&gt;For instance, if you are using Traefik as an API gateway in a Docker Compose file, you can define your routing and middleware logic directly in the labels. This keeps your configuration close to your code.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;services:
  laravel-api:
    image: ansezz/laravel-app:latest
    labels:
      - &quot;traefik.http.routers.api.rule=Host(`api.example.com`)&quot;
      - &quot;traefik.http.middlewares.api-auth.forwardauth.address=http://auth-service&quot;
      - &quot;traefik.http.middlewares.rate-limit.ratelimit.average=100&quot;
      - &quot;traefik.http.routers.api.middlewares=api-auth,rate-limit&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this example, Traefik is performing the role of an API gateway by checking with an external auth service and enforcing rate limits before the request ever reaches the Laravel application.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/load-balancer-vs-api-gateway/traefik-traffic-control.webp&quot; alt=&quot;Technical illustration of Traefik fronting a Laravel app in Docker, using container labels for forward-auth and rate-limit middleware before requests reach the application&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Shopify and the gateway pattern&lt;/h2&gt;
&lt;p&gt;For Shopify developers, the API gateway pattern is vital when building &quot;headless&quot; storefronts. Since you are communicating with both the Shopify Storefront API and your own custom backend, a gateway can unify these disparate sources. It can cache frequent Shopify queries to reduce API consumption and improve performance for the end user.&lt;/p&gt;
&lt;p&gt;A gateway also lets you implement logic Shopify doesn&apos;t support natively: transforming payloads or merging multiple sources into a single response, which cuts the number of round trips the browser has to make. That pattern is foundational to building &lt;a href=&quot;https://ansezz.com/blog/agentic-workflows-vibe-coding/&quot;&gt;agentic workflows&lt;/a&gt; in modern commerce.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/load-balancer-vs-api-gateway/headless-gateway.webp&quot; alt=&quot;Headless commerce diagram: a browser storefront calls one API gateway that merges Shopify Storefront API data with a custom backend and caches frequent queries into a single response&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Takeaways&lt;/h2&gt;
&lt;p&gt;Understanding the specific strengths of load balancers and API gateways will save you from infrastructure debt.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Choose a load balancer&lt;/strong&gt; for simple traffic distribution, high throughput, and maximum availability at the network layer.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Choose an API gateway&lt;/strong&gt; for complex routing, security enforcement, protocol translation, and providing a unified entry point to microservices.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Layer your stack&lt;/strong&gt; by putting an L4/L7 load balancer at the edge and an API gateway behind it to handle business logic.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Leverage automation&lt;/strong&gt; with tools like Docker and Coolify to manage these components without the overhead of manual server configuration.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Focus on observability&lt;/strong&gt; by using the gateway&apos;s ability to log per-client metrics, which helps in debugging and billing.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;How do you handle cross-cutting concerns like rate limiting and authentication across your current microservice fleet? If you&apos;re untangling an infrastructure layer for production, &lt;a href=&quot;https://ansezz.com/services/&quot;&gt;here&apos;s how I help teams ship it&lt;/a&gt;.&lt;/p&gt;
</content:encoded><category>architecture</category><category>api-design</category><category>networking</category><category>architecture</category><category>microservices</category><category>devops</category><category>laravel</category></item><item><title>Horizontal vs vertical scaling</title><link>https://ansezz.com/blog/horizontal-vs-vertical-scaling/</link><guid isPermaLink="true">https://ansezz.com/blog/horizontal-vs-vertical-scaling/</guid><description>Learn the key differences between horizontal and vertical scaling. Discover the right architectural strategy for scaling your web application effectively.</description><pubDate>Sat, 06 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Your application is slowing down. Users are reporting latency, your database is hitting 90% CPU usage, and your background workers are lagging. You know you need more power. But should you buy a bigger server or start adding more of them? Choosing the wrong scaling path can lead to wasted budget, unnecessary architectural complexity, or a hard ceiling on your growth.&lt;/p&gt;
&lt;p&gt;Engineering for scale is not just about adding hardware. It is about understanding the trade-offs between vertical and horizontal strategies. If you wait too long to scale out, you risk a single point of failure. If you scale out too early, you drown in the complexity of distributed systems. This guide breaks down the technical substance of both approaches.&lt;/p&gt;
&lt;h2&gt;Vertical scaling: the &quot;bigger box&quot; strategy&lt;/h2&gt;
&lt;p&gt;Vertical scaling, or &quot;scaling up,&quot; is the process of adding more power to your existing server. This typically means increasing the CPU, RAM, or storage capacity of a single instance. In a cloud environment like Google Cloud or AWS, this is as simple as changing your machine type from a small instance to a high-memory or high-compute instance.&lt;/p&gt;
&lt;p&gt;This is the path of least resistance. Since the application still lives on one machine, you do not need to change your code. There is no need for load balancers, service discovery, or complex networking. Your &lt;a href=&quot;https://ansezz.com/blog/category/laravel/&quot;&gt;Laravel&lt;/a&gt; application or monolith works exactly as it did before, just faster.&lt;/p&gt;
&lt;p&gt;However, vertical scaling has a hard ceiling. Every hardware generation has a limit. Eventually, you will find that the &quot;biggest box&quot; available is either too expensive or simply not enough. More importantly, vertical scaling does not solve the problem of redundancy. If that one big server goes down, your entire system goes down with it.&lt;/p&gt;
&lt;h2&gt;Horizontal scaling: building the &quot;fleet&quot;&lt;/h2&gt;
&lt;p&gt;Horizontal scaling, or &quot;scaling out,&quot; involves adding more machines to your infrastructure. Instead of one giant server, you have a fleet of smaller servers working in parallel. A load balancer sits in front of this fleet, distributing incoming traffic across the available nodes.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/horizontal-vs-vertical-scaling/architecture-diagram.webp&quot; alt=&quot;Modern web architecture diagram with a load balancer and multiple server nodes in pop-art style&quot; /&gt;&lt;/p&gt;
&lt;p&gt;This is the gold standard for high availability and modern &lt;a href=&quot;https://ansezz.com/blog/coolify-docker-saas-hosting/&quot;&gt;cloud infrastructure&lt;/a&gt;. When one server fails, the load balancer simply routes traffic to the healthy ones. It also offers extreme elasticity. With &lt;a href=&quot;https://ansezz.com/blog/smart-auto-scaling-ai/&quot;&gt;auto-scaling&lt;/a&gt;, you can add nodes during a traffic spike and remove them when things quiet down, ensuring you only pay for what you use.&lt;/p&gt;
&lt;p&gt;The catch is that horizontal scaling requires architectural changes. Your application must be &lt;a href=&quot;https://ansezz.com/blog/stateless-vs-stateful-apps/&quot;&gt;stateless&lt;/a&gt;. If a user uploads a file to Server A, Server B must be able to access it. If you store session data in the local memory of Server A, the user will be logged out if their next request hits Server B. You must externalize state to shared databases or object storage.&lt;/p&gt;
&lt;h2&gt;Key differences at a glance&lt;/h2&gt;
&lt;p&gt;Choosing between these two depends on your current stage and technical requirements. Here is how they compare across critical engineering metrics.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Vertical Scaling (Scale Up)&lt;/th&gt;
&lt;th&gt;Horizontal Scaling (Scale Out)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Primary Action&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Add CPU/RAM to one node&lt;/td&gt;
&lt;td&gt;Add more nodes to the pool&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Complexity&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Low (No code changes)&lt;/td&gt;
&lt;td&gt;High (Distributed system)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Fault Tolerance&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Low (Single point of failure)&lt;/td&gt;
&lt;td&gt;High (Redundancy by design)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Scalability Limit&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Hard limit (Hardware ceiling)&lt;/td&gt;
&lt;td&gt;Virtually unlimited&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Maintenance&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Requires downtime for resizing&lt;/td&gt;
&lt;td&gt;Zero-downtime rolling updates&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Cost&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Expensive at the high end&lt;/td&gt;
&lt;td&gt;More efficient through auto-scaling&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;The challenge of distributed state&lt;/h2&gt;
&lt;p&gt;When you move to a horizontal model, the database becomes the most significant bottleneck. Scaling an application layer is relatively easy because app servers are usually stateless. Scaling a database is harder because data must remain consistent across multiple nodes.&lt;/p&gt;
&lt;p&gt;For many &lt;a href=&quot;https://ansezz.com/blog/category/shopify/&quot;&gt;Shopify&lt;/a&gt; apps or custom web platforms, the first step into horizontal scaling is adding &lt;a href=&quot;https://ansezz.com/blog/replication-vs-backup-laravel/&quot;&gt;read replicas&lt;/a&gt;. You keep one &quot;primary&quot; database for writes and multiple &quot;replica&quot; databases for reads. This offloads the heavy lifting from the main node.&lt;/p&gt;
&lt;p&gt;If your data grows beyond the capacity of a single primary node, you might look into sharding. This involves splitting your data across different database clusters. It is a powerful technique but introduces massive complexity in how you query and join data.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/horizontal-vs-vertical-scaling/metrics-dashboard.webp&quot; alt=&quot;Server monitoring dashboard showing CPU, RAM, and database load metrics in a bento grid layout&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Scaling in practice: Laravel and DevOps&lt;/h2&gt;
&lt;p&gt;In the Laravel ecosystem, scaling often starts with vertical upgrades. I have seen many projects stay on a single powerful VPS for years. As traffic grows, the transition to horizontal scaling usually follows a specific pattern.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Externalize Sessions:&lt;/strong&gt; Move sessions from the &lt;code&gt;file&lt;/code&gt; driver to &lt;code&gt;redis&lt;/code&gt; or &lt;code&gt;database&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Centralize Files:&lt;/strong&gt; Move local storage to an S3-compatible object store.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Add a Load Balancer:&lt;/strong&gt; Use Nginx or a cloud-provider load balancer to distribute traffic.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Decouple Workers:&lt;/strong&gt; Move queue workers to their own dedicated instances to avoid competing for resources with the web server.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Tools like &lt;a href=&quot;https://ansezz.com/blog/coolify-self-hosted-saas/&quot;&gt;Coolify&lt;/a&gt; or Docker Swarm make managing multiple containers easier. They allow you to define your infrastructure as code, making it simple to spin up new nodes as needed.&lt;/p&gt;
&lt;h2&gt;How to choose your strategy&lt;/h2&gt;
&lt;p&gt;There is no one-size-fits-all answer. Your choice should be a pragmatic balance of cost, time, and reliability.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Choose Vertical Scaling if:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;You are in the early stages of a startup or MVP.&lt;/li&gt;
&lt;li&gt;Your traffic is predictable and stable.&lt;/li&gt;
&lt;li&gt;You have a small team with limited DevOps resources.&lt;/li&gt;
&lt;li&gt;Your application architecture is tightly coupled or legacy.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Choose Horizontal Scaling if:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;You need 99.9% or higher availability.&lt;/li&gt;
&lt;li&gt;Your traffic is bursty or growing rapidly.&lt;/li&gt;
&lt;li&gt;You are building a cloud-native application from scratch.&lt;/li&gt;
&lt;li&gt;You want to avoid the &quot;all-or-nothing&quot; risk of a single server.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In reality, most modern architectures use a hybrid approach. You might scale your database vertically as much as possible while scaling your application layer horizontally. This gives you the reliability of a fleet with the simplicity of a single data source.&lt;/p&gt;
&lt;h2&gt;Takeaways&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Vertical scaling is about making a single server more powerful through CPU and RAM upgrades.&lt;/li&gt;
&lt;li&gt;Horizontal scaling adds more servers to a pool and uses a load balancer to manage traffic.&lt;/li&gt;
&lt;li&gt;Vertical scaling is simpler and requires no code changes but has a performance ceiling and no redundancy.&lt;/li&gt;
&lt;li&gt;Horizontal scaling offers high availability and elasticity but requires a stateless application architecture.&lt;/li&gt;
&lt;li&gt;For most web applications, the transition to horizontal scaling starts by externalizing sessions, files, and background jobs.&lt;/li&gt;
&lt;li&gt;Databases are the hardest part to scale horizontally, often requiring replicas or sharding.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;At what point does the cost of managing a distributed horizontal system outweigh the cost of simply buying a larger server for your specific workload? If you&apos;re planning a scaling path for your stack, &lt;a href=&quot;https://ansezz.com/services/&quot;&gt;here&apos;s how I help teams ship it&lt;/a&gt;.&lt;/p&gt;
</content:encoded><category>architecture</category><category>scaling</category><category>devops</category><category>infrastructure</category><category>laravel</category><category>architecture</category></item><item><title>LLM vs AI agent: from prompts to action</title><link>https://ansezz.com/blog/llm-vs-ai-agent/</link><guid isPermaLink="true">https://ansezz.com/blog/llm-vs-ai-agent/</guid><description>The architectural shift from LLMs to autonomous AI agents. How memory, tool-use, and planning turn a stateless model into a system that takes action.</description><pubDate>Sat, 06 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Most developers start their AI journey by sending a prompt to an API and waiting for a text response. This workflow works for simple summaries or creative writing but fails when the task requires real-world actions. You quickly realize that a single chat completion cannot manage a multi-step refund process in a Shopify store or navigate a complex database schema to generate a report.&lt;/p&gt;
&lt;p&gt;The limitation is not the intelligence of the model. The limitation is the architecture. Relying solely on Large Language Models (LLMs) is like having a genius professor who has no hands, no memory of yesterday, and no access to a computer. You get great answers but zero execution.&lt;/p&gt;
&lt;p&gt;AI agents solve this by wrapping the LLM in a system of tools, memory, and planning logic. This shift from &quot;talking to a model&quot; to &quot;building an autonomous system&quot; is one of the biggest changes in how we architect software today.&lt;/p&gt;
&lt;h2&gt;The LLM as a prediction engine&lt;/h2&gt;
&lt;p&gt;A Large Language Model is essentially a stateless next-token predictor. When you send a prompt, the model uses its training data to calculate the most probable sequence of words to follow your input. It does not &quot;think&quot; in the human sense. It performs a single forward pass through a massive neural network.&lt;/p&gt;
&lt;p&gt;The core characteristics of a standalone LLM include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Statelessness&lt;/strong&gt;: The model does not remember previous interactions unless you include them in the current prompt.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Knowledge cutoff&lt;/strong&gt;: It only knows what was in its training set up to a certain date.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;No external interaction&lt;/strong&gt;: By default, it cannot check your email, query your production database, or browse the web.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Single-turn logic&lt;/strong&gt;: It produces one output for one input. Any multi-step reasoning must happen within that single output.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For simple applications, this is sufficient. If you are building a basic &lt;a href=&quot;https://ansezz.com/blog/api-gateway-ai-stack/&quot;&gt;API gateway for AI&lt;/a&gt;, you might only need a thin wrapper around a model. However, as soon as you need the system to take initiative, the LLM alone is not enough.&lt;/p&gt;
&lt;h2&gt;Defining the AI agent: the reasoning loop&lt;/h2&gt;
&lt;p&gt;An AI agent is an autonomous system that uses an LLM as its central reasoning engine. Unlike a standard LLM call, an agent operates within a loop. It observes the environment, thinks about what to do next, takes an action using a tool, and then observes the result of that action to decide its next step.&lt;/p&gt;
&lt;p&gt;This loop allows the agent to correct its own mistakes. If a database query fails, a standalone LLM would simply report the failure in its final response. An agent, however, sees the error, analyzes what went wrong, and tries a different query. This pattern of interleaving a reasoning step with an action and then observing the result is the ReAct (Reasoning + Acting) approach introduced by Yao et al. in 2022.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/llm-vs-ai-agent/agency-pillars.webp&quot; alt=&quot;Bento grid showing the four pillars that turn an LLM into an AI agent: memory, tools, planning, and reasoning&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;The four pillars of agency&lt;/h2&gt;
&lt;p&gt;To transform an LLM into an agent, you must provide four critical layers of infrastructure.&lt;/p&gt;
&lt;h3&gt;1. Memory&lt;/h3&gt;
&lt;p&gt;While LLMs have a &quot;context window,&quot; this is temporary and expensive. Agents use external memory systems to maintain state across sessions. This includes short-term memory for current task steps and long-term memory for user preferences or historical data. We often use vector databases like pgvector to store and retrieve these memories efficiently — the distinction between the &lt;a href=&quot;https://ansezz.com/blog/context-window-vs-memory/&quot;&gt;context window and persistent memory&lt;/a&gt; is what separates a toy from a production agent.&lt;/p&gt;
&lt;h3&gt;2. Tools&lt;/h3&gt;
&lt;p&gt;Tools are the hands of the agent. These are external functions, APIs, or scripts that the agent can choose to execute. When an agent identifies that it needs information it doesn&apos;t have, it generates a structured command (typically a JSON object that conforms to the tool&apos;s schema) to call a tool. This is what lets agents act on modern stacks like Laravel or &lt;a href=&quot;https://ansezz.com/blog/agentic-commerce-shopify/&quot;&gt;a Shopify store&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;3. Planning&lt;/h3&gt;
&lt;p&gt;Complex goals need to be broken down into smaller, manageable tasks. An agent uses the LLM to create a roadmap. This might involve task decomposition where a high-level request like &quot;Audit our cloud spend&quot; is split into sub-tasks like &quot;List all AWS instances,&quot; &quot;Query pricing API,&quot; and &quot;Generate CSV report.&quot;&lt;/p&gt;
&lt;h3&gt;4. Reasoning&lt;/h3&gt;
&lt;p&gt;This is the logic that governs how the agent uses the other three pillars. It is the control loop that keeps the system running until the goal is achieved or a stopping condition is met.&lt;/p&gt;
&lt;h2&gt;Technical comparison&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Large Language Model (LLM)&lt;/th&gt;
&lt;th&gt;AI Agent&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Execution&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Passive (prompt-response)&lt;/td&gt;
&lt;td&gt;Active (goal-oriented)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Persistence&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;None (stateless)&lt;/td&gt;
&lt;td&gt;Persistent (external memory)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Capabilities&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Text generation and analysis&lt;/td&gt;
&lt;td&gt;Tool use, API calls, web browsing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Reasoning&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;One-shot internal logic&lt;/td&gt;
&lt;td&gt;Multi-step iterative loop&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Connectivity&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Isolated&lt;/td&gt;
&lt;td&gt;Integrated with external systems&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Architecture&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Model-centric&lt;/td&gt;
&lt;td&gt;System-centric&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;Transitioning from RAG to agentic workflows&lt;/h2&gt;
&lt;p&gt;Retrieval-Augmented Generation (RAG) was the first step toward more capable AI. In a standard RAG setup, you retrieve relevant documents from a vector store and stuff them into the LLM prompt. This is a linear process.&lt;/p&gt;
&lt;p&gt;Agentic RAG takes this further. Instead of a fixed retrieval step, the agent decides &lt;em&gt;when&lt;/em&gt; to search, &lt;em&gt;what&lt;/em&gt; keywords to use, and &lt;em&gt;whether&lt;/em&gt; the retrieved information was actually useful. If the first search results are poor, the agent refines its query and searches again. I cover this evolution in more depth in &lt;a href=&quot;https://ansezz.com/blog/rag-architectures-traditional-agentic-corrective/&quot;&gt;traditional vs agentic vs corrective RAG&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/llm-vs-ai-agent/reasoning-loop.webp&quot; alt=&quot;Circular diagram of an AI agent&apos;s reasoning loop: think, act with a tool, then observe the result before deciding the next step&quot; /&gt;&lt;/p&gt;
&lt;p&gt;For example, when dealing with &lt;a href=&quot;https://ansezz.com/blog/7-rag-mistakes-production/&quot;&gt;7 common RAG mistakes&lt;/a&gt;, an agentic approach can mitigate issues like &quot;hallucinated retrieval&quot; by cross-referencing multiple sources or validating facts against a structured database.&lt;/p&gt;
&lt;h2&gt;Building agents in production&lt;/h2&gt;
&lt;p&gt;Building an agentic system requires more than just an API key. You need a robust backend to manage the state and execute the tools. Frameworks like LangGraph and CrewAI are popular for Python developers. However, for those in the PHP ecosystem, you can build powerful agentic backends using Laravel and tools like &lt;a href=&quot;https://ansezz.com/blog/claude-mcp-dev-tools/&quot;&gt;Claude MCP&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The Model Context Protocol (MCP) is particularly exciting for agent development. It provides a standardized way for agents to connect to local and remote data sources. Instead of writing custom connectors for every tool, you can use MCP to give your agent immediate access to your filesystem, GitHub repos, or database schemas.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// Example of a tool definition in a Laravel-based agent
public function getTools(): array
{
    return [
        [
            &apos;name&apos; =&amp;gt; &apos;query_shopify_orders&apos;,
            &apos;description&apos; =&amp;gt; &apos;Retrieves order details from Shopify using GraphQL.&apos;,
            &apos;parameters&apos; =&amp;gt; [
                &apos;type&apos; =&amp;gt; &apos;object&apos;,
                &apos;properties&apos; =&amp;gt; [
                    &apos;order_id&apos; =&amp;gt; [&apos;type&apos; =&amp;gt; &apos;string&apos;],
                ],
            ],
        ],
    ];
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;By defining tools clearly, you allow the agent to understand exactly what it can do. The LLM then acts as the router, deciding which tool to trigger based on the user&apos;s intent.&lt;/p&gt;
&lt;h2&gt;When to choose an agent over an LLM&lt;/h2&gt;
&lt;p&gt;Not every feature needs to be an agent. Agents are more complex to build, harder to test, and can be more expensive due to multiple LLM calls.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Use a standalone LLM when:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;You need immediate, low-latency text generation.&lt;/li&gt;
&lt;li&gt;The task is simple and doesn&apos;t require external data.&lt;/li&gt;
&lt;li&gt;The workflow is linear and predictable.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Use an AI agent when:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The task requires multiple steps or logic branches.&lt;/li&gt;
&lt;li&gt;You need the system to interact with external APIs or databases.&lt;/li&gt;
&lt;li&gt;The goal is open-ended (e.g., &quot;Research this topic and find three competitors&quot;).&lt;/li&gt;
&lt;li&gt;You need the system to learn and adapt over time using memory.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Takeaways&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;LLMs are engines, not drivers.&lt;/strong&gt; They provide the reasoning power but require a system around them to perform real work.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Agency is architectural.&lt;/strong&gt; You build agency by adding memory, tool-use, and planning layers to your model.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Reasoning loops are key.&lt;/strong&gt; The ability to observe and correct actions is what makes agents autonomous.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Start with RAG, then move to agents.&lt;/strong&gt; Agentic RAG is a natural evolution for teams already using vector databases.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Standardize your tools.&lt;/strong&gt; Protocols like MCP make it easier to give agents access to the data they need without custom boilerplate.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Is your current AI implementation stuck in a passive prompt-response loop, or are you ready to build systems that actually take action? If you want help making that jump, &lt;a href=&quot;https://ansezz.com/services/&quot;&gt;here&apos;s how I work with teams shipping agents&lt;/a&gt;.&lt;/p&gt;
</content:encoded><category>ai</category><category>llm</category><category>agentic-ai</category><category>rag</category><category>architecture</category><category>mcp</category></item><item><title>DevOps vs MLOps: key technical differences for 2026</title><link>https://ansezz.com/blog/devops-vs-mlops/</link><guid isPermaLink="true">https://ansezz.com/blog/devops-vs-mlops/</guid><description>The critical differences between DevOps and MLOps. How to automate software delivery and manage machine learning lifecycles for production.</description><pubDate>Fri, 05 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Shipping application code to production is difficult. Shipping a machine learning model that continues to perform accurately over time is significantly harder. You might have a perfectly automated pipeline that builds your Docker images and deploys them to a cluster in seconds. However, if your model starts making incorrect predictions because of a change in consumer behavior, your automated deployment of &quot;correct&quot; code is effectively shipping a broken product. This is the core friction between traditional software operations and the emerging world of machine learning operations.&lt;/p&gt;
&lt;p&gt;That gap is what the DevOps vs MLOps comparison is really about. DevOps focuses on the reliability and speed of software delivery. It treats code as the primary artifact. MLOps takes those same principles and adds two volatile new variables: data and models. While DevOps solves the problem of &quot;it works on my machine,&quot; MLOps solves the problem of &quot;it worked on last month&apos;s training data.&quot;&lt;/p&gt;
&lt;p&gt;Understanding the technical divide between these two disciplines is essential for any team moving beyond simple heuristic-based software and into the world of &lt;a href=&quot;https://ansezz.com/blog/category/ai/&quot;&gt;AI engineering&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;The core philosophy of DevOps&lt;/h2&gt;
&lt;p&gt;DevOps is built on the foundation of the &lt;a href=&quot;https://ansezz.com/blog/ci-vs-cd/&quot;&gt;continuous integration and continuous delivery (CI/CD)&lt;/a&gt; pipeline. The primary goal is to shorten the development life cycle and ship changes continuously with high software quality. In a standard DevOps environment, like one running a &lt;a href=&quot;https://ansezz.com/blog/coolify-docker-saas-hosting/&quot;&gt;Laravel application on Docker&lt;/a&gt;, the process is relatively deterministic.&lt;/p&gt;
&lt;p&gt;If the source code passes its unit tests, the integration tests succeed, and the environment configuration is correct, the application should behave predictably in production. The artifacts are static. Once a binary or a container image is built, it does not change its internal logic based on the data flowing through it.&lt;/p&gt;
&lt;p&gt;Technical teams use DevOps to manage:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Source code:&lt;/strong&gt; Versioning application logic in Git.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Build automation:&lt;/strong&gt; Compiling code and packaging it into artifacts.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Infrastructure as Code (IaC):&lt;/strong&gt; Using tools like Terraform or Pulumi to define servers and networks.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Testing:&lt;/strong&gt; Running automated suites to ensure no regressions in logic.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/devops-vs-mlops/devops-pipeline.webp&quot; alt=&quot;Illustrated DevOps pipeline moving left to right through code, build, test, and deploy stages&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;The emergence of MLOps&lt;/h2&gt;
&lt;p&gt;MLOps, or Machine Learning Operations, is a set of practices that aims to deploy and maintain machine learning models in production reliably and efficiently. It is not just &quot;DevOps for AI.&quot; While it borrows the automation mindset, the artifacts in MLOps are fundamentally different.&lt;/p&gt;
&lt;p&gt;A machine learning system is composed of three distinct components: code, data, and the model. In DevOps, you only really care about the code. In MLOps, a change in the data can break the system even if the code remains untouched. This introduces a level of non-determinism that traditional CI/CD pipelines are not designed to handle.&lt;/p&gt;
&lt;p&gt;If you are building &lt;a href=&quot;https://ansezz.com/blog/agentic-commerce-shopify/&quot;&gt;agentic commerce solutions on Shopify&lt;/a&gt;, for example, your recommendation engine might fail not because of a bug in the Python script, but because the underlying distribution of customer purchases shifted during a holiday sale. This phenomenon, known as data drift, requires a lifecycle that includes continuous monitoring and retraining.&lt;/p&gt;
&lt;h2&gt;Key technical differences at a glance&lt;/h2&gt;
&lt;p&gt;Here are the primary technical distinctions between the two methodologies.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;DevOps&lt;/th&gt;
&lt;th&gt;MLOps&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Primary artifact&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Source code and binaries&lt;/td&gt;
&lt;td&gt;Code, datasets, and model weights&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Lifecycle&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Build, test, deploy&lt;/td&gt;
&lt;td&gt;Data prep, train, evaluate, deploy, monitor&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Testing&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Unit and integration tests&lt;/td&gt;
&lt;td&gt;Model validation, accuracy metrics, bias checks&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Deployment&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Static (service/binary)&lt;/td&gt;
&lt;td&gt;Dynamic (model endpoint or batch job)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Monitoring&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Latency, CPU, error rates&lt;/td&gt;
&lt;td&gt;Data drift, model decay, precision, recall&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Feedback loop&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Code bug fixes&lt;/td&gt;
&lt;td&gt;Continuous retraining (CT)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;The CI/CD/CT pipeline&lt;/h2&gt;
&lt;p&gt;The most significant architectural difference is the introduction of a third loop. While DevOps relies on CI (Continuous Integration) and CD (Continuous Delivery), MLOps introduces CT (Continuous Training).&lt;/p&gt;
&lt;h3&gt;Continuous Integration (CI) in MLOps&lt;/h3&gt;
&lt;p&gt;CI in this context is no longer just about testing code. It involves testing the data. You must validate the schema of incoming datasets and ensure that feature engineering logic is consistent between the training environment and the production environment. A mismatch here leads to training-serving skew, a common reason for model failure.&lt;/p&gt;
&lt;h3&gt;Continuous Delivery (CD) in MLOps&lt;/h3&gt;
&lt;p&gt;CD involves the automated deployment of the model service. This often means deploying a prediction API or updating a weights file in a running inference engine. Because models are large and computationally expensive, MLOps pipelines often use canary deployments or shadow testing to verify performance on live data before full rollouts.&lt;/p&gt;
&lt;h3&gt;Continuous Training (CT)&lt;/h3&gt;
&lt;p&gt;This is the unique signature of MLOps. A CT pipeline automatically triggers a retraining job when certain conditions are met. This might be a scheduled interval or a trigger based on performance degradation. The pipeline pulls new data, runs the training script, evaluates the new model version against a benchmark, and registers it in a model registry if it passes.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/devops-vs-mlops/mlops-retraining-loop.webp&quot; alt=&quot;Diagram of an MLOps continuous training loop cycling through data collection, retraining, model evaluation, and registry&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Monitoring and the problem of drift&lt;/h2&gt;
&lt;p&gt;In traditional &lt;a href=&quot;https://ansezz.com/blog/category/devops/&quot;&gt;DevOps&lt;/a&gt;, monitoring is centered on system health. You look at memory usage, response times, and HTTP 500 errors. If the server is up and the code is executing, the system is usually considered &quot;healthy.&quot;&lt;/p&gt;
&lt;p&gt;In MLOps, system health is only half the story. You also need to monitor statistical health. A model might be returning a &quot;200 OK&quot; status code with 50ms latency, but if the predictions it provides are nonsensical, the system is failing.&lt;/p&gt;
&lt;p&gt;There are two main types of drift that MLOps engineers must track:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Data drift:&lt;/strong&gt; The input data distribution changes. For example, a model trained on high-end fashion data might struggle if the store expands to include budget streetwear.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Concept drift:&lt;/strong&gt; The relationship between input and output changes. For instance, what was considered a &quot;normal&quot; credit card transaction in 2019 might look very different during a global supply chain crisis in 2026.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Detecting these requires sophisticated logging of every prediction and its eventual outcome, creating a feedback loop that informs the next training cycle. This is a critical step often missed in early-stage &lt;a href=&quot;https://ansezz.com/blog/ai-vs-traditional-development/&quot;&gt;AI development&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Tooling and infrastructure&lt;/h2&gt;
&lt;p&gt;The toolsets for these two fields are diverging. While there is overlap in the use of Docker and Kubernetes, the specialized needs of machine learning have given rise to a new stack.&lt;/p&gt;
&lt;p&gt;Standard DevOps tools include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;GitHub Actions / GitLab CI:&lt;/strong&gt; Pipeline orchestration.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Docker:&lt;/strong&gt; Containerization.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Terraform:&lt;/strong&gt; Infrastructure provisioning.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Prometheus / Grafana:&lt;/strong&gt; System monitoring.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;MLOps expands this list with:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;MLflow / Weights &amp;amp; Biases:&lt;/strong&gt; Experiment tracking and model versioning.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Kubeflow / Vertex AI:&lt;/strong&gt; ML-specific orchestration.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Feature stores:&lt;/strong&gt; Centralized repositories for pre-processed data features.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Evidently AI / Arize:&lt;/strong&gt; Statistical monitoring and drift detection.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Building a bridge between these tools is the hallmark of a mature technical organization. You need the stability of DevOps to host the infrastructure and the flexibility of MLOps to manage the intelligence layer.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/devops-vs-mlops/mlops-monitoring.webp&quot; alt=&quot;MLOps monitoring dashboard with drift charts alongside Python code for model registry and metrics tracking&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Bridging the gap in production&lt;/h2&gt;
&lt;p&gt;For teams managing complex web applications, the goal is not to choose one over the other. It is integration. A modern stack might use a Laravel backend for user management, hosted via a &lt;a href=&quot;https://ansezz.com/blog/coolify-self-hosted-saas/&quot;&gt;Coolify-managed VPS&lt;/a&gt;, while calling an MLOps-managed &lt;a href=&quot;https://ansezz.com/blog/training-vs-inference/&quot;&gt;inference endpoint&lt;/a&gt; for personalized content.&lt;/p&gt;
&lt;p&gt;One practical way to start is by implementing versioning for your datasets. Just as you would never deploy code without a commit hash, you should never train a model without knowing exactly which version of the data was used. This ensures reproducibility, which is the first step toward a functional MLOps workflow.&lt;/p&gt;
&lt;p&gt;The same rigor applies to retrieval systems. Treat vector databases and retrieval pipelines as managed infrastructure inside your DevOps workflow rather than ad-hoc scripts. That discipline alone heads off many of the &lt;a href=&quot;https://ansezz.com/blog/7-rag-mistakes-production/&quot;&gt;RAG failures&lt;/a&gt; teams hit in production.&lt;/p&gt;
&lt;h2&gt;Takeaways&lt;/h2&gt;
&lt;p&gt;Managing the technical transition from code-centric to model-centric operations requires a shift in how you view automation.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;DevOps is about code reliability:&lt;/strong&gt; Focus on CI/CD pipelines, automated testing, and deterministic deployments.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;MLOps is about model reliability:&lt;/strong&gt; Focus on data validation, experiment tracking, and statistical monitoring.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The CT loop is essential:&lt;/strong&gt; Continuous training prevents your models from becoming obsolete as real-world data evolves.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Monitoring must be dual-layered:&lt;/strong&gt; Track both system metrics (latency/errors) and model metrics (drift/accuracy).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Tooling is specialized:&lt;/strong&gt; Use standard DevOps tools for the hosting layer and MLOps tools for the training and registry layers.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Data is an artifact:&lt;/strong&gt; Treat your training data with the same versioning rigor you apply to your source code.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;How do you plan to handle the statistical monitoring of your models once they transition from a static environment to the unpredictable flow of production data? If you&apos;re standing up an MLOps practice, &lt;a href=&quot;https://ansezz.com/services/&quot;&gt;here&apos;s how I help teams build it&lt;/a&gt;.&lt;/p&gt;
</content:encoded><category>devops</category><category>devops</category><category>machine-learning</category><category>ai-engineering</category><category>ci-cd</category></item><item><title>Context window vs memory: building AI that remembers</title><link>https://ansezz.com/blog/context-window-vs-memory/</link><guid isPermaLink="true">https://ansezz.com/blog/context-window-vs-memory/</guid><description>Stop context-stuffing your LLM prompts. The difference between the context window and persistent memory — and how RAG + pgvector scale AI agents affordably.</description><pubDate>Thu, 04 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Large language models have a memory problem that costs you money and performance. You spend thousands of tokens feeding the same documentation, user history, and context into every single prompt. Yet the moment the session ends, the model forgets everything. It is like hiring a genius consultant who suffers from total amnesia the instant they walk out the door.&lt;/p&gt;
&lt;p&gt;This cycle of &quot;context stuffing&quot; is unsustainable for production systems. Relying solely on a massive context window leads to high latency and the &quot;lost in the middle&quot; phenomenon where models ignore data buried in the center of a large prompt. To build truly intelligent agentic systems, you must distinguish between the ephemeral context window and persistent long-term memory.&lt;/p&gt;
&lt;p&gt;Building a robust AI architecture requires a strategic mix of RAG, vector databases, and efficient context management. This guide breaks down the technical differences between context and memory, and how to implement a hybrid approach that scales.&lt;/p&gt;
&lt;h2&gt;Understanding the context window&lt;/h2&gt;
&lt;p&gt;The context window is the model&apos;s immediate working memory. It represents the total number of tokens (words or parts of words) the model can process at one specific moment. When you send a prompt to a frontier model like Claude or GPT, the context window includes your current instruction, the previous conversation history, and any files you have attached.&lt;/p&gt;
&lt;p&gt;Think of the context window as a physical desk. You can only fit so many papers on it at once. If you add a new stack of documents, you have to push the old ones off. Once a token falls out of this window, the model loses all awareness of it. It does not matter if the information was vital. It is gone.&lt;/p&gt;
&lt;p&gt;Modern models offer massive context windows — Claude is at one million tokens and Gemini reaches up to two million. While impressive, these are still temporary. They are not a replacement for a database. Using a large context window for everything is an expensive way to handle data that should be stored permanently. Bigger is not automatically better, either: accuracy and recall degrade as the window fills, a problem the field now calls &quot;context rot.&quot;&lt;/p&gt;
&lt;h2&gt;The illusion of long-term memory&lt;/h2&gt;
&lt;p&gt;Many developers mistake a long context window for long-term memory. It is a dangerous technical shortcut. If you are building for &lt;a href=&quot;https://ansezz.com/blog/agentic-commerce-shopify/&quot;&gt;Shopify Plus&lt;/a&gt;, for example, you might be tempted to dump your entire product catalog into the prompt.&lt;/p&gt;
&lt;p&gt;This creates three immediate problems:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Cost&lt;/strong&gt;: You pay for those tokens every time the user asks a question.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Latency&lt;/strong&gt;: The more tokens you send, the longer the model takes to reason and respond.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Accuracy&lt;/strong&gt;: Large context windows are prone to noise. The model may hallucinate because it is overwhelmed by irrelevant data.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;True long-term memory is persistent. It lives outside the model. It allows an AI agent to remember a user&apos;s preference from three months ago without needing that preference to be included in every single API call. This is where Retrieval-Augmented Generation (RAG) becomes the hero of your architecture.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/context-window-vs-memory/token-usage-dashboard.webp&quot; alt=&quot;Bento-grid dashboard contrasting high per-request token usage against a persistent vector database store&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;RAG: the external brain for AI&lt;/h2&gt;
&lt;p&gt;Retrieval-Augmented Generation (RAG) is the technical bridge between a stateless LLM and a persistent knowledge base. Instead of stuffing everything into the context window, you store your data in a vector database. When a query comes in, you perform a semantic search to find only the most relevant &quot;chunks&quot; of information.&lt;/p&gt;
&lt;p&gt;These chunks are then injected into the context window. This keeps the prompt lean and the model focused. RAG turns the &quot;desk&quot; (context window) into a tool for reasoning over a &quot;library&quot; (vector database).&lt;/p&gt;
&lt;p&gt;For technical teams using the Laravel ecosystem, implementing RAG has become significantly easier with tools like &lt;a href=&quot;https://github.com/pgvector/pgvector&quot;&gt;pgvector&lt;/a&gt;. You can store embeddings directly in your PostgreSQL database, allowing for seamless integration between your relational data and your AI logic.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Context Window&lt;/th&gt;
&lt;th&gt;Long-Term Memory (RAG)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Duration&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Session-based (Temporary)&lt;/td&gt;
&lt;td&gt;Persistent (Permanent)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Capacity&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Limited (1M–2M tokens)&lt;/td&gt;
&lt;td&gt;Virtually Unlimited&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Cost&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;High per-request cost&lt;/td&gt;
&lt;td&gt;Low per-request (fixed storage)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Update Speed&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Instant for the session&lt;/td&gt;
&lt;td&gt;Requires indexing/embedding&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;The economics of tokens vs infrastructure&lt;/h2&gt;
&lt;p&gt;Choosing between a larger context window and a RAG pipeline is an economic decision. For a small internal tool with ten users, context stuffing is fine. The engineering hours required to build a RAG pipeline would exceed the token savings.&lt;/p&gt;
&lt;p&gt;However, for a SaaS application or a high-volume e-commerce store, the math shifts quickly. Sending 100,000 tokens per request at scale will destroy your margins. A well-optimized RAG system might only send 2,000 tokens per request.&lt;/p&gt;
&lt;p&gt;The infrastructure cost of a vector database like Pinecone or a self-hosted pgvector instance is often a fraction of the cost of wasted tokens. The trade-offs between managed and self-hosted stores are worth weighing up front — see &lt;a href=&quot;https://ansezz.com/blog/picking-the-right-rag-stack/&quot;&gt;picking the right RAG stack&lt;/a&gt;. Either way, you avoid the common &lt;a href=&quot;https://ansezz.com/blog/7-rag-mistakes-production/&quot;&gt;RAG mistakes in production&lt;/a&gt; by focusing on retrieval quality rather than just prompt size.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/context-window-vs-memory/rag-data-flow.webp&quot; alt=&quot;RAG data flow diagram: a query runs semantic search over a vector database, then the top chunks are injected into the LLM context window&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Implementing persistent memory with pgvector&lt;/h2&gt;
&lt;p&gt;If you are running a Laravel application, you do not need a separate, complex vector database for basic memory. PostgreSQL with the pgvector extension is often the best choice for mid-sized applications. It allows you to perform similarity searches alongside your standard Eloquent queries.&lt;/p&gt;
&lt;p&gt;Imagine a user searching for &quot;shoes for rainy weather.&quot; In a traditional database, you would look for the keyword &quot;rainy.&quot; With embeddings and pgvector, the system understands the semantic relationship between &quot;rainy&quot; and &quot;waterproof.&quot;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// Example of a semantic search in Laravel using pgvector
$queryEmbedding = AI::generateEmbedding(&quot;shoes for rainy weather&quot;);

$products = Product::query()
    -&amp;gt;selectRaw(&apos;name, description, embedding &amp;lt;=&amp;gt; ? as distance&apos;, [$queryEmbedding])
    -&amp;gt;orderBy(&apos;distance&apos;)
    -&amp;gt;limit(5)
    -&amp;gt;get();
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;By storing these embeddings, you create a persistent memory of your product catalog that is always ready for the LLM to access. You only pull the relevant products into the context window when they are needed.&lt;/p&gt;
&lt;h2&gt;Claude MCP and the future of context&lt;/h2&gt;
&lt;p&gt;The Model Context Protocol (MCP) by Anthropic is a game-changer for how we handle context. It allows models to connect directly to external data sources like Google Drive, Slack, or your local filesystem.&lt;/p&gt;
&lt;p&gt;Instead of you manually managing what goes into the context window, the model uses &lt;a href=&quot;https://ansezz.com/blog/claude-mcp-dev-tools/&quot;&gt;Claude MCP servers&lt;/a&gt; to fetch what it needs on demand. This blurs the line between context and memory. The model &quot;remembers&quot; where to find information and retrieves it dynamically.&lt;/p&gt;
&lt;p&gt;This is the foundation for agentic systems. An agent that can query its own database or read its own logs via MCP doesn&apos;t need a massive context window. It needs a high &quot;reasoning capacity&quot; to know which piece of memory to grab at the right time.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/context-window-vs-memory/pgvector-code.webp&quot; alt=&quot;Pop-art-style code editor showing a pgvector semantic similarity query in a Laravel application&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Hybrid strategies for agentic systems&lt;/h2&gt;
&lt;p&gt;The most advanced AI systems do not choose one over the other. They use a tiered memory strategy.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Short-term context&lt;/strong&gt;: The last 5–10 messages of the conversation for immediate flow.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Episodic memory&lt;/strong&gt;: A RAG-based retrieval of previous conversations with the specific user.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Semantic knowledge&lt;/strong&gt;: A RAG-based retrieval of the general knowledge base (manuals, docs, catalogs).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Tool-based memory&lt;/strong&gt;: Using &lt;a href=&quot;https://ansezz.com/blog/circuit-breakers-vector-db/&quot;&gt;circuit breakers and vector DBs&lt;/a&gt; to ensure the system doesn&apos;t spiral when a tool fails.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This multi-layered approach ensures the model has exactly what it needs to solve a problem without the bloat of an oversized prompt.&lt;/p&gt;
&lt;h2&gt;Takeaways&lt;/h2&gt;
&lt;p&gt;Managing the boundary between context and memory is the difference between a toy and a production-grade AI application.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Stop context stuffing&lt;/strong&gt;: If your prompt is consistently over 50k tokens, you are likely wasting money and reducing accuracy.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Use RAG for persistence&lt;/strong&gt;: Move static or long-term data into a vector database like pgvector or Pinecone.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Optimize for latency&lt;/strong&gt;: Smaller prompts result in faster response times and a better user experience.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Leverage MCP&lt;/strong&gt;: Use the Model Context Protocol to give your agents a standardized way to &quot;reach out&quot; to their memory stores.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Monitor token counts&lt;/strong&gt;: Track how many tokens actually contribute to the final output versus how many are just noise.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;As models get smarter, the &quot;size&quot; of the desk becomes less important than the &quot;organization&quot; of the library. Are you building a bigger desk, or a better library? If you&apos;re designing memory for a production agent, &lt;a href=&quot;https://ansezz.com/services/&quot;&gt;here&apos;s how I help teams ship it&lt;/a&gt;.&lt;/p&gt;
</content:encoded><category>ai</category><category>rag</category><category>pgvector</category><category>llm</category><category>mcp</category><category>ai-engineering</category></item><item><title>Data engineer vs data scientist: roles, tools, and overlap</title><link>https://ansezz.com/blog/data-engineer-vs-data-scientist/</link><guid isPermaLink="true">https://ansezz.com/blog/data-engineer-vs-data-scientist/</guid><description>Data engineer vs data scientist: who builds the pipelines, who builds the models, where the tools overlap, and which role your AI project needs first.</description><pubDate>Thu, 04 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Your business is drowning in data, but your AI models are still hallucinating or returning irrelevant results. You have invested in the latest Large Language Models and vector databases, yet the output remains inconsistent because the underlying data is a fragmented mess of raw logs and poorly formatted JSON. Without a robust foundation, even the most advanced neural network is just a sophisticated guessing machine. The solution lies in understanding the distinct but complementary roles of the Data Engineer and the Data Scientist.&lt;/p&gt;
&lt;p&gt;While these two roles often share the same office space and use similar programming languages like Python and SQL, their core missions are fundamentally different. One builds the factory, while the other refines the product. In the modern technical landscape, especially when deploying &lt;a href=&quot;https://ansezz.com/blog/agentic-workflows-vibe-coding/&quot;&gt;agentic systems&lt;/a&gt; or RAG architectures, the distinction between infrastructure and inference is the difference between a project that scales and one that stays in the prototyping phase.&lt;/p&gt;
&lt;h2&gt;The architect vs. the detective&lt;/h2&gt;
&lt;p&gt;To understand the difference, consider the construction of a smart city. The Data Engineer is the civil architect and utility provider. They design the power grids, the water filtration systems, and the high-speed transit tunnels. Their goal is reliability, throughput, and structural integrity. If the water stops flowing or the lights flicker, the city ceases to function.&lt;/p&gt;
&lt;p&gt;The Data Scientist is the urban planner and behavioral psychologist. They look at the traffic patterns, the energy consumption rates, and the population growth to decide where to build the next park or how to optimize the public transport schedule. They do not build the pipes. They use the resources flowing through the pipes to make the city smarter and more efficient.&lt;/p&gt;
&lt;p&gt;In a technical stack, this means the Data Engineer manages the flow of information from source to storage. The Data Scientist then extracts that information to train models, run experiments, and generate business insights.&lt;/p&gt;
&lt;h2&gt;The role of the data engineer: building the pipeline&lt;/h2&gt;
&lt;p&gt;The Data Engineer is responsible for the design, construction, and maintenance of the systems that collect, store, and move data. They operate at the &quot;upstream&quot; end of the lifecycle. Their primary focus is on the &quot;three Vs&quot;: Volume, Velocity, and Variety.&lt;/p&gt;
&lt;h3&gt;Core responsibilities&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Pipeline Development&lt;/strong&gt;: Creating ETL (Extract, Transform, Load) or ELT processes that move data from diverse sources like Shopify APIs, Google Cloud buckets, or internal PostgreSQL databases into a centralized warehouse.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Infrastructure Management&lt;/strong&gt;: Setting up and managing data warehouses (Snowflake, BigQuery) and data lakes. This often involves working with &lt;a href=&quot;https://ansezz.com/blog/coolify-docker-saas-hosting/&quot;&gt;Docker and Coolify&lt;/a&gt; for self-hosted data tools or managing managed services on AWS.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Data Modeling&lt;/strong&gt;: Designing the schema and architecture of the data to ensure it is optimized for high-performance querying.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Reliability and Scaling&lt;/strong&gt;: Ensuring that the data infrastructure can handle spikes in traffic without crashing and that the data remains consistent across all systems.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Data Engineers spend a significant amount of time thinking about system architecture and DevOps principles. They are the ones who implement &lt;a href=&quot;https://ansezz.com/blog/circuit-breakers-vector-db/&quot;&gt;circuit breakers for vector databases&lt;/a&gt; and ensure that your RAG pipeline does not fail because of a sudden surge in API requests.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/data-engineer-vs-data-scientist/tools-grid.webp&quot; alt=&quot;A grid of technical tools including SQL, Python, Spark, and PyTorch in a pop-art style&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;The role of the data scientist: extracting the value&lt;/h2&gt;
&lt;p&gt;Once the data is clean, formatted, and accessible, the Data Scientist takes over. They operate &quot;downstream,&quot; using the curated datasets to solve specific business problems or create predictive features.&lt;/p&gt;
&lt;h3&gt;Core responsibilities&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Exploratory Data Analysis (EDA)&lt;/strong&gt;: Investigating datasets to find hidden patterns, outliers, or correlations that can inform business decisions.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Machine Learning Development&lt;/strong&gt;: Building and tuning models for classification, regression, or recommendation systems. This might involve using frameworks like PyTorch or TensorFlow.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;A/B Testing and Experimentation&lt;/strong&gt;: Designing and running tests to see how changes in a web application or Shopify store affect user behavior and conversion rates.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Data Storytelling&lt;/strong&gt;: Communicating complex mathematical findings to non-technical stakeholders through visualizations and reports.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Data Scientists are the ones who ask &quot;Why is our churn rate increasing?&quot; or &quot;What is the optimal price point for this new subscription service?&quot; They rely heavily on the Data Engineer to provide high-quality data. If the data is corrupted, the model will be biased or inaccurate.&lt;/p&gt;
&lt;h2&gt;Data engineer vs data scientist: a side-by-side view&lt;/h2&gt;
&lt;p&gt;The tools and skill sets overlap, but the application differs. Below is a breakdown of how these roles compare in a production environment.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Data Engineer&lt;/th&gt;
&lt;th&gt;Data Scientist&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Primary Goal&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Build and maintain data systems&lt;/td&gt;
&lt;td&gt;Analyze and model data for insights&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Common Languages&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;SQL, Python, Java, Scala&lt;/td&gt;
&lt;td&gt;Python, R, SQL&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Key Tools&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Spark, Kafka, Airflow, dbt, Docker&lt;/td&gt;
&lt;td&gt;scikit-learn, PyTorch, Jupyter, Tableau&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Main Output&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Cleaned datasets, APIs, pipelines&lt;/td&gt;
&lt;td&gt;Predictive models, insights, reports&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Mindset&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Engineering and reliability&lt;/td&gt;
&lt;td&gt;Statistics and experimentation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Cloud Focus&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Infrastructure (GCP, AWS, Terraform)&lt;/td&gt;
&lt;td&gt;Managed ML services (SageMaker, Vertex AI)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;While a Data Scientist might use SQL to pull a specific cohort of users for analysis, a Data Engineer uses SQL to optimize a transformation job that processes millions of rows per second.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/data-engineer-vs-data-scientist/data-architecture.webp&quot; alt=&quot;Pop-art diagram of a data pipeline flowing from sources through a warehouse to models, showing where engineers and scientists hand off&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;The synergy in modern AI stacks&lt;/h2&gt;
&lt;p&gt;In the era of LLMs and generative AI, the lines between these roles are becoming more blurred, yet the need for specialization is higher than ever. A similar boundary is being redrawn between the &lt;a href=&quot;https://ansezz.com/blog/ml-engineer-vs-ai-engineer/&quot;&gt;ML engineer and the AI engineer&lt;/a&gt;. Consider a Retrieval-Augmented Generation (RAG) system.&lt;/p&gt;
&lt;p&gt;The Data Engineer builds the ingestion pipeline that scrapes documentation, cleans the text, generates embeddings, and stores them in a vector database like pgvector. They ensure the &lt;a href=&quot;https://ansezz.com/blog/api-gateway-ai-stack/&quot;&gt;API gateway&lt;/a&gt; is secure and that the index stays fresh, whether through scheduled batch jobs or near-real-time streaming.&lt;/p&gt;
&lt;p&gt;The Data Scientist then steps in to evaluate the performance of the RAG system. They experiment with different embedding models, adjust the &quot;top-k&quot; retrieval parameters, and fine-tune the prompts to ensure the AI provides the most accurate and helpful responses.&lt;/p&gt;
&lt;p&gt;Without the engineer, the scientist has no data to query. Without the scientist, the engineer has a very expensive pipeline that delivers technically correct but practically useless results. Both are essential for moving beyond basic chatbots and into &lt;a href=&quot;https://ansezz.com/blog/agentic-commerce-shopify/&quot;&gt;agentic commerce&lt;/a&gt; solutions.&lt;/p&gt;
&lt;h2&gt;Which role does your project need?&lt;/h2&gt;
&lt;p&gt;If you are a startup or an established business looking to modernize your digital presence, you might wonder which hire to make first.&lt;/p&gt;
&lt;p&gt;You need a Data Engineer if:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Your data is scattered across multiple SaaS platforms and spreadsheets.&lt;/li&gt;
&lt;li&gt;Your existing reports are slow or frequently crash.&lt;/li&gt;
&lt;li&gt;You want to build a real-time data streaming platform or a scalable AI infrastructure.&lt;/li&gt;
&lt;li&gt;You are migrating to the cloud (GCP or AWS) and need a custom pipeline.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You need a Data Scientist if:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;You have a large amount of clean data but do not know how to use it.&lt;/li&gt;
&lt;li&gt;You want to build recommendation engines or churn prediction models.&lt;/li&gt;
&lt;li&gt;You need to run complex experiments to optimize your marketing spend.&lt;/li&gt;
&lt;li&gt;You want to leverage AI to find specific insights that are not obvious through standard reporting.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Often, the best approach is to build the engineering foundation first. It is impossible to do science on a pile of broken pipes.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/data-engineer-vs-data-scientist/workspace.webp&quot; alt=&quot;A pop-art comic-style workspace with a laptop showing code, coffee, and a plant&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Takeaways&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Data Engineers&lt;/strong&gt; focus on the &quot;how&quot; of data movement and storage. They are software engineers specialized in distributed systems and infrastructure.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Data Scientists&lt;/strong&gt; focus on the &quot;what&quot; and &quot;why&quot; of the data. They are statisticians and analysts specialized in extracting meaning and building models.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Collaboration&lt;/strong&gt; is key for AI success. A successful RAG or agentic system requires high-quality engineering to feed high-quality science.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Tooling&lt;/strong&gt; overlaps in Python and SQL, but engineers lean toward orchestration (Airflow, dbt) while scientists lean toward modeling (scikit-learn, PyTorch).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Foundation First&lt;/strong&gt;: You cannot perform meaningful data science without a robust data engineering infrastructure.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;How are you currently handling the gap between your raw data infrastructure and your analytical insights? If you&apos;re building that foundation for production AI, &lt;a href=&quot;https://ansezz.com/services/&quot;&gt;here&apos;s how I help teams ship it&lt;/a&gt;.&lt;/p&gt;
</content:encoded><category>career</category><category>machine-learning</category><category>ai</category><category>infrastructure</category><category>databases</category><category>career</category></item><item><title>Cloud engineer vs DevOps engineer in 2026</title><link>https://ansezz.com/blog/cloud-engineer-vs-devops-engineer/</link><guid isPermaLink="true">https://ansezz.com/blog/cloud-engineer-vs-devops-engineer/</guid><description>Cloud engineer vs DevOps engineer: how their roles, skills, and tools differ, where they merge into platform engineering, and which one to hire first.</description><pubDate>Wed, 03 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;The cloud engineer vs DevOps engineer question trips up almost every team that outgrows a single server. The titles overlap, the job posts blur them together, and hiring the wrong specialty has a real cost: infrastructure that is over-engineered and expensive, or fast to ship but prone to outages. The two roles solve different problems. This guide breaks down where they diverge, where they overlap, and which one to hire first.&lt;/p&gt;
&lt;h2&gt;The cloud engineer: designing the foundation&lt;/h2&gt;
&lt;p&gt;A Cloud Engineer acts as the architect of your digital workspace. Their primary responsibility is to design, implement, and manage the cloud infrastructure where your applications reside. This role focuses on the &quot;where&quot; and the &quot;what&quot; of your technical stack. They spend their time navigating the vast catalogs of providers like AWS, Google Cloud Platform (GCP), or Azure.&lt;/p&gt;
&lt;p&gt;Cloud Engineers specialize in building the environment itself. This includes designing Virtual Private Clouds (VPCs), configuring subnets, and managing identity and access management (IAM) policies. They ensure that your database clusters are highly available and that your storage solutions are cost-effective. By 2026, Cloud Engineering has evolved to include sophisticated FinOps practices. They monitor resource consumption to prevent surprise bills from unoptimized cloud usage.&lt;/p&gt;
&lt;p&gt;When you need to migrate a legacy system to a modern cloud-native architecture, the Cloud Engineer leads the way. They translate business requirements into technical blueprints. For instance, they might design an &lt;a href=&quot;https://ansezz.com/blog/api-gateway-ai-stack/&quot;&gt;API gateway architecture&lt;/a&gt; to manage traffic for a distributed AI application. Their focus is on the resilience, security, and scalability of the infrastructure layer.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/cloud-engineer-vs-devops-engineer/cloud-infrastructure-design.webp&quot; alt=&quot;Comic-style illustration of a cloud engineer designing VPCs, IAM, and database infrastructure&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;The DevOps engineer: automating the lifecycle&lt;/h2&gt;
&lt;p&gt;While the Cloud Engineer builds the foundation, the DevOps Engineer builds the machinery that moves code onto that foundation. DevOps is a methodology focused on the software delivery lifecycle. A DevOps Engineer is the bridge between development and operations teams. Their goal is to maximize the speed and reliability of software releases through automation.&lt;/p&gt;
&lt;p&gt;The core of a DevOps engineer&apos;s toolkit is the &lt;a href=&quot;https://ansezz.com/blog/ci-vs-cd/&quot;&gt;CI/CD pipeline&lt;/a&gt; — continuous integration and continuous delivery. They use tools like GitHub Actions, GitLab CI, or Jenkins to automate the building, testing, and deployment of code. If a developer pushes a change, the DevOps Engineer&apos;s systems ensure that code is automatically scanned for vulnerabilities and deployed to a staging environment without manual intervention.&lt;/p&gt;
&lt;p&gt;Reliability is another major pillar. DevOps Engineers implement observability stacks using Prometheus and Grafana. They set up sophisticated alerting systems to catch issues before they affect end users. In modern setups, they often leverage tools like Docker and &lt;a href=&quot;https://ansezz.com/blog/coolify-docker-saas-hosting/&quot;&gt;Coolify for hosting&lt;/a&gt; to simplify deployment workflows. They care deeply about the &quot;how&quot; of software delivery.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/cloud-engineer-vs-devops-engineer/cicd-automation.webp&quot; alt=&quot;Comic-style illustration of a CI/CD pipeline automating build, test, and deploy stages&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Side-by-side comparison: skills and focus&lt;/h2&gt;
&lt;p&gt;To better understand which professional fits your current needs, it helps to compare their daily priorities. While there is a significant overlap, the center of gravity for each role is different. Both rely heavily on infrastructure as code (IaC) using tools like Terraform or Pulumi — the same tooling I weigh up in &lt;a href=&quot;https://ansezz.com/blog/terraform-vs-ansible/&quot;&gt;Terraform vs Ansible&lt;/a&gt;.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Cloud Engineer&lt;/th&gt;
&lt;th&gt;DevOps Engineer&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Primary Goal&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Infrastructure design and management&lt;/td&gt;
&lt;td&gt;Automation and software delivery&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Key Focus&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Networking, Security, Storage, Cost&lt;/td&gt;
&lt;td&gt;CI/CD, Deployment, Monitoring, Culture&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Cloud Expertise&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Deep (multi-service architecture)&lt;/td&gt;
&lt;td&gt;Functional (deployment targets)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Coding Focus&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Infrastructure scripts (Terraform, HCL)&lt;/td&gt;
&lt;td&gt;Glue code and automation (Bash, Python)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;KPIs&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Uptime, Cost efficiency, Security&lt;/td&gt;
&lt;td&gt;Deployment frequency, Mean Time to Recovery&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Tools&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;AWS/GCP/Azure, VPC, IAM, RDS&lt;/td&gt;
&lt;td&gt;GitHub Actions, Docker, Kubernetes, Ansible&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Cloud Engineers are often the ones who decide whether to use a managed database service or a self-hosted one. DevOps Engineers are the ones who ensure that the migration to that database happens seamlessly with zero downtime.&lt;/p&gt;
&lt;h2&gt;The overlap: platform engineering in 2026&lt;/h2&gt;
&lt;p&gt;The line between Cloud and DevOps has become increasingly blurred. In high-growth startups and mature enterprises, these roles often merge into what is known as Platform Engineering. The platform team builds an &quot;Internal Developer Platform&quot; (IDP). This allows developers to self-serve their own infrastructure needs within pre-defined guardrails.&lt;/p&gt;
&lt;p&gt;Platform engineering combines the architectural knowledge of a cloud engineer with the automation mindset of a DevOps engineer. They use &lt;a href=&quot;https://ansezz.com/blog/docker-vs-kubernetes/&quot;&gt;Kubernetes&lt;/a&gt; (EKS, GKE, or AKS) as the base layer. By abstracting away the complexity of the cloud, they enable product teams to move faster. If this sounds adjacent to site reliability, it is — I unpack the boundaries in &lt;a href=&quot;https://ansezz.com/blog/sre-vs-platform-engineer/&quot;&gt;SRE vs platform engineer&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;For companies running Laravel applications, this might involve setting up a standardized Docker-based environment, so the code runs the same way on a local machine as it does in a production cluster on GCP. Browse the &lt;a href=&quot;https://ansezz.com/blog/category/devops/&quot;&gt;DevOps category&lt;/a&gt; to see how these workflows are structured in practice.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/cloud-engineer-vs-devops-engineer/toolchain-bento.webp&quot; alt=&quot;Bento grid of platform engineering toolchain icons: Terraform, Docker, Kubernetes, and cloud providers&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Choosing the right hire for your business&lt;/h2&gt;
&lt;p&gt;Determining which role to hire first depends on your current technical debt and business stage. If you are starting from scratch or moving away from a single-server setup, you need a Cloud Engineer. They will ensure your architecture is secure and scalable from day one. Without this foundation, your automation efforts will be built on top of a fragile environment.&lt;/p&gt;
&lt;p&gt;If you already have a functional cloud setup but your developers are burning hours on manual deployments and firefighting, you need a DevOps engineer. They will untangle your release process and implement the guardrails necessary for rapid iteration. They focus on developer experience and velocity.&lt;/p&gt;
&lt;p&gt;Large-scale e-commerce businesses using Shopify Plus often find that their needs are hybrid. They might need custom middleware hosted on cloud infrastructure while maintaining a rigid CI/CD process for their custom Shopify apps. In these scenarios, having a specialist who understands both the infrastructure and the automation pipeline is vital.&lt;/p&gt;
&lt;h2&gt;Takeaways&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Cloud Engineers&lt;/strong&gt; specialize in infrastructure architecture, cloud-native services, security, and cost optimization.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;DevOps Engineers&lt;/strong&gt; focus on the software delivery pipeline, automation, CI/CD, and system reliability.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Tools overlap&lt;/strong&gt; heavily, with both roles requiring proficiency in Terraform, Docker, and at least one major cloud provider.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Platform Engineering&lt;/strong&gt; is the modern synthesis of both roles, aimed at providing self-service infrastructure to developers.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Hire a Cloud Engineer&lt;/strong&gt; when you need to build or modernize the &quot;hardware&quot; of your digital business.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Hire a DevOps Engineer&lt;/strong&gt; when you need to speed up your release cycle and automate manual operational tasks.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;How does your current team balance the need for architectural stability with the demand for rapid feature delivery? &lt;a href=&quot;https://ansezz.com/services/&quot;&gt;Here&apos;s how I help teams strike that balance&lt;/a&gt;.&lt;/p&gt;
</content:encoded><category>career</category><category>devops</category><category>infrastructure</category><category>ci-cd</category><category>cloud-platforms</category><category>career</category></item><item><title>CDN vs cache: why your high-traffic site needs both</title><link>https://ansezz.com/blog/cdn-vs-cache/</link><guid isPermaLink="true">https://ansezz.com/blog/cdn-vs-cache/</guid><description>CDN and cache solve different problems: distance vs repeated work. How to layer edge delivery and server-side caching for high-traffic sites.</description><pubDate>Tue, 02 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;High-traffic applications often collapse under the weight of redundant data requests. Every time a user in London requests a file from a server in New York, the latency kills the user experience and drains your server resources. If you do not optimize how data is stored and delivered, your infrastructure costs will skyrocket as your performance plateaus.&lt;/p&gt;
&lt;p&gt;The solution lies in understanding the distinct roles of Content Delivery Networks (CDNs) and general caching. While many developers use these terms interchangeably, they serve different masters in the architecture stack. The CDN-vs-cache decision is rarely either/or: mastering the synergy between a global CDN and local caching is what lets a high-traffic site stay fast under load.&lt;/p&gt;
&lt;h2&gt;The fundamental speed gap&lt;/h2&gt;
&lt;p&gt;Latency is the silent killer of conversion rates. A standard request involves multiple round-trips between the client and the origin server. If the data has to travel across the Atlantic, the physical distance imposes a speed limit that no amount of code optimization can fix. This is a latency problem, not a bandwidth one — a &lt;a href=&quot;https://ansezz.com/blog/bandwidth-vs-throughput/&quot;&gt;wider pipe won&apos;t fix the lag&lt;/a&gt; when the bottleneck is round-trip distance.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/cdn-vs-cache/latency-edge.webp&quot; alt=&quot;Pop-art comic panel contrasting high latency from a distant origin server with low latency from a nearby CDN edge server&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Caching solves this by keeping data closer to the execution point. A CDN solves this by keeping that cache closer to the user. When these two work together, they create a multi-layered shield that protects your origin server from unnecessary load.&lt;/p&gt;
&lt;h2&gt;Understanding the cache hierarchy&lt;/h2&gt;
&lt;p&gt;Caching is a broad technical concept that refers to the temporary storage of data for faster retrieval. It is not limited to a single location. In a modern stack, caching happens at multiple layers to ensure that the most expensive operations are never repeated.&lt;/p&gt;
&lt;p&gt;The most common layers include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Browser cache&lt;/strong&gt;: Assets like CSS, JS, and images are stored on the user&apos;s device. This makes repeat visits feel instantaneous.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Server cache&lt;/strong&gt;: Tools like Redis or Memcached store database query results and computed objects in memory. This prevents the application from hitting the database for every single request. The same in-memory layer powers more advanced patterns too, like &lt;a href=&quot;https://ansezz.com/blog/redis-semantic-caching-rag/&quot;&gt;semantic caching in RAG pipelines&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Application cache&lt;/strong&gt;: Modern frameworks like &lt;a href=&quot;https://ansezz.com/blog/category/laravel/&quot;&gt;Laravel&lt;/a&gt; have built-in mechanisms to cache entire HTML fragments or API responses.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The primary goal of caching is to reduce the workload on your primary infrastructure. By storing the result of a complex calculation or a heavy database query, you trade a small amount of memory for a massive gain in response time.&lt;/p&gt;
&lt;h2&gt;The global reach of CDNs&lt;/h2&gt;
&lt;p&gt;A CDN is a specialized type of cache that lives at the &quot;edge&quot; of the network. While a standard cache might live on your main server, a CDN is a globally distributed network of servers designed to host your content in dozens of geographical locations simultaneously.&lt;/p&gt;
&lt;p&gt;When a user visits your site, the CDN intercepts the request — acting as a &lt;a href=&quot;https://ansezz.com/blog/forward-proxy-vs-reverse-proxy/&quot;&gt;reverse proxy&lt;/a&gt; sitting in front of your origin. If the requested asset is already stored at the edge server closest to the user, it is served immediately. This bypasses the need to travel back to your origin server entirely. This is particularly critical for static assets like images, video files, and large JavaScript bundles.&lt;/p&gt;
&lt;p&gt;For businesses running on &lt;a href=&quot;https://ansezz.com/blog/category/shopify/&quot;&gt;Shopify&lt;/a&gt;, the platform handles much of this CDN logic automatically — its CDN (backed by Cloudflare) ships on every plan, not just Plus. Even so, understanding how to manage cache headers and purge cycles remains a vital skill for custom development.&lt;/p&gt;
&lt;h2&gt;Technical comparison: CDN vs cache&lt;/h2&gt;
&lt;p&gt;To choose the right tool for the job, you must understand where each one excels. The following table breaks down the core technical differences between a standard internal cache and a global CDN.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Local/Server Cache&lt;/th&gt;
&lt;th&gt;Content Delivery Network (CDN)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Primary Goal&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Reduce CPU and database load.&lt;/td&gt;
&lt;td&gt;Reduce network latency and distance.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Location&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;On or near the origin server.&lt;/td&gt;
&lt;td&gt;At the network edge (Points of Presence).&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Scope&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Often specific to a single server or user.&lt;/td&gt;
&lt;td&gt;Shared across many users globally.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Ownership&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Part of your application infrastructure.&lt;/td&gt;
&lt;td&gt;Typically a third-party service provider.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Best For&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;DB queries, session data, fragments.&lt;/td&gt;
&lt;td&gt;Images, JS, CSS, static HTML, video.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Caching is about how the data is stored. A CDN is about where that data is distributed. You use a cache to stop the server from doing the same work twice. You use a CDN to stop the data from traveling across the world twice.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/cdn-vs-cache/cache-layers.webp&quot; alt=&quot;Pop-art technical diagram showing the four caching layers a request passes through: browser, CDN edge, server cache, and database&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Implementation in Laravel and Shopify&lt;/h2&gt;
&lt;p&gt;Implementing these concepts varies depending on your tech stack. In a &lt;a href=&quot;https://ansezz.com/blog/category/laravel/&quot;&gt;Laravel environment&lt;/a&gt;, you often manage caching through a unified API that supports multiple drivers.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// Example of caching a database query in Laravel
$users = Cache::remember(&apos;active_users&apos;, 3600, function () {
    return DB::table(&apos;users&apos;)-&amp;gt;where(&apos;active&apos;, true)-&amp;gt;get();
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This code snippet ensures that the database is only queried once per hour for this specific data. To take this a step further, you would configure your web server to set &lt;code&gt;Cache-Control&lt;/code&gt; headers that a CDN like Cloudflare or Akamai can interpret.&lt;/p&gt;
&lt;p&gt;In the world of &lt;a href=&quot;https://ansezz.com/blog/category/shopify/&quot;&gt;Shopify development&lt;/a&gt;, you rely heavily on the platform&apos;s native CDN. Shopify uses a robust edge network to serve product images and storefront assets. However, when building custom apps or headless storefronts, you must manually manage how your API responses are cached to prevent performance bottlenecks.&lt;/p&gt;
&lt;h2&gt;Synergy: using both for maximum scale&lt;/h2&gt;
&lt;p&gt;The most resilient architectures use a &quot;cache-aside&quot; pattern combined with an edge delivery strategy. This creates a multi-step defense for your origin server.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;The edge shield&lt;/strong&gt;: The CDN handles the majority of incoming traffic for static files and common public pages.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The server shield&lt;/strong&gt;: For requests that reach the origin, a server-side cache (like Redis) provides immediate data without hitting the database.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The database shield&lt;/strong&gt;: Proper indexing and internal database caching provide a final layer of optimization.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This layered approach is essential for &lt;a href=&quot;https://ansezz.com/blog/category/devops/&quot;&gt;modern DevOps&lt;/a&gt; practices. It ensures that even during a massive traffic spike, such as a Black Friday sale or a viral product launch, your core application remains responsive.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/cdn-vs-cache/purge-dashboard.webp&quot; alt=&quot;Pop-art dashboard UI showing CDN performance metrics next to a purge cache button for clearing the edge cache&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Common pitfalls to avoid&lt;/h2&gt;
&lt;p&gt;Managing two different caching systems introduces the challenge of cache invalidation. If you update a product price in your database but the CDN is still serving a cached version of the page, you will face customer service issues.&lt;/p&gt;
&lt;p&gt;Always implement a clear &quot;purge&quot; strategy. When data changes on your origin, you must have a mechanism to clear both your internal cache and the corresponding edge cache on your CDN. Many developers use webhooks or event listeners to automate this process. This ensures that your users always see the most accurate data without sacrificing speed.&lt;/p&gt;
&lt;h2&gt;Takeaways&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Caching&lt;/strong&gt; is the technique of storing data temporarily to avoid repeating expensive operations.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;CDN&lt;/strong&gt; is a network of servers that use caching to deliver content from a location physically close to the user.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Distance matters&lt;/strong&gt;: A CDN reduces the physical travel time of data, while a local cache reduces the processing time.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Use both&lt;/strong&gt;: Implement server-side caching for database results and a CDN for static assets to achieve the best performance.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Invalidation is key&lt;/strong&gt;: Ensure your system can purge outdated data from all layers simultaneously to maintain data integrity.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;How do you handle cache invalidation across multiple edge locations when your application data changes in real-time? If you&apos;re wiring edge delivery and caching into a high-traffic build, &lt;a href=&quot;https://ansezz.com/services/&quot;&gt;here&apos;s how I help teams ship it&lt;/a&gt;.&lt;/p&gt;
</content:encoded><category>devops</category><category>networking</category><category>redis</category><category>performance</category><category>laravel</category><category>shopify</category><category>infrastructure</category></item><item><title>CI vs CD: automating quality and delivery</title><link>https://ansezz.com/blog/ci-vs-cd/</link><guid isPermaLink="true">https://ansezz.com/blog/ci-vs-cd/</guid><description>Continuous Integration vs Continuous Delivery explained. How to automate the path from commit to production for Laravel backends and Shopify storefronts.</description><pubDate>Tue, 02 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Shipping a broken build to production is an expensive way to realize your local environment isn&apos;t a mirror of reality. For developers working on Laravel backends or Shopify storefronts, the gap between &quot;it works on my machine&quot; and a stable production release is often filled with manual steps that invite human error. Manual FTP uploads, SSHing into servers to run migrations, or manually publishing Shopify themes are risks that modern engineering cannot afford.&lt;/p&gt;
&lt;p&gt;Continuous Integration (CI) and Continuous Delivery (CD) are the two pillars that eliminate this uncertainty. By automating the path from a code commit to a live environment, teams can ship faster while actually increasing the stability of their applications. This guide breaks down the core differences between CI and CD and how to apply them to modern software stacks.&lt;/p&gt;
&lt;h2&gt;Defining Continuous Integration (CI)&lt;/h2&gt;
&lt;p&gt;Continuous Integration is the practice of merging all developer working copies to a shared mainline several times a day. In a Laravel or Vue.js environment, this means that every time you push code to a branch, an automated system builds the application and runs a suite of tests.&lt;/p&gt;
&lt;p&gt;The goal of CI is to identify &quot;integration hell&quot; before it happens. Instead of waiting for a weekly release to find out that two developers changed the same core service, the CI pipeline flags the conflict within minutes. A standard CI pipeline for a Laravel application typically includes several automated steps.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Dependency management:&lt;/strong&gt; Installing Composer and NPM packages to ensure the environment is fresh.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Static analysis:&lt;/strong&gt; Running tools like PHPStan or Laravel Pint to enforce code quality and style.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Automated testing:&lt;/strong&gt; Executing unit and feature tests using PHPUnit or Pest.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Security audits:&lt;/strong&gt; Scanning for known vulnerabilities in third-party dependencies.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;By the time a pull request is ready for review, the CI system has already provided a &quot;green light&quot; confirming that the code is syntactically correct and doesn&apos;t break any behavior your tests cover.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/ci-vs-cd/pipeline-stages.webp&quot; alt=&quot;Pop-art bento grid showing the build, test, and deploy stages of a CI/CD pipeline flowing left to right&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Defining Continuous Delivery and Deployment (CD)&lt;/h2&gt;
&lt;p&gt;While CI focuses on the quality of the code, Continuous Delivery (CD) focuses on the delivery of that code. There are two distinct flavors of CD that are often confused: Continuous Delivery and Continuous Deployment.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Continuous Delivery&lt;/strong&gt; ensures that every build that passes the CI pipeline is ready to be deployed to production. However, the actual release to the live environment requires a manual trigger. This is common in regulated industries or for high-stakes storefronts where a marketing manager might want to time a release.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Continuous Deployment&lt;/strong&gt; takes this a step further by removing the manual trigger. Every change that passes the pipeline is automatically pushed to the production environment. This demands deep confidence in your test suite and the &lt;a href=&quot;https://ansezz.com/blog/logging-vs-monitoring/&quot;&gt;observability to catch regressions fast&lt;/a&gt; — without it, a bad commit reaches users before anyone notices.&lt;/p&gt;
&lt;p&gt;For developers using &lt;a href=&quot;https://ansezz.com/blog/coolify-self-hosted-saas/&quot;&gt;Coolify for self-hosted SaaS&lt;/a&gt;, CD becomes the engine that powers rapid iteration. Once the CI passes, Coolify can automatically pull the latest Docker image and update the running containers with zero downtime.&lt;/p&gt;
&lt;h2&gt;CI vs CD: the core differences&lt;/h2&gt;
&lt;p&gt;The primary difference lies in the scope and the ultimate goal. CI is about the developer&apos;s experience and code integrity. CD is about the user&apos;s experience and the release process.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Continuous Integration (CI)&lt;/th&gt;
&lt;th&gt;Continuous Delivery (CD)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Primary goal&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Detect bugs and integration issues early.&lt;/td&gt;
&lt;td&gt;Ensure code is always in a releasable state.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Trigger&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Triggered by every code commit or push.&lt;/td&gt;
&lt;td&gt;Triggered after CI passes successfully.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Key output&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;A validated code base and test reports.&lt;/td&gt;
&lt;td&gt;A deployable artifact (Docker image, ZIP, etc.).&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Manual step&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Fully automated process.&lt;/td&gt;
&lt;td&gt;Manual approval (Delivery) or fully automated (Deployment).&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;In 2026, the lines are blurring as tools become more integrated. Most teams treat them as a single, fluid pipeline where code flows from a developer&apos;s IDE directly into a staging or production environment.&lt;/p&gt;
&lt;h2&gt;Implementing CI for Laravel applications&lt;/h2&gt;
&lt;p&gt;Laravel provides a robust foundation for CI because of its built-in testing capabilities. When setting up a pipeline, you should aim for a &quot;clean room&quot; environment. This means using Docker to ensure the CI environment exactly matches your production environment.&lt;/p&gt;
&lt;p&gt;A typical GitHub Actions workflow for Laravel might look like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;name: Laravel CI

on: [push, pull_request]

jobs:
  laravel-tests:
    runs-on: ubuntu-latest
    services:
      mysql:
        image: mysql:8.0
        env:
          MYSQL_DATABASE: testing
          MYSQL_ALLOW_EMPTY_PASSWORD: &quot;yes&quot;
        ports:
          - 3306:3306
        options: &amp;gt;-
          --health-cmd=&quot;mysqladmin ping&quot;
          --health-interval=10s --health-timeout=5s --health-retries=5

    steps:
      - uses: actions/checkout@v4
      - name: Setup PHP
        uses: shivammathur/setup-php@v2
        with:
          php-version: &quot;8.3&quot;
      - name: Install Dependencies
        run: composer install --prefer-dist --no-interaction
      - name: Execute Tests
        run: vendor/bin/phpunit
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This simple script ensures that no code can be merged into the main branch unless it passes every test case. It creates a safety net that allows the team to innovate without the fear of breaking the core application logic.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/ci-vs-cd/github-actions-yaml.webp&quot; alt=&quot;Pop-art code editor displaying a GitHub Actions workflow YAML file for a Laravel CI job&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;CD best practices for Shopify storefronts&lt;/h2&gt;
&lt;p&gt;Shopify development has traditionally been a manual process of editing themes in the browser. However, with the rise of &lt;a href=&quot;https://ansezz.com/blog/agentic-commerce-shopify/&quot;&gt;agentic commerce on Shopify&lt;/a&gt;, the need for professional CI/CD has skyrocketed.&lt;/p&gt;
&lt;p&gt;For Shopify, CD is about managing &quot;theme versions&quot; rather than server binaries. Using the Shopify CLI, you can automate the deployment of theme changes to different environments.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Staging stores:&lt;/strong&gt; Always deploy to a development or staging store first.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Theme checks:&lt;/strong&gt; Use &lt;code&gt;shopify theme check&lt;/code&gt; in your CI pipeline to catch Liquid errors before they go live.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Atomic deploys:&lt;/strong&gt; Push your code to a new, unpublished theme ID. Once verified, use the CLI to publish it as the live theme. The cutover is instant, so customers never see a half-deployed storefront — a blue-green flavor of deployment for e-commerce.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;A broken layout or a missing &quot;Add to Cart&quot; button caused by a Liquid syntax error never reaches the live store.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/ci-vs-cd/shopify-deploy.webp&quot; alt=&quot;Pop-art scene of a Shopify theme deployment dashboard on a tablet showing a published theme swap&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;The bridge: automation and quality&lt;/h2&gt;
&lt;p&gt;The real power of CI/CD is realized when you move beyond simple tests and start incorporating advanced checks. Modern pipelines in 2026 often include visual regression testing, where the system takes screenshots of the UI and compares them to the previous version to detect unintended layout shifts.&lt;/p&gt;
&lt;p&gt;For teams building &lt;a href=&quot;https://ansezz.com/blog/api-gateway-ai-stack/&quot;&gt;API gateways for the AI stack&lt;/a&gt;, CI/CD is non-negotiable. When your application relies on LLMs or external APIs, the pipeline must include integration tests that verify these connections are still active and behaving as expected.&lt;/p&gt;
&lt;h2&gt;Takeaways&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;CI is for quality:&lt;/strong&gt; Use Continuous Integration to run tests, lint code, and check for security vulnerabilities on every push.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;CD is for delivery:&lt;/strong&gt; Use Continuous Delivery to automate the packaging and deployment of your app to staging or production.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Speed is a feature:&lt;/strong&gt; Keep your pipelines fast. A CI pipeline that takes 30 minutes to run is a pipeline that developers will try to bypass.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Immutable artifacts:&lt;/strong&gt; Build your application once (e.g., a Docker image) and promote that same artifact through staging and production.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Zero downtime:&lt;/strong&gt; Use strategies like blue-green deployments or symlink swaps to ensure users aren&apos;t interrupted during a release.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Shift left:&lt;/strong&gt; Move security and quality checks as early in the process as possible to reduce the cost of fixing bugs.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If your deployment process still involves a &quot;deployment checklist&quot; that a human has to follow, you aren&apos;t doing CD. Automation is the only way to scale a modern software business without scaling the frequency of production outages.&lt;/p&gt;
&lt;p&gt;How much time does your team spend manually verifying releases before they go live? If you want a pipeline that does it for you, &lt;a href=&quot;https://ansezz.com/services/&quot;&gt;here&apos;s how I help teams automate delivery&lt;/a&gt;.&lt;/p&gt;
</content:encoded><category>devops</category><category>devops</category><category>ci-cd</category><category>laravel</category><category>shopify</category></item><item><title>API vs MCP: connecting AI systems</title><link>https://ansezz.com/blog/api-vs-mcp/</link><guid isPermaLink="true">https://ansezz.com/blog/api-vs-mcp/</guid><description>The core differences between traditional APIs and the Model Context Protocol (MCP) — and when to use each to build scalable, agentic AI systems.</description><pubDate>Mon, 01 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Your AI agents are currently trapped in a silo of brittle, custom-coded API integrations that break the moment you update a schema. You spend weeks writing boilerplate glue code just to give a Large Language Model (LLM) access to a single database or a CRM. This manual mapping of endpoints to prompts is the hidden tax of modern AI engineering. It creates a maintenance nightmare where every new tool requires a fresh round of documentation reading and prompt tuning.&lt;/p&gt;
&lt;p&gt;The friction is real. While APIs have powered the internet for decades, they were never designed for the non-deterministic, reasoning-heavy nature of AI agents. We are moving from a world of hard-coded connections to a world of dynamic capability discovery. This is where the Model Context Protocol (MCP) enters the chat.&lt;/p&gt;
&lt;h2&gt;The API era: the power grid of the web&lt;/h2&gt;
&lt;p&gt;Traditional APIs (Application Programming Interfaces) are the foundation of modern software. Whether it is a RESTful endpoint, a GraphQL query, or a gRPC stream, the core concept is the same. It is a defined contract for software-to-software communication. You know exactly what input to send, and you get a predictable output in return.&lt;/p&gt;
&lt;p&gt;In the context of AI development, APIs are like the power grid. They provide the raw energy and data your application needs to function. If you are building a &lt;a href=&quot;https://ansezz.com/blog/agentic-commerce-shopify/&quot;&gt;Shopify application&lt;/a&gt;, you use the Shopify Admin API to fetch orders or update inventory. The logic is rigid, the schemas are fixed, and the developer is responsible for knowing exactly which endpoint to call at which time.&lt;/p&gt;
&lt;p&gt;The problem arises when you want an LLM to use these tools. You have to write &quot;tool definitions&quot; or &quot;function schemas&quot; that describe the API to the model. You are essentially translating the API documentation into a format the model can understand. This works for one or two tools, but it does not scale to an entire ecosystem of internal data sources and external services.&lt;/p&gt;
&lt;h2&gt;What is Model Context Protocol?&lt;/h2&gt;
&lt;p&gt;The Model Context Protocol (MCP) is an open standard introduced by Anthropic to solve the &quot;M x N&quot; integration problem. In a traditional setup, if you have M different AI clients (like Claude, ChatGPT, and an internal agent) and N different tools (like GitHub, Slack, and your internal DB), you have to build M x N individual integrations.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/api-vs-mcp/rest-graphql-mcp.webp&quot; alt=&quot;A bento grid comparison showing REST, GraphQL, and MCP as different technical modules&quot; /&gt;&lt;/p&gt;
&lt;p&gt;MCP changes this to an M + N problem. By creating a standardized &quot;universal plug,&quot; any MCP-compatible AI client can connect to any MCP-compliant server. Think of it as the USB-C of the AI world. Instead of writing custom code to explain how to search your database, you host an MCP server that describes its own capabilities. When the AI agent connects, it &quot;discovers&quot; what the server can do without you having to hard-code the instructions.&lt;/p&gt;
&lt;p&gt;This protocol uses JSON-RPC 2.0 under the hood. It allows for bidirectional communication, meaning the server can provide tools, resources (like files or data), and even prompt templates to the model. This is a massive shift from the stateless, request-response nature of most web APIs.&lt;/p&gt;
&lt;h2&gt;Structural differences: discovery and consumers&lt;/h2&gt;
&lt;p&gt;The fundamental difference between API and MCP lies in who consumes the interface and how they find what they need.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Traditional API&lt;/th&gt;
&lt;th&gt;Model Context Protocol (MCP)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Primary Consumer&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Human Developers&lt;/td&gt;
&lt;td&gt;AI Agents &amp;amp; LLMs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Discovery&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Manual (Reading Docs)&lt;/td&gt;
&lt;td&gt;Automatic (Self-describing)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Interface&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Heterogeneous (REST/GraphQL)&lt;/td&gt;
&lt;td&gt;Standardized (JSON-RPC)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;State&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Mostly Stateless&lt;/td&gt;
&lt;td&gt;Stateful Sessions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Flexibility&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Rigid / Pre-scripted&lt;/td&gt;
&lt;td&gt;Dynamic / Reasoning-based&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;APIs are built for humans to read and implement. You look at a Swagger UI, understand the authentication, and write a fetch request. MCP is built for models to navigate. An MCP server provides a list of tools and resources along with natural language descriptions. The model uses its reasoning capabilities to decide which tool to call based on the user&apos;s intent.&lt;/p&gt;
&lt;p&gt;If you are &lt;a href=&quot;https://ansezz.com/blog/ai-vs-traditional-development/&quot;&gt;comparing AI vs traditional development&lt;/a&gt;, this is a prime example of the shift. Traditional development is about building the pipes. AI engineering with MCP is about building the connectors that allow the model to choose its own pipes.&lt;/p&gt;
&lt;h2&gt;Why AI agents need MCP for scale&lt;/h2&gt;
&lt;p&gt;Agentic systems require a level of autonomy that traditional APIs struggle to provide efficiently. When an agent is tasked with &quot;researching a company and drafting a proposal,&quot; it might need to hit a search tool, a CRM, a file system, and a PDF generator.&lt;/p&gt;
&lt;p&gt;Using standard APIs, you would need to feed the descriptions of all those endpoints into the model&apos;s context window. This eats up tokens and increases the risk of the model getting confused. With MCP, the client negotiates capabilities at connection time and the agent calls tools on demand, so you can surface only the tools relevant to the task instead of pre-loading every schema. The result is less wasted context and fewer irrelevant choices for the model to wade through.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/api-vs-mcp/agent-tool-selection.webp&quot; alt=&quot;An AI agent dashboard dynamically selecting tools from an MCP server&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Furthermore, MCP defines a &quot;resource&quot; primitive. Unlike an API endpoint that just returns data on request, an MCP resource can be subscribed to, so the server pushes a notification when the underlying file or record changes. For lightweight retrieval-augmented generation (RAG) cases, an MCP server can expose your local files or database directly to the model in a standardized way, sparing you a custom ingestion layer for every experiment. It does not replace a vector store for large-scale semantic search, though — get that wrong and you hit the usual &lt;a href=&quot;https://ansezz.com/blog/7-rag-mistakes-production/&quot;&gt;production RAG mistakes&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Implementation: how they work together&lt;/h2&gt;
&lt;p&gt;It is important to realize that MCP does not replace APIs. In fact, MCP usually wraps existing APIs. Your business logic, security constraints, and data persistence still live in your core services.&lt;/p&gt;
&lt;p&gt;Consider a scenario where you have a Laravel backend. You have a set of REST endpoints for managing customer orders. To make these accessible to an AI agent, you would build a small MCP server (perhaps using Node.js or Python) that sits between the agent and your Laravel API.&lt;/p&gt;
&lt;p&gt;The MCP server handles the &quot;translation&quot; layer. It describes the &lt;code&gt;get_order_history&lt;/code&gt; tool in a way that an LLM understands. When the LLM calls that tool through the MCP protocol, the MCP server executes the actual REST call to your Laravel backend, receives the JSON, and passes the relevant context back to the model.&lt;/p&gt;
&lt;p&gt;This separation of concerns allows your core engineering team to focus on building robust APIs while your AI team focuses on creating the MCP wrappers that enable agentic behavior.&lt;/p&gt;
&lt;h2&gt;Choosing the right tool for the job&lt;/h2&gt;
&lt;p&gt;When should you stick to direct API calls, and when should you implement MCP? The answer depends on the complexity of your integrations and the role of the LLM.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Use direct APIs when:&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;You have a simple request-response flow with no AI reasoning involved.&lt;/li&gt;
&lt;li&gt;You need absolute minimum latency for high-throughput systems.&lt;/li&gt;
&lt;li&gt;You are performing deterministic tasks like processing payments or bulk data migrations.&lt;/li&gt;
&lt;li&gt;You only have one or two integrations that rarely change.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;Use MCP when:&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;You are building complex agents that need to use three or more tools.&lt;/li&gt;
&lt;li&gt;Your tools and data sources change frequently.&lt;/li&gt;
&lt;li&gt;You want your tools to be reusable across different AI clients (e.g., &lt;a href=&quot;https://ansezz.com/blog/claude-mcp-dev-tools/&quot;&gt;Claude Desktop and custom IDE tools&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;You need to expose local data or specialized resources to an LLM without building a custom backend for every experiment.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;One boundary worth drawing early: MCP covers the agent-to-tool axis only. The moment two agents need to delegate work to each other, you are in a different protocol&apos;s territory — see &lt;a href=&quot;https://ansezz.com/blog/mcp-vs-a2a-vs-acp/&quot;&gt;MCP vs A2A vs ACP&lt;/a&gt; for how the two layers stack.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/api-vs-mcp/rest-vs-mcp-code.webp&quot; alt=&quot;Side-by-side code comparison of a REST endpoint call and an MCP tool definition&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Takeaways&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;APIs are the &quot;what&quot; (the data and logic), while MCP is the &quot;how&quot; (how the AI interacts with that data).&lt;/li&gt;
&lt;li&gt;MCP eliminates the M x N integration problem by providing a universal protocol for AI tool discovery.&lt;/li&gt;
&lt;li&gt;Traditional APIs are designed for deterministic software-to-software paths; MCP is designed for non-deterministic AI-to-tool reasoning.&lt;/li&gt;
&lt;li&gt;Implementing MCP often involves wrapping existing REST or GraphQL APIs to make them self-describing for LLMs.&lt;/li&gt;
&lt;li&gt;Using MCP reduces token waste and prompt complexity by standardizing how context is shared with the model.&lt;/li&gt;
&lt;li&gt;For modern AI engineering, MCP is becoming the standard layer for building scalable agentic workflows.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;What is the biggest friction point you face when giving your AI agents access to your internal production databases? If you&apos;re wiring MCP into a real product, &lt;a href=&quot;https://ansezz.com/services/&quot;&gt;here&apos;s how I help teams ship it&lt;/a&gt;.&lt;/p&gt;
</content:encoded><category>architecture</category><category>mcp</category><category>api-design</category><category>ai</category><category>llm</category><category>agentic-ai</category><category>architecture</category></item><item><title>Ditch expensive cloud providers for self-hosted SaaS</title><link>https://ansezz.com/blog/coolify-self-hosted-saas/</link><guid isPermaLink="true">https://ansezz.com/blog/coolify-self-hosted-saas/</guid><description>The cloud tax kills SaaS margins early. How self-hosted Coolify on cheap ARM instances at Hetzner or Oracle slashes a $500 AWS bill toward zero.</description><pubDate>Sat, 30 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Most SaaS founders are quietly getting robbed by their own cloud provider.&lt;/p&gt;
&lt;p&gt;I have spent over a decade building and scaling web applications, and if there is one thing I have learned, it is that the &quot;cloud tax&quot; is the most effective way to kill your margins before you even find product-market fit. We have been conditioned to believe that unless our small CRUD app is running on a multi-region, auto-scaling AWS EKS cluster, we are doing it wrong.&lt;/p&gt;
&lt;p&gt;That is a lie designed to keep you paying for complexity you do not need.&lt;/p&gt;
&lt;h2&gt;The architecture of a trap&lt;/h2&gt;
&lt;p&gt;It starts innocently enough. You sign up for AWS or GCP because they give you $1,000 in credits. You spin up an RDS instance for your database, a few S3 buckets for storage, and maybe a managed Kubernetes service because it feels &quot;professional.&quot;&lt;/p&gt;
&lt;p&gt;Then the credits run out.&lt;/p&gt;
&lt;p&gt;Suddenly, you are paying $200 a month for a database that is 99% idle. You are paying for NAT gateways, provisioned IOPS, and &quot;management fees&quot; for services that could easily run on a $5 VPS. You are stuck in a web of proprietary APIs and IAM roles that require a full-time DevOps engineer just to update an environment variable.&lt;/p&gt;
&lt;p&gt;This is the agitation: managed services feel like a superpower at the start, but they become a golden cage as you scale. The complexity overhead alone is enough to slow your development velocity to a crawl. When I look at how &lt;a href=&quot;https://ansezz.com/blog/ai-vs-traditional-development/&quot;&gt;AI is changing traditional development&lt;/a&gt;, it becomes clear that we need to move faster, not get bogged down in infrastructure molasses.&lt;/p&gt;
&lt;h2&gt;Enter Coolify: Heroku&apos;s open-source soulmate&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/coolify-self-hosted-saas/features.webp&quot; alt=&quot;Coolify dashboard listing core features: git deploys, automatic SSL, one-click databases, and PR previews&quot; /&gt;&lt;/p&gt;
&lt;p&gt;The solution I have moved my entire stack to is Coolify.&lt;/p&gt;
&lt;p&gt;Coolify is an open-source, self-hostable alternative to Vercel, Heroku, and Netlify. It gives you that same &quot;git push to deploy&quot; experience we all love, but it runs on your own hardware. Whether you have a small ARM VPS on Hetzner (the CAX11 starts around €6/month) or a free ARM instance on Oracle Cloud, Coolify turns it into a private PaaS.&lt;/p&gt;
&lt;p&gt;I recently wrote about how &lt;a href=&quot;https://ansezz.com/blog/coolify-docker-saas-hosting/&quot;&gt;Coolify and Docker are changing SaaS hosting&lt;/a&gt;, but the shift is deeper than just a tool change. It is a mindset shift toward technical sovereignty.&lt;/p&gt;
&lt;p&gt;Here is what makes Coolify a game-changer for a senior engineer:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Zero vendor lock-in&lt;/strong&gt; — your configurations are stored on your server. If Coolify disappeared tomorrow, your Docker containers would keep running.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Automatic SSL&lt;/strong&gt; — it handles Let&apos;s Encrypt out of the box. No more messing with Nginx configs or certbot.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Database management&lt;/strong&gt; — you can spin up Postgres, MySQL, Redis, or MongoDB in one click. They run as containers on your server, meaning you pay $0 in additional managed service fees.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Pull request deployments&lt;/strong&gt; — it creates temporary environments for every PR, just like Vercel, but without the &quot;team seat&quot; tax.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;The magic of ARM (Graviton and OCI)&lt;/h2&gt;
&lt;p&gt;Once you have self-hosting in place, ARM is the multiplier that takes the bill from &quot;cheap&quot; to &quot;almost free.&quot; Stop renting x86 and start renting ARM.&lt;/p&gt;
&lt;p&gt;AWS pegs its Graviton instances at up to 20% lower cost and up to 40% better price-performance than comparable x86 instances. But the real &quot;cheat code&quot; right now is Oracle Cloud Infrastructure (OCI). Its &quot;Always Free&quot; tier gives you 2 ARM Ampere A1 OCPUs and 12 GB of RAM at no cost, forever (Oracle halved this from 4 OCPUs / 24 GB in mid-2026, so size accordingly).&lt;/p&gt;
&lt;p&gt;I can run a small production workload — frontend, backend, database, and Redis — on that single free instance using Coolify.&lt;/p&gt;
&lt;p&gt;When you pair ARM efficiency with a self-hosted orchestrator, the math changes. A startup that was paying $500/month on AWS can often move that entire workload to a sub-$15/month ARM instance on Hetzner — or to OCI&apos;s free tier outright. That difference goes straight back into your runway or your marketing budget.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/coolify-self-hosted-saas/architecture.webp&quot; alt=&quot;Diagram of a self-hosted SaaS on one ARM instance: frontend, backend, Postgres, and Redis containers behind Coolify&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Docker and Nix: the engine room&lt;/h2&gt;
&lt;p&gt;Coolify relies heavily on Docker, which is the industry standard for a reason. It ensures that what works on my machine works on the server. But as I move deeper into the &quot;vibe coding&quot; era, I&apos;m also looking at how technologies like Nix can further stabilize our environments.&lt;/p&gt;
&lt;p&gt;By using Nix flakes to define our development environment and Docker to package the runtime, we create a bulletproof deployment pipeline. When I use tools like the &lt;a href=&quot;https://ansezz.com/blog/mcp-context-aware-agents/&quot;&gt;Model Context Protocol (MCP)&lt;/a&gt;, I want my AI agents to have a clear, reproducible environment to work within. Self-hosting doesn&apos;t mean &quot;unprofessional&quot; — it means having total control over the stack.&lt;/p&gt;
&lt;h2&gt;Comparison: the hidden cost of &quot;easy&quot;&lt;/h2&gt;
&lt;p&gt;Let&apos;s look at the numbers for a standard Laravel or Node.js app with a database and a background worker.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The managed path (Vercel + Supabase + AWS S3):&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Vercel Pro: $20/month per user&lt;/li&gt;
&lt;li&gt;Supabase Pro: $25/month&lt;/li&gt;
&lt;li&gt;AWS S3 + bandwidth: $15/month&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Total: $60+/month (and rising with every user/teammate)&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;The Coolify path (Hetzner VPS):&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;4 vCPU ARM / 8GB RAM (CAX21): ~$12/month&lt;/li&gt;
&lt;li&gt;Backups to S3-compatible storage: $1/month&lt;/li&gt;
&lt;li&gt;Coolify: $0 (open source)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Total: ~$13/month&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The &quot;managed&quot; path costs roughly 5x more before you even have your first 100 users — and that gap widens with every teammate and every GB of egress. For a senior engineer, the half hour it takes to install Coolify on a fresh Linux box is worth the thousands of dollars saved over the life of the project.&lt;/p&gt;
&lt;h2&gt;Practical steps to a self-hosted SaaS stack&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/coolify-self-hosted-saas/workspace.webp&quot; alt=&quot;A developer workspace with a terminal mid-deploy, set up for self-hosted Coolify deployment&quot; /&gt;&lt;/p&gt;
&lt;p&gt;If you are tired of the cloud tax, here is my recommended path to freedom:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Grab a VPS&lt;/strong&gt; — I recommend Hetzner for raw performance/price or OCI for their insane free tier. Pick an ARM-based instance (Ubuntu 24.04).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Install Coolify&lt;/strong&gt; — run the one-line install command from their documentation. It takes about 5 minutes.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Connect your Git&lt;/strong&gt; — link your GitHub or GitLab account.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Dockerize your app&lt;/strong&gt; — if you are using Laravel, it is as simple as adding a &lt;code&gt;Dockerfile&lt;/code&gt;. For Vite or Next.js, Coolify has built-in builders that don&apos;t even require a &lt;code&gt;Dockerfile&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Move your DB&lt;/strong&gt; — export your managed DB and import it into a Coolify-managed container. Set up S3 backups immediately.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;The bottom line&lt;/h2&gt;
&lt;p&gt;We are entering a cycle where efficiency is the only thing that matters. The days of &quot;VC-subsidized&quot; infrastructure are over. Whether you are building a small tool or a massive enterprise SaaS, you owe it to your bottom line to look at self-hosting.&lt;/p&gt;
&lt;p&gt;Coolify has matured to the point where the developer experience is indistinguishable from the big players. The only difference is who owns the keys to the castle. And when you do outgrow a single box, the platform scales with you — I cover that in &lt;a href=&quot;https://ansezz.com/blog/scaling-with-coolify/&quot;&gt;advanced Coolify deployment strategies&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I am curious: what is the most &quot;expensive&quot; mistake you have ever made on a cloud bill — a forgotten NAT gateway or a runaway Lambda function? Drop a note via &lt;a href=&quot;https://ansezz.com/contact/&quot;&gt;contact&lt;/a&gt;. 🤘&lt;/p&gt;
</content:encoded><category>devops</category><category>coolify</category><category>self-hosting</category><category>devops</category><category>docker</category><category>multi-tenancy</category></item><item><title>Machine learning vs generative AI</title><link>https://ansezz.com/blog/ml-vs-genai/</link><guid isPermaLink="true">https://ansezz.com/blog/ml-vs-genai/</guid><description>Machine Learning predicts; Generative AI creates. The discriminative-vs-generative split decides your architecture, infra, and monthly cloud bill.</description><pubDate>Sat, 30 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Your data is likely sitting idle in a warehouse while competitors ship agentic systems that write code and predict churn in the same stack. Choosing the wrong architectural path between traditional machine learning and generative AI burns GPU credits and ships models that hallucinate when they should be calculating.&lt;/p&gt;
&lt;p&gt;Marketing hype blurs the line between these two technologies, and stakeholders use the terms interchangeably. That mismatch breeds technical debt. Build a generative model for a task that demands strict mathematical precision and you set yourself up for failure. Rely on traditional models for creative automation and your product feels rigid and outdated.&lt;/p&gt;
&lt;p&gt;The fix is understanding the engineering differences between predictive and creative systems. This guide breaks down machine learning vs generative AI so you can build scalable, high-performance applications with confidence. If you want to zoom out first, my breakdown of &lt;a href=&quot;https://ansezz.com/blog/ai-vs-machine-learning/&quot;&gt;AI vs machine learning&lt;/a&gt; shows where both sit in the broader AI hierarchy.&lt;/p&gt;
&lt;h2&gt;Machine learning: the logic of prediction&lt;/h2&gt;
&lt;p&gt;Traditional Machine Learning (ML) is the backbone of modern data science. It is primarily a discriminative technology. This means its job is to distinguish between different types of data or to predict a numerical value based on historical patterns. When you use an ML model, you are essentially asking it to categorize an input or forecast a trend.&lt;/p&gt;
&lt;p&gt;Machine Learning models thrive on structured data. They look at rows and columns in a database to find correlations. For instance, a regression model might analyze thousands of Shopify transactions to predict next month&apos;s revenue. A classification model might look at server logs to identify a potential DDoS attack.&lt;/p&gt;
&lt;p&gt;The architecture of traditional ML is often task-specific. You train a model for one specific purpose. If you want to detect fraud, you train a fraud detection model. That model cannot suddenly start recommending products. It is a precision tool designed for a single objective. This specialization makes ML highly efficient for high-stakes environments where accuracy and predictability are the metrics that matter most.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/ml-vs-genai/ml-dashboard.webp&quot; alt=&quot;Machine learning dashboard showing prediction charts, classification metrics, and model code&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Generative AI: the architecture of creation&lt;/h2&gt;
&lt;p&gt;Generative AI (GenAI) represents a paradigm shift. While traditional ML analyzes data to make a choice, GenAI uses data to create something entirely new. It is built on deep learning architectures, specifically Transformers and Diffusion models. These systems do not just classify data. They learn the underlying probability distribution of their training set to generate novel outputs.&lt;/p&gt;
&lt;p&gt;When you prompt a frontier Large Language Model (LLM) like Claude or GPT, you are interacting with a generative system. It predicts the next most likely token in a sequence, but the result is a coherent piece of text, a code snippet, or an image. This creative capability is what makes GenAI so versatile for building &lt;a href=&quot;https://ansezz.com/blog/agentic-commerce-shopify/&quot;&gt;agentic commerce solutions on Shopify&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;GenAI models are often foundation models. They are trained on massive, unstructured datasets and carry billions of parameters. This allows them to perform a wide variety of tasks without needing to be retrained from scratch. A single model can summarize a legal document, write a Python script, and brainstorm marketing copy. This flexibility is its greatest strength, but it also introduces the risk of hallucinations.&lt;/p&gt;
&lt;h2&gt;Machine learning vs generative AI: discriminative vs generative models&lt;/h2&gt;
&lt;p&gt;The fundamental technical difference between machine learning and generative AI lies in their mathematical objectives. To understand which one to deploy, look at how each one processes information.&lt;/p&gt;
&lt;h3&gt;Discriminative models (traditional ML)&lt;/h3&gt;
&lt;p&gt;Discriminative models learn the boundary between classes. Mathematically, they model the conditional probability &lt;code&gt;P(y | x)&lt;/code&gt;. This means &quot;given the input &lt;code&gt;x&lt;/code&gt;, what is the probability of the label &lt;code&gt;y&lt;/code&gt;?&quot;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Focus:&lt;/strong&gt; Determining the difference between data points.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Output:&lt;/strong&gt; A discrete label (Spam/Not Spam) or a continuous value (Price).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Efficiency:&lt;/strong&gt; Typically requires less computational power for inference.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Example:&lt;/strong&gt; A Random Forest algorithm identifying fraudulent credit card transactions.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Generative models (GenAI)&lt;/h3&gt;
&lt;p&gt;Generative models learn how the data itself is distributed. They model the joint probability &lt;code&gt;P(x, y)&lt;/code&gt; or the probability of the input itself &lt;code&gt;P(x)&lt;/code&gt;. This means &quot;what does a typical example of this data look like?&quot;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Focus:&lt;/strong&gt; Understanding the structure of the data to replicate it.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Output:&lt;/strong&gt; New data samples (Text, Image, Audio).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Efficiency:&lt;/strong&gt; Highly resource-intensive, requiring specialized GPUs.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Example:&lt;/strong&gt; A Transformer model generating a new Laravel controller based on a prompt.&lt;/li&gt;
&lt;/ul&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Machine Learning (Discriminative)&lt;/th&gt;
&lt;th&gt;Generative AI (Generative)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Primary Goal&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Classify or Predict&lt;/td&gt;
&lt;td&gt;Create New Content&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Data Type&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Structured (Tables, Logs)&lt;/td&gt;
&lt;td&gt;Unstructured (Text, Images)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Output Type&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Numbers, Labels, Scores&lt;/td&gt;
&lt;td&gt;Content, Code, Media&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Model Complexity&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Low to Medium&lt;/td&gt;
&lt;td&gt;Very High&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Training Data&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Task-Specific, Labeled&lt;/td&gt;
&lt;td&gt;Massive, Unlabeled/Self-Supervised&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/ml-vs-genai/genai-architecture.webp&quot; alt=&quot;Generative AI architecture diagram showing transformer neural network layers processing input tokens into generated output&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Use cases: when to choose one over the other&lt;/h2&gt;
&lt;p&gt;Choosing the right tool depends on your business goals and the nature of your data. Using an LLM to predict a stock price is an expensive mistake. Using a linear regression model to write a blog post is impossible.&lt;/p&gt;
&lt;h3&gt;When to use Machine Learning&lt;/h3&gt;
&lt;p&gt;Machine Learning is superior for tasks requiring high precision and deterministic logic. If you are working through &lt;a href=&quot;https://ansezz.com/work/&quot;&gt;complex technical challenges in a real product&lt;/a&gt;, ML is often the right choice for the &quot;back-office&quot; logic.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Fraud Detection:&lt;/strong&gt; Identifying anomalies in financial transactions where false positives must be minimized.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Inventory Forecasting:&lt;/strong&gt; Predicting stock levels for an e-commerce store based on seasonal trends.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Recommendation Engines:&lt;/strong&gt; Ranking products for a user based on their previous browsing history.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Medical Diagnostics:&lt;/strong&gt; Analyzing lab results to flag specific health markers.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;When to use Generative AI&lt;/h3&gt;
&lt;p&gt;Generative AI shines when you need to bridge the gap between human language and machine logic. It is the perfect tool for building intuitive interfaces and automating creative workflows.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Code Generation:&lt;/strong&gt; Automating the creation of boilerplate code or converting legacy code to modern frameworks like Laravel and Vue.js.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Content Personalization:&lt;/strong&gt; Generating unique product descriptions or email subject lines for thousands of customers.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Search and Retrieval:&lt;/strong&gt; Using Retrieval-Augmented Generation (RAG) to allow users to &quot;chat&quot; with their own documentation. You can learn more about this in our guide on &lt;a href=&quot;https://ansezz.com/blog/7-rag-mistakes-production/&quot;&gt;common RAG mistakes&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Prototyping:&lt;/strong&gt; Quickly generating UI mockups or synthetic data to test a new application.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;The hybrid future: combining ML and GenAI in production&lt;/h2&gt;
&lt;p&gt;In 2026, the most successful engineering teams are not choosing one over the other. They are building hybrid architectures. These systems use traditional ML for the &quot;heavy lifting&quot; of data processing and Generative AI for the user-facing interaction layer.&lt;/p&gt;
&lt;p&gt;Consider a modern customer support system. A traditional ML model can be used to perform sentiment analysis and route the ticket to the correct department based on urgency. Once the ticket is routed, a Generative AI agent can draft a response using internal knowledge bases. This combination ensures that the system is both efficient and empathetic.&lt;/p&gt;
&lt;p&gt;Another example is in the realm of &lt;a href=&quot;https://ansezz.com/blog/ai-vs-traditional-development/&quot;&gt;AI vs traditional development&lt;/a&gt;. We often see systems where an ML model predicts which users are likely to churn. A Generative AI model then creates a custom discount offer and a personalized email specifically tailored to that user&apos;s interests to keep them engaged.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/ml-vs-genai/hybrid-architecture.webp&quot; alt=&quot;Hybrid AI architecture combining a vector database, an LLM, and Shopify integration in one production pipeline&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;DevOps and deployment considerations&lt;/h2&gt;
&lt;p&gt;Deploying these systems requires different infrastructure strategies. Traditional ML models are often small enough to run on standard CPUs or even on edge devices. They are easy to containerize using Docker and can be hosted cheaply on platforms like &lt;a href=&quot;https://ansezz.com/blog/coolify-docker-saas-hosting/&quot;&gt;Coolify or AWS&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Generative AI models are a different beast. Even &quot;small&quot; LLMs require significant VRAM, and the &lt;a href=&quot;https://ansezz.com/blog/training-vs-inference/&quot;&gt;cost profile of training vs inference&lt;/a&gt; shapes every hardware decision you make. If you are self-hosting, you need robust GPU orchestration. Many businesses opt for API-based solutions like Claude or OpenAI to avoid the overhead of managing hardware. However, for those concerned with data privacy or high-volume usage, deploying open-weights models on Google Cloud or AWS with specialized inference servers is the standard approach.&lt;/p&gt;
&lt;p&gt;Whichever technology you choose, keep the architecture clean: containerize for environment consistency, and monitor the right failure mode. Track model drift in ML and hallucination rates in GenAI. That rigor is what keeps your AI solutions scalable and maintainable over time.&lt;/p&gt;
&lt;h2&gt;Takeaways&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Define your goal first:&lt;/strong&gt; Use Machine Learning for prediction and classification. Use Generative AI for content creation and reasoning.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Data type matters:&lt;/strong&gt; ML excels with structured, tabular data. GenAI is built for unstructured data like text, images, and code.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cost and latency:&lt;/strong&gt; Traditional ML is generally cheaper and faster to run. GenAI requires significant computational power and has higher latency.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Hybrid is better:&lt;/strong&gt; Combine the precision of ML with the flexibility of GenAI to build robust, modern applications.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Infrastructure:&lt;/strong&gt; Plan your deployment early. Use containerization and cloud-native services to handle the specific hardware requirements of each technology.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Avoid the hype:&lt;/strong&gt; Don&apos;t use an LLM just because it is trending. Choose the model that solves the engineering problem most efficiently.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Is your current data strategy focused on predicting what will happen next, or are you ready to start creating the outcomes you want to see? If you&apos;re wiring either into a real product, &lt;a href=&quot;https://ansezz.com/services/&quot;&gt;here&apos;s how I help teams ship it&lt;/a&gt;.&lt;/p&gt;
</content:encoded><category>ai</category><category>production</category><category>ai-engineering</category><category>machine-learning</category><category>llm</category><category>rag</category><category>architecture</category><category>ai</category></item><item><title>Laravel Octane for high-traffic PHP apps</title><link>https://ansezz.com/blog/laravel-octane-high-traffic/</link><guid isPermaLink="true">https://ansezz.com/blog/laravel-octane-high-traffic/</guid><description>Laravel Octane boots your app once and keeps it warm in memory, feeding requests through a worker pool. How to cut boot overhead and scale to high traffic.</description><pubDate>Fri, 29 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Standard PHP is a bit like a restaurant that fires its entire staff and rebuilds the kitchen from scratch for every single customer order. You walk in, they hire a chef, buy a stove, cook your meal, and then demolish the building as soon as you leave. It&apos;s consistent and safe, but it&apos;s a massive waste of energy when you&apos;re trying to serve thousands of people at once.&lt;/p&gt;
&lt;p&gt;This is the PHP-FPM lifecycle. Every request boots the entire Laravel framework, loads your service providers, parses your config, and instantiates your objects. For low-traffic sites, it&apos;s fine. But when you hit real scale — say, a busy &lt;a href=&quot;https://ansezz.com/blog/laravel-multi-tenancy/&quot;&gt;multi-tenant SaaS&lt;/a&gt; — those milliseconds of &quot;boot time&quot; become a wall you can&apos;t climb without throwing excessive amounts of expensive hardware at the problem. Horizontal scaling buys you room, but it doesn&apos;t fix the underlying latency.&lt;/p&gt;
&lt;p&gt;The solution is to stop rebuilding the kitchen. Laravel Octane changes the game by booting your application once, keeping it in memory, and then feeding requests to it through a high-performance worker pool. It transforms PHP from a &quot;short-lived script&quot; language into a &quot;long-lived process&quot; powerhouse.&lt;/p&gt;
&lt;h2&gt;Why your app feels heavy without Laravel Octane&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/laravel-octane-high-traffic/fpm-vs-octane.webp&quot; alt=&quot;PHP-FPM rebuilding the framework each request versus Octane staying warm&quot; /&gt;&lt;/p&gt;
&lt;p&gt;The overhead of traditional PHP isn&apos;t just about speed; it&apos;s about efficiency. In a standard request, your CPU spends a significant chunk of time just getting the application ready to do work. Once it finally starts execution, it does the database query, renders the view, and then dies.&lt;/p&gt;
&lt;p&gt;If you&apos;re running a complex Laravel monolith with dozens of packages and custom service providers, your boot time might be 30ms to 50ms before a single line of your actual business logic even runs. Under high traffic, this leads to CPU thrashing. You&apos;re paying for the &quot;setup&quot; over and over again.&lt;/p&gt;
&lt;p&gt;Laravel Octane removes this boot cycle. By using high-performance application servers like Swoole or RoadRunner, your app stays resident in memory. The first request boots the framework, and subsequent requests hit a &quot;warm&quot; application. We&apos;re talking about moving from 50ms responses to sub-10ms responses just by changing how the process is managed.&lt;/p&gt;
&lt;h2&gt;Choosing your application server&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/laravel-octane-high-traffic/swoole-vs-roadrunner.webp&quot; alt=&quot;Swoole and RoadRunner compared as Laravel Octane application servers&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Octane runs on top of a high-performance application server. It supports four: FrankenPHP, Swoole, Open Swoole, and RoadRunner. FrankenPHP (a Go-based server built on Caddy) is the server Octane recommends first when &lt;code&gt;octane:install&lt;/code&gt; prompts you, and it&apos;s the easiest path to HTTP/2, HTTP/3, and automatic HTTPS. But the two I reach for most in production are Swoole and RoadRunner, and they solve the &quot;persistent state&quot; problem differently.&lt;/p&gt;
&lt;h3&gt;Swoole&lt;/h3&gt;
&lt;p&gt;Swoole is a C extension for PHP. It&apos;s essentially a high-performance networking engine that allows PHP to handle asynchronous tasks, coroutines, and long-lived connections.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Pros&lt;/strong&gt; — it is incredibly fast. Because it lives as an extension, it has deep access to PHP&apos;s internals. It also unlocks the Octane cache, an in-memory store (backed by Swoole tables) that the docs clock at up to 2 million reads/writes per second, plus concurrent tasks, ticks, and intervals.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cons&lt;/strong&gt; — it can be a bit of a nightmare to install and debug. Because it&apos;s a binary extension, you have to compile it or find the right package for your OS. Xdebug doesn&apos;t always play nice with it, and it can be picky about your environment.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;RoadRunner&lt;/h3&gt;
&lt;p&gt;RoadRunner is written in Go. It acts as a load balancer and process manager that communicates with your PHP workers via a high-speed binary protocol (Goridge).&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Pros&lt;/strong&gt; — no extensions required. It&apos;s a single binary you drop into your project. It&apos;s much easier to set up in a &lt;a href=&quot;https://ansezz.com/blog/coolify-docker-saas-hosting/&quot;&gt;Docker container&lt;/a&gt; and generally feels more &quot;cloud-native.&quot;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cons&lt;/strong&gt; — it&apos;s slightly slower than Swoole because of the communication overhead between the Go binary and the PHP processes, though for 99% of apps, this difference is negligible.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For most developers starting out, I&apos;d pick FrankenPHP at the install prompt and not overthink it — it&apos;s the smoothest setup and ships HTTP/3 for free. RoadRunner is the easy pick if you want a single Go binary with no PHP extension to manage. Reach for Swoole only when you&apos;re chasing every last millisecond or need its concurrency features like async task workers, ticks, and the Octane cache.&lt;/p&gt;
&lt;h2&gt;Tuning your worker pool&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/laravel-octane-high-traffic/worker-pool.webp&quot; alt=&quot;A pool of warm Octane workers handling incoming requests&quot; /&gt;&lt;/p&gt;
&lt;p&gt;The secret sauce of Octane is the &quot;worker pool.&quot; Instead of one process per request, you have a fixed number of workers waiting to handle incoming traffic. If you misconfigure this, you&apos;ll either leave performance on the table or crash your server.&lt;/p&gt;
&lt;p&gt;A general rule of thumb for sizing your worker pool depends on whether your app is CPU-bound or I/O-bound.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;CPU-bound apps&lt;/strong&gt; — if you&apos;re doing heavy data processing or image manipulation, set your worker count to the number of CPU cores you have. Adding more workers will just cause context switching and slow things down.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;I/O-bound apps&lt;/strong&gt; — most web apps spend 90% of their time waiting for a database, Redis, or an external API. In this case, you can scale your workers to 2x or even 4x your core count. This allows one worker to wait for the DB while another handles a new request.&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;# Starting Octane with 16 workers for an I/O-heavy app
php artisan octane:start --server=swoole --workers=16
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Don&apos;t forget about &lt;strong&gt;task workers&lt;/strong&gt; if you&apos;re using Swoole or Open Swoole. Sized separately with the &lt;code&gt;--task-workers&lt;/code&gt; flag, these run in their own processes and are perfect for offloading slow work like sending emails or fanning out concurrent queries without blocking the main request cycle.&lt;/p&gt;
&lt;h2&gt;The danger of persistent state&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/laravel-octane-high-traffic/memory-leaks.webp&quot; alt=&quot;Hunting down a memory leak in a long-lived Octane worker&quot; /&gt;&lt;/p&gt;
&lt;p&gt;The biggest hurdle when moving to Octane is the shift in mindset. In traditional PHP, &quot;leaky&quot; code doesn&apos;t matter much because the process dies after 100ms. In Octane, a memory leak is a ticking time bomb.&lt;/p&gt;
&lt;p&gt;If you have a static array in a service provider that you append to on every request, that array will grow until your server runs out of RAM. You have to be extremely careful with:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Static properties&lt;/strong&gt; — avoid using them for request-specific data.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Singletons&lt;/strong&gt; — if you register a singleton in your app container, it stays alive. If that singleton caches data, you need to make sure that data is cleared or managed properly between requests.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Global state&lt;/strong&gt; — avoid &lt;code&gt;global&lt;/code&gt; variables at all costs (which you should be doing anyway).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Laravel helps you by &quot;resetting&quot; some core services between requests, but it can&apos;t catch everything. If you&apos;re migrating an old codebase from &lt;a href=&quot;https://ansezz.com/blog/monolith-to-microservices/&quot;&gt;monolith to microservices&lt;/a&gt;, you&apos;ll want to audit your service providers for any long-lived state.&lt;/p&gt;
&lt;h3&gt;Protecting yourself with max-requests&lt;/h3&gt;
&lt;p&gt;The best insurance policy against memory leaks is the &lt;code&gt;--max-requests&lt;/code&gt; flag. This tells Octane to gracefully restart a worker after it has handled a certain number of requests. Octane already does this every 500 requests by default; tune the number to match how fast your workers accumulate memory.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# Restart workers every 1000 requests to prevent memory bloat
php artisan octane:start --max-requests=1000
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This keeps your memory usage predictable while still giving you the performance benefits of a warm application.&lt;/p&gt;
&lt;h2&gt;Real-world optimization tips&lt;/h2&gt;
&lt;p&gt;Once you have Octane running, there are a few technical levers you can pull to squeeze out even more performance.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Database connections&lt;/strong&gt; — in Octane your workers stay alive, so each one holds its own DB connection open between requests instead of reconnecting every time. That&apos;s a win, but watch the math: more workers means more open connections, so check your &lt;code&gt;database.php&lt;/code&gt; config and make sure the worker count doesn&apos;t blow past the max connection limit on your DB server. This matters even more once you add &lt;a href=&quot;https://ansezz.com/blog/replication-vs-backup-laravel/&quot;&gt;read replicas&lt;/a&gt; to the mix.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Octane cache&lt;/strong&gt; — if you&apos;re using Swoole, reach for &lt;code&gt;Cache::store(&apos;octane&apos;)&lt;/code&gt;. It&apos;s an in-memory Swoole table that&apos;s blistering fast and shared across every worker on the box. Use it for frequently accessed configuration or small datasets that don&apos;t change often. Just remember it&apos;s wiped when the server restarts.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Bytecode caching&lt;/strong&gt; — make sure OPcache is enabled and tuned. Set &lt;code&gt;opcache.validate_timestamps=0&lt;/code&gt; in production since your code won&apos;t be changing while the server is running.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Graceful reloads&lt;/strong&gt; — when you deploy new code, use &lt;code&gt;php artisan octane:reload&lt;/code&gt;. This will gracefully restart the workers without dropping current connections. It&apos;s essential for zero-downtime deployments.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Takeaways for the high-traffic dev&lt;/h2&gt;
&lt;p&gt;Transitioning to Octane isn&apos;t just about installing a package; it&apos;s about maturing your infrastructure.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Identify the bottleneck&lt;/strong&gt; — only use Octane if your boot time is the problem. If your database queries take 2 seconds, Octane won&apos;t help you.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Test for leaks&lt;/strong&gt; — watch how your app behaves under sustained load and profile its memory growth over time.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Monitor workers&lt;/strong&gt; — keep an eye on your CPU and RAM usage to find the &quot;sweet spot&quot; for your worker count.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Leverage concurrency&lt;/strong&gt; — on Swoole, use &lt;code&gt;Octane::concurrently()&lt;/code&gt; to run independent operations in parallel via task workers and return their results together, cutting total response time for complex pages.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Octane takes PHP to a level where it can compete with Node.js and Go for high-concurrency applications while keeping the developer experience of Laravel intact. If you&apos;re building a SaaS that expects a lot of noise, this is your jet engine.&lt;/p&gt;
&lt;p&gt;Are you running Octane in production yet, or is the fear of memory leaks keeping you on PHP-FPM? Drop a note via &lt;a href=&quot;https://ansezz.com/contact/&quot;&gt;contact&lt;/a&gt; — let&apos;s talk worker counts. 🤘&lt;/p&gt;
</content:encoded><category>laravel</category><category>laravel</category><category>performance</category><category>scaling</category></item><item><title>Shopify UCP quick-start: make your store agent-ready</title><link>https://ansezz.com/blog/shopify-ucp-quick-start/</link><guid isPermaLink="true">https://ansezz.com/blog/shopify-ucp-quick-start/</guid><description>AI agents are the new buyers, and they can&apos;t see your store without a UCP manifest. A quick-start to making your Shopify store agent-ready.</description><pubDate>Thu, 28 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Your store is effectively invisible to the biggest spenders of 2026. I&apos;m not talking about Gen Z or Alpha. I&apos;m talking about AI agents.&lt;/p&gt;
&lt;p&gt;If Google&apos;s AI Mode or the Gemini app can&apos;t find a machine-readable map of your Shopify store, it won&apos;t recommend your products. It definitely won&apos;t buy from you.&lt;/p&gt;
&lt;p&gt;Traditional SEO was built for humans with eyeballs. The Universal Commerce Protocol (UCP) is built for agents with wallets.&lt;/p&gt;
&lt;p&gt;The rollout is moving fast. Until you expose that manifest, you&apos;re opting out of the agentic economy.&lt;/p&gt;
&lt;p&gt;Here is how to fix that.&lt;/p&gt;
&lt;h2&gt;Why your legacy SEO is failing&lt;/h2&gt;
&lt;p&gt;For ten years, we&apos;ve obsessed over meta tags and JSON-LD schema. That was enough when Google was just a list of links. But we&apos;ve entered the era of &lt;a href=&quot;https://ansezz.com/blog/agentic-commerce-shopify/&quot;&gt;agentic commerce on Shopify&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Agents like Gemini don&apos;t &quot;browse&quot; your collections. They don&apos;t look at your hero banners. They want to know three things:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;What do you sell?&lt;/li&gt;
&lt;li&gt;Can I buy it right now?&lt;/li&gt;
&lt;li&gt;Which API do I call to checkout?&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;If your site doesn&apos;t answer these questions in a standardized way, the agent moves to a competitor who does. Legacy schema tells an agent what a product &lt;em&gt;is&lt;/em&gt;. UCP tells an agent how to &lt;em&gt;transact&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/shopify-ucp-quick-start/seo-vs-ucp.webp&quot; alt=&quot;Side-by-side comparison of human-centric SEO versus agent-centric UCP&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;What is the Universal Commerce Protocol?&lt;/h2&gt;
&lt;p&gt;UCP is an open standard co-developed by Shopify and Google, with backing from a wide cast of retailers and payment providers. It creates a &quot;handshake&quot; between a merchant and an AI agent.&lt;/p&gt;
&lt;p&gt;The core of this handshake is the &lt;code&gt;/.well-known/ucp&lt;/code&gt; endpoint.&lt;/p&gt;
&lt;p&gt;It&apos;s a simple JSON file that acts as a discovery manifest. When an agent hits your domain, it looks for this file to understand your capabilities. It&apos;s the &lt;code&gt;robots.txt&lt;/code&gt; of the AI era, but for money.&lt;/p&gt;
&lt;p&gt;Conceptually, the manifest declares which commerce services and capabilities your store exposes and where an agent should call them. The exact schema is still evolving — check the &lt;a href=&quot;https://ucp.dev/&quot;&gt;official UCP spec&lt;/a&gt; for the current shape — but it looks roughly like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
  &quot;ucp&quot;: {
    &quot;version&quot;: &quot;2026-04-08&quot;,
    &quot;services&quot;: {
      &quot;dev.ucp.shopping&quot;: [&quot;https://api.yourstore.com/ucp/shopping&quot;]
    },
    &quot;capabilities&quot;: {
      &quot;dev.ucp.shopping.checkout&quot;: [&quot;create_session&quot;, &quot;complete_session&quot;]
    },
    &quot;payment_handlers&quot;: {
      &quot;com.shopify.shop_pay&quot;: [&quot;https://api.yourstore.com/ucp/pay&quot;]
    }
  },
  &quot;signing_keys&quot;: [{ &quot;kid&quot;: &quot;store_2026&quot;, &quot;kty&quot;: &quot;EC&quot;, &quot;crv&quot;: &quot;P-256&quot; }]
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This manifest tells the agent exactly which capabilities you support and where to send the request. No scraping required. No &quot;guessing&quot; where the add-to-cart button is.&lt;/p&gt;
&lt;h2&gt;How to enable UCP on Shopify&lt;/h2&gt;
&lt;p&gt;If you are running a standard Shopify or Shopify Plus store, the good news is you don&apos;t have to write this JSON manually. Shopify is baking this directly into the core.&lt;/p&gt;
&lt;p&gt;Shopify auto-enabled Agentic Storefronts for eligible stores, so this is opt-out rather than opt-in — but you should verify it.&lt;/p&gt;
&lt;p&gt;To check or change it, open the &lt;strong&gt;Agentic&lt;/strong&gt; sales channel in your Shopify admin (&lt;strong&gt;Sales channels &amp;gt; Agentic&lt;/strong&gt;). By default, &lt;strong&gt;Allow Shopify to manage for me&lt;/strong&gt; is on, which keeps your products in Shopify Catalog and enrolls you in new channels automatically. Turn that off if you want to control &lt;strong&gt;Direct checkout&lt;/strong&gt; and catalog access per channel.&lt;/p&gt;
&lt;p&gt;With it enabled, Shopify generates and hosts the manifest under your store&apos;s &lt;code&gt;/.well-known/ucp&lt;/code&gt; path.&lt;/p&gt;
&lt;p&gt;If you are running a headless setup with Hydrogen or a custom frontend, you&apos;ll need to ensure your middleware correctly proxies this path back to Shopify&apos;s servers. We see a lot of developers break their agentic discovery because their Vercel or Netlify rewrites aren&apos;t handling the &lt;code&gt;.well-known&lt;/code&gt; directory.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/shopify-ucp-quick-start/architecture.webp&quot; alt=&quot;Architecture diagram of an agent reaching the UCP manifest through middleware&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Why this matters now&lt;/h2&gt;
&lt;p&gt;Search is shifting from links to agents. As Google&apos;s &quot;AI Mode&quot; reaches more shoppers and Gemini acts as a default shopping assistant, it will prioritize stores that support the UCP handshake.&lt;/p&gt;
&lt;p&gt;This lands alongside Shopify&apos;s rollout of &lt;a href=&quot;https://ansezz.com/blog/mcp-context-aware-agents/&quot;&gt;MCP context-aware agents&lt;/a&gt;. These agents use the Model Context Protocol to link your store&apos;s live data directly into the LLM&apos;s reasoning loop.&lt;/p&gt;
&lt;p&gt;If your manifest is missing, your store is a black box. The agent will see your products in the search index, but it won&apos;t be able to fulfill the request &quot;buy me the best hiking boots from a local store.&quot; It will choose the store where it can verify the checkout capability instantly.&lt;/p&gt;
&lt;h2&gt;Auditing your implementation&lt;/h2&gt;
&lt;p&gt;Don&apos;t just flip a switch and hope. You need to verify that your manifest is actually reachable by external bots.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Open a private browser window.&lt;/li&gt;
&lt;li&gt;Go to &lt;code&gt;https://yourdomain.com/.well-known/ucp&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;You should see a raw JSON object.&lt;/li&gt;
&lt;li&gt;If you see a 404 or a redirect to your homepage, your theme or app is blocking the path.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Check your &lt;code&gt;robots.txt&lt;/code&gt; file as well. Ensure you aren&apos;t accidentally disallowing agents from crawling the &lt;code&gt;.well-known&lt;/code&gt; directory.&lt;/p&gt;
&lt;p&gt;If you are using a custom Laravel backend to power your Shopify store&apos;s logic, make sure your routes file includes a specific entry for this endpoint. At ansezz, we often see custom middleware stripping out these hidden directories for &quot;security&quot; reasons. In 2026, that security is just costing you sales.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/shopify-ucp-quick-start/admin-toggle.webp&quot; alt=&quot;Shopify admin showing the Agentic storefronts toggle enabled&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Technical takeaways&lt;/h2&gt;
&lt;p&gt;Here is your agent-readiness checklist:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Enable &lt;strong&gt;Agentic storefronts&lt;/strong&gt; in your Shopify admin.&lt;/li&gt;
&lt;li&gt;Verify the &lt;code&gt;/.well-known/ucp&lt;/code&gt; path returns valid JSON.&lt;/li&gt;
&lt;li&gt;Confirm your payment handlers (Shop Pay, Shopify Payments) are declared in the manifest.&lt;/li&gt;
&lt;li&gt;Test your site with an agentic browser or a UCP validator tool.&lt;/li&gt;
&lt;li&gt;Check your &lt;a href=&quot;https://ansezz.com/blog/coolify-docker-saas-hosting/&quot;&gt;Coolify Docker SaaS hosting&lt;/a&gt; configs if you&apos;re hosting custom middleware, to ensure the path isn&apos;t being dropped by your reverse proxy.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The transition from a human-centric web to agent-centric commerce is happening whether we&apos;re ready or not. UCP is the first real step toward making your store a first-class citizen in the AI economy.&lt;/p&gt;
&lt;p&gt;Are you letting agents shop your store, or are you still building only for a world that clicks? If you want help getting your storefront agent-ready, &lt;a href=&quot;https://ansezz.com/services/&quot;&gt;here&apos;s how I work with teams&lt;/a&gt;.&lt;/p&gt;
</content:encoded><category>shopify</category><category>shopify</category><category>agentic-commerce</category><category>ai</category><category>hydrogen</category><category>agentic-ai</category></item><item><title>Claude MCP: connecting my dev tools to LLMs</title><link>https://ansezz.com/blog/claude-mcp-dev-tools/</link><guid isPermaLink="true">https://ansezz.com/blog/claude-mcp-dev-tools/</guid><description>The Model Context Protocol is a USB-C port for LLMs: one MCP server, any host, no MxN integration tax. The architecture, the servers I run, and why it is safe.</description><pubDate>Wed, 27 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Every time I have to build a custom integration for a new tool, a little piece of my developer soul dies. It is a maintenance nightmare that never ends. We have reached a point where building the actual product is often faster than setting up the pipes to make it work with our data — which is exactly the problem Claude&apos;s MCP (Model Context Protocol) was built to kill.&lt;/p&gt;
&lt;p&gt;If you have spent any time building &lt;a href=&quot;https://ansezz.com/blog/agentic-workflows-vibe-coding/&quot;&gt;agentic workflows and vibe coding&lt;/a&gt;, you know exactly what I am talking about. You have an LLM like Claude that is incredibly smart but essentially locked in a room with no windows. To give it context, you have to manually copy-paste code, export CSV files, or spend three days writing a brittle wrapper for a third-party API just so your assistant can &quot;see&quot; your work.&lt;/p&gt;
&lt;p&gt;This fragmentation is the biggest bottleneck in modern software development. We have powerful models, but they are isolated from our local files, our databases, and our production logs. It is like having a world-class architect who isn&apos;t allowed to visit the construction site. They are just guessing based on the photos you decide to send them.&lt;/p&gt;
&lt;p&gt;Enter the Model Context Protocol — or, in Anthropic&apos;s own framing, a USB-C port for AI applications.&lt;/p&gt;
&lt;h2&gt;The fragmentation tax is killing your productivity&lt;/h2&gt;
&lt;p&gt;The problem is simple but massive. Every AI application — whether it is Claude Desktop, a custom agent, or an IDE extension — wants to talk to your data. On the other side, every data source — your GitHub repos, your Postgres databases, your Slack channels — has its own specific API and authentication flow.&lt;/p&gt;
&lt;p&gt;Without a standard, we are stuck in an M×N problem. If you have 5 AI apps and 10 data sources, you need 50 different integrations. (If you are weighing whether to expose a plain REST endpoint or an MCP server here, I broke that down in &lt;a href=&quot;https://ansezz.com/blog/api-vs-mcp/&quot;&gt;API vs MCP&lt;/a&gt;.) This is why most &quot;AI-powered&quot; tools feel shallow. They only support a few basic integrations, and if you want to use your internal company data, you are back to writing custom glue code.&lt;/p&gt;
&lt;p&gt;The cost is real. We waste hours rebuilding the same connectors, every new integration is another potential leak, and the &quot;magic&quot; of AI evaporates the moment we hit a data silo.&lt;/p&gt;
&lt;h2&gt;The solution: MCP as a universal standard&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/claude-mcp-dev-tools/architecture.webp&quot; alt=&quot;MCP sitting between data sources and AI hosts as a universal translator&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Claude MCP (Model Context Protocol) is the first serious attempt to standardize how AI applications discover and interact with data and tools. Instead of building a specific connector for every model and every tool, you build an MCP server.&lt;/p&gt;
&lt;p&gt;This server acts as a translator. It sits between your data and the AI, exposing a consistent interface that any MCP-compliant host (like Claude Desktop) can understand. It is exactly like the USB standard. It doesn&apos;t matter if you are plugging in a mouse, a keyboard, or an external drive. The protocol is the same, so it just works.&lt;/p&gt;
&lt;p&gt;This shifts the entire paradigm of &lt;a href=&quot;https://ansezz.com/blog/mcp-context-aware-agents/&quot;&gt;context-aware agents&lt;/a&gt;. Instead of hard-coding logic into the agent, you simply &quot;plug in&quot; the servers you need.&lt;/p&gt;
&lt;h3&gt;How the architecture actually works&lt;/h3&gt;
&lt;p&gt;There are three main players in the MCP ecosystem:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;The host&lt;/strong&gt; — this is the environment the user interacts with. It could be Claude Desktop, a terminal, or an IDE like Cursor. The host is responsible for managing the lifecycle of the connection.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The client&lt;/strong&gt; — this is the part of the host that speaks the protocol. It does the &quot;handshake&quot; with the server to find out what it can do.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The server&lt;/strong&gt; — this is a lightweight program that provides context (resources), actions (tools), and prompt templates.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;For example, if I want Claude to have access to my local project files, I run a local MCP server that exposes those files as &quot;resources.&quot; The host (Claude Desktop) asks the server: &quot;what do you have?&quot; The server replies: &quot;I have these 10 files and a tool to run grep searches.&quot;&lt;/p&gt;
&lt;p&gt;The model can then decide to call the &quot;grep&quot; tool whenever it needs to find a specific function definition. I didn&apos;t have to write a single line of logic inside Claude to make that happen. I just connected the server.&lt;/p&gt;
&lt;h2&gt;Modularity and the MCP server ecosystem&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/claude-mcp-dev-tools/servers.webp&quot; alt=&quot;Grid of plug-in MCP servers for Postgres, GitHub, and Google Drive feeding context to an LLM&quot; /&gt;&lt;/p&gt;
&lt;p&gt;The beauty of this modularity is that once a server is built, anyone can use it. The community has already started building servers for everything you can imagine. I have been using a few in my daily workflow that have completely changed how I code:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;A Postgres server&lt;/strong&gt; — I point Claude at a local or remote database so it can inspect schemas and run read-only queries to help me debug data issues without leaving the chat. Anthropic&apos;s original reference Postgres server is now archived, so I lean on a maintained community build.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;GitHub&apos;s official MCP server&lt;/strong&gt; — this lets the model search my repositories, list issues, and open pull requests. It is like having a junior dev who actually knows where the code is. GitHub now ships and hosts this itself, with a remote option you authenticate via OAuth.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;A Google Drive server&lt;/strong&gt; — handy when I need to cross-reference technical docs stored in Drive with the actual implementation in my IDE. The reference server is archived too, so I treat it as a starting point rather than something to trust blindly.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Wiring these up is the step that separates chatting with a model from delegating to one — the point where a workflow crosses into &lt;a href=&quot;https://ansezz.com/blog/ai-coding-workflow-levels/&quot;&gt;the agentic levels&lt;/a&gt; instead of staying a faster autocomplete.&lt;/p&gt;
&lt;p&gt;This also solves a massive pain point in &lt;a href=&quot;https://ansezz.com/blog/agentic-commerce-shopify/&quot;&gt;agentic commerce for Shopify&lt;/a&gt;. Imagine an agent that can talk directly to your Shopify store via MCP to check inventory levels or update product descriptions in real-time, all while maintaining a secure, standardized connection.&lt;/p&gt;
&lt;h2&gt;Security first: the sandbox model&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/claude-mcp-dev-tools/security.webp&quot; alt=&quot;Sandboxed MCP server communicating through a narrow pipe to the host&quot; /&gt;&lt;/p&gt;
&lt;p&gt;The biggest question I get when I talk about connecting dev tools to an LLM is: &quot;is it safe?&quot;&lt;/p&gt;
&lt;p&gt;Security is a first-class concern in MCP&apos;s design. The server runs as a separate process that only exposes the specific tools and resources you grant it. (Note: a local stdio server still runs with your normal user privileges — it&apos;s process isolation, not a hardened sandbox — so only run servers you trust.)&lt;/p&gt;
&lt;p&gt;For local servers, the protocol typically uses &lt;code&gt;stdio&lt;/code&gt; (stdin/stdout). This means the server can only talk to the host through a very narrow pipe. It doesn&apos;t have open network ports listening for connections. It only exists as long as the host is running it.&lt;/p&gt;
&lt;p&gt;For remote servers, MCP standardizes on OAuth 2.1 (with PKCE) for authorization. This allows for fine-grained, scope-based permissions. You can authorize a GitHub MCP server to only read public repositories, or a database server to only access specific tables.&lt;/p&gt;
&lt;p&gt;This is a huge improvement over the &quot;give me your master API key&quot; approach that we have seen in the past. We can now treat AI tools with the same &quot;least privilege&quot; mindset we use for any other service in our stack. This is especially important when you are trying to avoid &lt;a href=&quot;https://ansezz.com/blog/7-rag-mistakes-production/&quot;&gt;RAG mistakes in production&lt;/a&gt;, where data leakage is a top-tier risk.&lt;/p&gt;
&lt;h2&gt;Why I am betting on MCP&lt;/h2&gt;
&lt;p&gt;I have been a developer for over a decade, and I have seen plenty of &quot;standards&quot; come and go. What makes MCP different is its simplicity and its backers. Anthropic has made this open source because they realize that the more context a model has, the more valuable it becomes.&lt;/p&gt;
&lt;p&gt;We are moving toward a world of &quot;agentic&quot; software development. In this world, we don&apos;t just use AI to write snippets of code. We use AI as an orchestrator that can reach into our cloud infrastructure on GCP, check our Docker logs, and suggest fixes for a failing Laravel app.&lt;/p&gt;
&lt;p&gt;Without a protocol like MCP, that vision is impossible to scale. It would be too expensive and too risky to build. But with MCP, we are building a world where tools are plug-and-play.&lt;/p&gt;
&lt;h3&gt;Practical takeaways for senior engineers&lt;/h3&gt;
&lt;p&gt;If you are ready to start experimenting with this, here is what I recommend:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Install the Claude Desktop app&lt;/strong&gt; — it is one of the most mature MCP hosts, and a low-friction place to start.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Try the filesystem server&lt;/strong&gt; — this is the easiest way to feel the power. Give Claude access to a specific folder and watch it navigate your codebase.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Don&apos;t build, search first&lt;/strong&gt; — browse the official MCP Registry before writing anything. The reference repo now keeps only a handful of maintained servers (filesystem, fetch, git, memory, and a few others), and most of the well-known integrations are either archived or maintained by the vendor directly — like GitHub&apos;s own server.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Think in tools, not just prompts&lt;/strong&gt; — start thinking about what &quot;tools&quot; your internal systems could expose. If you have a custom admin panel, could it be an MCP server?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Connecting your dev tools to an LLM isn&apos;t just about speed. It is about reducing the cognitive load of switching between tabs, terminals, and documentation. It allows you to stay in the &quot;flow&quot; longer.&lt;/p&gt;
&lt;p&gt;Are you ready to stop copy-pasting your code into a chat box and start connecting your tools directly to the brain? What is the one internal tool you wish you could &quot;plug in&quot; to Claude right now? Drop a note via &lt;a href=&quot;https://ansezz.com/contact/&quot;&gt;contact&lt;/a&gt; — let&apos;s figure it out. 🤘&lt;/p&gt;
</content:encoded><category>ai</category><category>mcp</category><category>claude</category><category>agentic-ai</category><category>llm</category></item><item><title>Caching for speed: Redis and semantic layers in RAG</title><link>https://ansezz.com/blog/redis-semantic-caching-rag/</link><guid isPermaLink="true">https://ansezz.com/blog/redis-semantic-caching-rag/</guid><description>Stop paying for the same LLM call twice. Two-tier caching with Redis keys and RedisVL semantic lookups slashes RAG latency and trims your LLM API bill.</description><pubDate>Tue, 26 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;You finally shipped your RAG pipeline. It works. The retrieval is accurate. The LLM is snappy. But then you look at your cloud bill and your P99 latency. Every single query — even &quot;what are your shipping times?&quot; asked for the tenth time — triggers a full chain of embedding, vector search, and an expensive LLM call.&lt;/p&gt;
&lt;p&gt;At scale, this is a disaster. You are essentially paying for the same computation over and over again. Your users are waiting two seconds for answers that should take twenty milliseconds. Your &quot;denial of wallet&quot; risk is through the roof.&lt;/p&gt;
&lt;p&gt;The solution isn&apos;t a bigger model or a faster vector DB. It&apos;s a smarter cache. I&apos;m talking about semantic caching with Redis. It collapses a multi-second chain into a single-digit-millisecond lookup and, at the cache hit rates a busy FAQ bot reaches, can cut your LLM API spend by roughly half.&lt;/p&gt;
&lt;p&gt;Here is how I build these systems to handle production traffic.&lt;/p&gt;
&lt;h2&gt;The two-tier cache architecture&lt;/h2&gt;
&lt;p&gt;Standard caching relies on exact matches. If a user asks &quot;How do I reset my password?&quot; and another asks &quot;how do i reset my password&quot;, they might hit the same key if you normalize the string. But if the second user asks &quot;Can you help me change my password?&quot;, a traditional cache fails.&lt;/p&gt;
&lt;p&gt;In a modern RAG stack, I use a two-tier approach.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Exact cache&lt;/strong&gt; — a simple key-value store in Redis. I normalize the query (lowercase, trim, strip punctuation) and hash it. It&apos;s your first line of defense. It costs almost nothing and has zero false positives.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Semantic cache&lt;/strong&gt; — if the exact cache misses, I embed the query and look for &quot;near enough&quot; matches in a Redis vector index. If I find a previous question close enough (roughly 0.9+ cosine similarity, i.e. a cosine distance of 0.1 or less), I serve that cached response instead of hitting the LLM.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This architecture ensures that you never do the heavy lifting twice for the same intent.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/redis-semantic-caching-rag/architecture.webp&quot; alt=&quot;Two-tier cache architecture — exact match, semantic match, LLM fallback&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Why Redis is the king of semantic caching&lt;/h2&gt;
&lt;p&gt;Most developers think of Redis as just a key-value store. But with the &lt;a href=&quot;https://redis.io/blog/how-to-cache-semantic-search/&quot;&gt;Redis Vector Library (RedisVL)&lt;/a&gt;, it becomes a high-performance vector database.&lt;/p&gt;
&lt;p&gt;Why use Redis for this instead of your main vector DB like Pinecone or Weaviate?&lt;/p&gt;
&lt;p&gt;Latency.&lt;/p&gt;
&lt;p&gt;Your main vector DB is likely optimized for searching through millions of document chunks. Your semantic cache is much smaller — it only stores recent queries and answers. By co-locating this cache in Redis, which likely already sits in your application tier, you reduce network hops.&lt;/p&gt;
&lt;p&gt;I typically see vector lookups in Redis finish in under 5ms. Compare that to an embedding API call that takes 100ms and an LLM generation that takes 1500ms. The math is simple.&lt;/p&gt;
&lt;h2&gt;Implementing the semantic layer&lt;/h2&gt;
&lt;p&gt;The trick to a good semantic cache is the similarity threshold. Too low, and you give users wrong answers (the &quot;semantic trap&quot;). Too high, and you never get a cache hit.&lt;/p&gt;
&lt;p&gt;I usually start with a distance threshold of 0.1 for cosine distance, which translates to roughly 90 percent similarity. You can implement this quickly using the RedisVL extensions.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from redisvl.extensions.cache.llm import SemanticCache

# Initialize the cache with a conservative threshold
llm_cache = SemanticCache(
    name=&quot;production_rag_cache&quot;,
    redis_url=&quot;redis://localhost:6379&quot;,
    distance_threshold=0.1,
)

# Check for a hit
query = &quot;how do i update my billing info?&quot;
hit = llm_cache.check(prompt=query)

if hit:
    return hit[0][&quot;response&quot;]

# If miss, run full RAG and then store
# response = run_rag_pipeline(query)
# llm_cache.store(prompt=query, response=response)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This simple wrapper handles the embedding of the incoming query, the vector search in Redis, and the logic for returning the most relevant cached response.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/redis-semantic-caching-rag/code.webp&quot; alt=&quot;Python semantic-cache snippet on a developer workstation&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Avoid the semantic trap: context and versioning&lt;/h2&gt;
&lt;p&gt;Semantic caching is powerful but dangerous if you aren&apos;t careful. If your underlying data changes, your cache might still be serving old, incorrect information.&lt;/p&gt;
&lt;p&gt;I always include a &lt;code&gt;context_version&lt;/code&gt; in my cache keys or metadata. If I re-index my product catalog or update my documentation, I bump the version. The cache immediately starts missing for old entries, forcing a refresh with the new data.&lt;/p&gt;
&lt;p&gt;Another trap is tenant isolation. If User A asks &quot;what is my balance?&quot;, you absolutely cannot serve that cached response to User B. I solve this by partitioning the cache:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Use namespaces&lt;/strong&gt; — &lt;code&gt;cache:tenant_id:query_hash&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Include metadata&lt;/strong&gt; — add &lt;code&gt;tenant_id&lt;/code&gt; to the vector index filters.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This ensures that semantic matches only happen within the correct security boundary. For more on building secure, multi-tenant systems, check out my thoughts on &lt;a href=&quot;https://ansezz.com/blog/laravel-multi-tenancy/&quot;&gt;Laravel multi-tenancy&lt;/a&gt; which shares similar isolation principles.&lt;/p&gt;
&lt;h2&gt;Managing TTL and staleness&lt;/h2&gt;
&lt;p&gt;In a standard cache, you just set an expiry of 3600 seconds and forget it. With a semantic cache, I prefer a tiered TTL strategy.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Exact matches&lt;/strong&gt; — 1 hour TTL. If the user asks the exact same thing, they probably want the exact same answer.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Semantic matches&lt;/strong&gt; — 4 hour TTL. These are more expensive to generate, so we want to keep them longer, but we also include a &quot;last validated&quot; timestamp.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Proactive invalidation&lt;/strong&gt; — if my Shopify store updates a product price, I trigger a Redis worker to purge all cache entries related to that product ID.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This hybrid approach keeps the system responsive without serving stale data. I&apos;ve written about similar &lt;a href=&quot;https://ansezz.com/blog/event-driven-pubsub/&quot;&gt;event-driven patterns here&lt;/a&gt; if you want to dive deeper into how to handle these updates at scale.&lt;/p&gt;
&lt;h2&gt;Measuring success: precision and recall&lt;/h2&gt;
&lt;p&gt;Don&apos;t just turn on the cache and walk away. You need to monitor two specific metrics:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Cache hit rate&lt;/strong&gt; — what percentage of queries are being handled by Redis? I aim for 30–50 percent for general FAQ-style bots.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Semantic precision&lt;/strong&gt; — are the cached answers actually correct?&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I log every semantic hit along with its similarity score. Once a week, I sample hits with scores between 0.85 and 0.92 and manually review them. If I see too many &quot;near misses&quot; that are actually different questions, I tighten the threshold.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/redis-semantic-caching-rag/dashboard.webp&quot; alt=&quot;Cache analytics dashboard — hit rate, latency, LLM cost, precision&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Final takeaways for senior engineers&lt;/h2&gt;
&lt;p&gt;Implementing Redis as a semantic layer isn&apos;t just about speed. It&apos;s about making your AI systems sustainable. If you are serious about moving from a prototype to a production-ready SaaS, caching is not optional.&lt;/p&gt;
&lt;p&gt;Here is your checklist for next week:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Install &lt;code&gt;redisvl&lt;/code&gt; and set up a basic vector index in your dev environment.&lt;/li&gt;
&lt;li&gt;Implement a two-tier lookup (exact then semantic).&lt;/li&gt;
&lt;li&gt;Set your distance threshold conservatively (start at 0.05 or 0.1).&lt;/li&gt;
&lt;li&gt;Add a &lt;code&gt;tenant_id&lt;/code&gt; or &lt;code&gt;context_version&lt;/code&gt; to your metadata to avoid cross-talk.&lt;/li&gt;
&lt;li&gt;Monitor your hit rate and watch your API bill drop.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For more technical deep dives into modern architecture, look at &lt;a href=&quot;https://ansezz.com/blog/7-rag-mistakes-production/&quot;&gt;7 RAG mistakes in production&lt;/a&gt; to see what else might be slowing you down, or browse the rest of the &lt;a href=&quot;https://ansezz.com/blog/category/architecture/&quot;&gt;architecture archive&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;What is the one query in your system that keeps hitting your LLM unnecessarily? Drop a note via &lt;a href=&quot;https://ansezz.com/contact/&quot;&gt;contact&lt;/a&gt; — let&apos;s figure out if a semantic cache would have caught it. 🤘&lt;/p&gt;
</content:encoded><category>architecture</category><category>redis</category><category>ai</category><category>rag</category><category>vector-search</category><category>performance</category><category>infrastructure</category></item><item><title>Smart auto-scaling for modern AI apps</title><link>https://ansezz.com/blog/smart-auto-scaling-ai/</link><guid isPermaLink="true">https://ansezz.com/blog/smart-auto-scaling-ai/</guid><description>CPU auto-scaling fails GPU workloads. Why queue depth, KV-cache pressure, and TTFT beat CPU as triggers, plus the KEDA patterns to scale AI in time.</description><pubDate>Mon, 25 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Your AI application is lagging, users are complaining, but your cloud dashboard says everything is fine. Your CPU usage is hovering at a comfortable 20 percent while your inference requests are timing out.&lt;/p&gt;
&lt;p&gt;This is the classic scaling trap for AI engineers. Traditional auto-scaling is built for web servers where CPU and memory are the primary bottlenecks. In the world of large language models and vector databases, those metrics are practically useless.&lt;/p&gt;
&lt;p&gt;If you wait for your CPU to hit 80 percent before spinning up a new pod, your service will be dead in the water long before the second instance even starts its boot sequence. GPU-bound workloads require a completely different playbook.&lt;/p&gt;
&lt;p&gt;To build a resilient, cost-effective AI SaaS, you need to move beyond reactive hardware metrics. You need to scale on intent, queue pressure, and the specific physics of GPU memory.&lt;/p&gt;
&lt;h2&gt;Why CPU-based auto-scaling lies to you&lt;/h2&gt;
&lt;p&gt;Most horizontal pod autoscalers (HPA) are configured to watch CPU utilization by default. For a Laravel or Node.js API, this works great. The work is linear — more requests equal more CPU cycles.&lt;/p&gt;
&lt;p&gt;AI models are different. The CPU handles the &quot;boring&quot; stuff like tokenization, request routing, and managing HTTP headers. The heavy lifting happens on the GPU.&lt;/p&gt;
&lt;p&gt;I have seen production clusters where the GPU is pinned at 100 percent while the CPU sits idle. Kubernetes sees the low CPU usage and thinks the pod is healthy. It might even try to pack &lt;em&gt;more&lt;/em&gt; pods onto that node, leading to a catastrophic failure.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/smart-auto-scaling-ai/cpu-lie.webp&quot; alt=&quot;CPU usage tells you nothing about GPU saturation&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;GPU utilization vs occupancy: the hardware layer&lt;/h2&gt;
&lt;p&gt;When you finally switch to monitoring GPUs, you encounter two confusing metrics: utilization and occupancy.&lt;/p&gt;
&lt;p&gt;GPU utilization is essentially a duty cycle. It tells you the percentage of time the GPU was active over a sample period. It is a lagging indicator. By the time it hits 90 percent, your request queue has likely been building for 30 seconds.&lt;/p&gt;
&lt;p&gt;Occupancy is more granular. It measures how many &quot;warps&quot; or hardware slots are filled within the streaming multiprocessors (SM). You can have high utilization but low occupancy if your batch size is too small.&lt;/p&gt;
&lt;p&gt;For scaling, utilization is the baseline, but it isn&apos;t the truth. You need to look at what is happening before the request even hits the silicon.&lt;/p&gt;
&lt;h2&gt;Queue depth: your best leading indicator&lt;/h2&gt;
&lt;p&gt;If you want to stop fires before they start, monitor your queue depth. In vLLM or SGLang, this is the number of requests waiting for a slot in the inference engine.&lt;/p&gt;
&lt;p&gt;Queue depth is a direct predictor of latency. If you know your model can handle 16 concurrent requests before P99 latency starts to climb, set your scaling trigger at 12.&lt;/p&gt;
&lt;p&gt;Scaling on queue depth lets you provision capacity while the current hardware is still performing within SLO. It gives you that 60-second head start you need to pull a fresh container and load a 20GB model weights file into memory.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/smart-auto-scaling-ai/queue-depth.webp&quot; alt=&quot;Queue depth predicts latency before users feel it&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Token velocity and the KV cache&lt;/h2&gt;
&lt;p&gt;In generative AI, not all requests are created equal. A 10-token summary request is light. A 4,000-token RAG retrieval analysis is a heavyweight.&lt;/p&gt;
&lt;p&gt;This is where token velocity and KV-cache usage come in. The KV cache is the GPU memory that stores attention keys and values for in-flight sequences. If your KV cache is nearly full, the engine has to preempt a request to free blocks. In current vLLM (V1), the default recovery is recompute — the victim&apos;s cache is discarded and its prompt plus generated tokens are reprocessed from scratch when it resumes. Older vLLM (V0) swapped blocks out to host memory over PCIe instead. Either way you pay for it.&lt;/p&gt;
&lt;p&gt;Latency will skyrocket. Your P99 will look like a mountain range.&lt;/p&gt;
&lt;p&gt;I recommend scaling based on a combination of:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Token velocity&lt;/strong&gt; — total tokens per second across all active instances.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;KV-cache pressure&lt;/strong&gt; — the percentage of available cache blocks currently occupied.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;When the cache is full, it doesn&apos;t matter how low your GPU utilization is. You cannot fit more work onto that chip. You must scale.&lt;/p&gt;
&lt;h2&gt;Predictive scaling with ARIMA&lt;/h2&gt;
&lt;p&gt;Reactive scaling is always playing catch-up. Even with fast boot times, there is a delay. For enterprise apps with predictable traffic patterns, I use ARIMA (Auto-Regressive Integrated Moving Average) models to forecast load.&lt;/p&gt;
&lt;p&gt;If I know traffic historically spikes at 9:00 am every Monday, I don&apos;t wait for the queue to grow. I use a time-series forecast to spin up the &quot;base load&quot; pods at 8:55 am.&lt;/p&gt;
&lt;p&gt;This turns your infrastructure into a proactive system rather than a reactive one. You pay for what you use, but you ensure the capacity is there before the first user clicks &quot;Generate.&quot;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/smart-auto-scaling-ai/predictive.webp&quot; alt=&quot;ARIMA forecast lifting capacity before the 9am spike&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Practical steps for your stack&lt;/h2&gt;
&lt;p&gt;Implementing this doesn&apos;t have to be a nightmare. Here is how I structure it:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Use KEDA&lt;/strong&gt; — the Kubernetes Event-Driven Autoscaler is the gold standard. It lets you scale based on Prometheus metrics like queue depth or P99 latency instead of just CPU.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Set TTFT SLOs&lt;/strong&gt; — measure time-to-first-token (TTFT). This is the most critical metric for user perception. If TTFT P99 exceeds 500ms, you need more replicas.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Blur the lines&lt;/strong&gt; — don&apos;t rely on a single metric. Create a composite score of GPU utilization, queue depth, and cache pressure. Once you have multiple replicas, route across them with &lt;a href=&quot;https://ansezz.com/blog/gpu-aware-load-balancing/&quot;&gt;GPU-aware load balancing&lt;/a&gt; so traffic lands on the least-saturated GPU.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Fix your RAG&lt;/strong&gt; — sometimes the scaling issue is actually a retrieval issue. If your vector search is slow, the inference engine waits longer, hogging the GPU. Check out these &lt;a href=&quot;https://ansezz.com/blog/7-rag-mistakes-production/&quot;&gt;common RAG mistakes&lt;/a&gt; to ensure your bottleneck isn&apos;t upstream.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Optimize the frontend&lt;/strong&gt; — for Shopify apps or custom SaaS, ensure your &lt;a href=&quot;https://ansezz.com/blog/agentic-workflows-vibe-coding/&quot;&gt;agentic workflows&lt;/a&gt; handle retries gracefully when the infrastructure is scaling up.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Scaling AI isn&apos;t about having the biggest GPUs. It is about having the smartest triggers. By moving to service-level metrics, you save money on idle compute and save your users from the dreaded &quot;thinking...&quot; spinner.&lt;/p&gt;
&lt;p&gt;Are you still scaling on CPU, or have you made the jump to queue-based triggers yet? Drop a note via &lt;a href=&quot;https://ansezz.com/contact/&quot;&gt;contact&lt;/a&gt; — I love this conversation. 🤘&lt;/p&gt;
</content:encoded><category>architecture</category><category>scaling</category><category>ai</category><category>llm</category><category>kubernetes</category><category>llm-inference</category><category>infrastructure</category></item><item><title>GPU-aware load balancing for AI inference</title><link>https://ansezz.com/blog/gpu-aware-load-balancing/</link><guid isPermaLink="true">https://ansezz.com/blog/gpu-aware-load-balancing/</guid><description>Round-robin breaks when LLM requests span 50 to 50,000 tokens. GPU-aware load balancing with prefill/decode disaggregation and the metrics that cut P99.</description><pubDate>Sun, 24 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;You just scaled your RAG application to a hundred concurrent users. Suddenly, your latency spikes. Some users get their answers in two seconds, while others are staring at a loading spinner for thirty. You check your load balancer and it says everything is fine. CPU is at 40%. RAM is stable. But your GPUs are screaming, and your P99 latency is in the gutter. This is the problem GPU-aware load balancing exists to solve.&lt;/p&gt;
&lt;p&gt;The problem is that you are treating your AI models like traditional web servers. Sending a 4,000-token prompt to the same GPU that is currently generating a 50-token summary is a recipe for disaster. Round-robin routing is a relic of the past when it comes to LLM inference. If you don&apos;t account for the unique way GPUs handle compute and memory, you aren&apos;t just wasting money. You are killing your user experience.&lt;/p&gt;
&lt;p&gt;The solution isn&apos;t just &quot;more GPUs.&quot; It is building a load balancer that actually understands what is happening inside the model. We need to talk about GPU-aware routing, prefill vs decode disaggregation, and why your KV cache is the most valuable asset in your stack.&lt;/p&gt;
&lt;h2&gt;Why round-robin is a trap for LLMs&lt;/h2&gt;
&lt;p&gt;In traditional software development, a request is a request. Whether it&apos;s a &lt;code&gt;GET /users&lt;/code&gt; or a &lt;code&gt;POST /orders&lt;/code&gt;, the variance in resource consumption is usually predictable and small. Standard load balancers like Nginx or HAProxy work great here. They look at basic health checks and send traffic to the next available worker.&lt;/p&gt;
&lt;p&gt;AI is different. A single request to an LLM has a massive variance in &quot;weight.&quot; One user might ask &quot;what is 2+2?&quot; while another uploads a 50-page PDF and asks for a deep analysis. If your load balancer sends both to the same GPU, the heavy request will hog the compute resources, forcing the light request to wait in a queue.&lt;/p&gt;
&lt;p&gt;This is why CPU-based metrics are useless. A GPU can be at 100% utilization while performing very different types of work. Some work is compute-bound, meaning it needs raw processing power. Other work is memory-bound, meaning it is limited by how fast data can move in and out of VRAM. To solve this, we have to look deeper into the inference lifecycle.&lt;/p&gt;
&lt;h2&gt;Prefill vs decode: the performance gap&lt;/h2&gt;
&lt;p&gt;LLM inference happens in two distinct phases. Understanding the difference between them is the &quot;aha!&quot; moment for GPU load balancing.&lt;/p&gt;
&lt;p&gt;The first phase is &lt;strong&gt;prefill&lt;/strong&gt;. This is when the model reads your entire prompt and processes all the tokens at once. It is a heavy, compute-intensive task that builds something called the &lt;strong&gt;KV cache&lt;/strong&gt; (key-value cache). Prefill loves big batches and high-performance tensor cores. It is where the &quot;heavy lifting&quot; happens.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/gpu-aware-load-balancing/prefill-vs-decode.webp&quot; alt=&quot;Diagram comparing the compute-bound prefill phase and memory-bound decode phase of LLM inference&quot; /&gt;&lt;/p&gt;
&lt;p&gt;The second phase is &lt;strong&gt;decode&lt;/strong&gt;. This is where the model generates the response one token at a time. Each new token only needs to look at the previously generated tokens and the KV cache. This phase is surprisingly light on compute but incredibly heavy on memory bandwidth. It is slow and long-lived.&lt;/p&gt;
&lt;p&gt;When you mix these two on the same GPU without a smart scheduler, the &quot;prefill&quot; of a new request will often pause the &quot;decode&quot; of existing requests. This causes the jittery, stuttering text generation that users hate. By using GPU-aware load balancing, we can prioritize these phases differently across our fleet.&lt;/p&gt;
&lt;h2&gt;Metrics for the real world&lt;/h2&gt;
&lt;p&gt;To build a better router, you need to stop looking at CPU and start looking at these four metrics:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Token queue depth&lt;/strong&gt; — how many tokens are waiting to be processed? This is a much more accurate representation of &quot;load&quot; than simple request counts.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;KV cache utilization&lt;/strong&gt; — GPUs have a limited amount of VRAM. The KV cache stores the &quot;memory&quot; of ongoing conversations. If a GPU&apos;s VRAM is 90% full of KV cache, it literally cannot accept a large new prompt, even if it&apos;s currently &quot;idle.&quot;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Time to first token (TTFT)&lt;/strong&gt; — this measures the latency of the prefill phase. If your TTFT is climbing, your prefill pool is congested.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Inter-token latency (ITL)&lt;/strong&gt; — this measures the speed of the decode phase. If this is high, your GPUs are likely memory-bandwidth constrained.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I often recommend using tools like &lt;a href=&quot;https://github.com/vllm-project/vllm&quot;&gt;vLLM&lt;/a&gt; because they expose these metrics out of the box. You can pipe these into a custom gateway that makes routing decisions based on real-time VRAM availability rather than just &quot;is the server up?&quot;&lt;/p&gt;
&lt;h2&gt;Prefix-aware routing: reuse the KV cache&lt;/h2&gt;
&lt;p&gt;Here is a secret — the most expensive part of a RAG request is often re-processing the same system prompt or long context over and over again. If you send five consecutive questions about the same document to five different GPUs, each GPU has to perform the &quot;prefill&quot; phase for that document from scratch.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/gpu-aware-load-balancing/prefix-routing.webp&quot; alt=&quot;Prefix-aware routing sending prompts with a shared prefix to the GPU holding a warm KV cache&quot; /&gt;&lt;/p&gt;
&lt;p&gt;This is where &lt;strong&gt;prefix-aware routing&lt;/strong&gt; (also called KV-cache-aware routing) comes in. Instead of routing randomly, your load balancer tokenizes the start of the prompt and looks for a GPU that already has that specific content in its KV cache.&lt;/p&gt;
&lt;p&gt;By matching the prefix of a prompt to a GPU that already holds those tokens, you skip the prefill work for the shared portion and reuse the cached keys and values directly. That can turn hundreds of milliseconds of TTFT into a near-instant first token, and it is one of the highest-leverage optimizations in a production RAG system. I&apos;ve written before about &lt;a href=&quot;https://ansezz.com/blog/7-rag-mistakes-production/&quot;&gt;common RAG mistakes&lt;/a&gt;, and ignoring cache locality is definitely one of them. If you want the layer above this — caching whole responses and embeddings — see &lt;a href=&quot;https://ansezz.com/blog/redis-semantic-caching-rag/&quot;&gt;Redis and semantic caching for RAG&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Splitting the fleet into specialized pools&lt;/h2&gt;
&lt;p&gt;As you scale, you should stop treating every GPU as a generalist. A senior move is to create &lt;strong&gt;disaggregated inference fleets&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;I like to split my GPUs into two pools:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;The prefill pool&lt;/strong&gt; — compute-heavy GPUs (like H100s or B200s) optimized for chewing through large prompts fast. These nodes process the initial context, then &quot;hand off&quot; the KV state.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The decode pool&lt;/strong&gt; — GPUs you size for memory bandwidth and capacity rather than raw FLOPs (think A100s, or cheaper L40S cards) that focus on churning out tokens for existing requests.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This separation lets you scale based on your specific workload. If your users are uploading huge documents but only asking for short summaries, you scale your prefill pool. If they are having long, chatty conversations, you scale your decode pool.&lt;/p&gt;
&lt;p&gt;This is the same logic we use in &lt;a href=&quot;https://ansezz.com/blog/coolify-docker-saas-hosting/&quot;&gt;modern DevOps with Coolify&lt;/a&gt;. You wouldn&apos;t put your heavy database on the same tiny instance as your frontend — why would you mix your heavy prefill work with your light decode work?&lt;/p&gt;
&lt;h2&gt;Implementing your first GPU-aware router&lt;/h2&gt;
&lt;p&gt;You don&apos;t need to build a custom engine from scratch to start doing this. Here is the practical path I follow when setting this up for a new SaaS:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Centralize your metrics&lt;/strong&gt; — use Prometheus to scrape vLLM or TGI metrics from every GPU node.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Use a smart gateway&lt;/strong&gt; — implement a middleware in Go or Rust (or even a heavy-duty Lua script in OpenResty) that queries these metrics before choosing a target.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Prioritize KV cache&lt;/strong&gt; — route on prefix overlap when you can. A cheap first approximation is session stickiness: if a node served this &lt;code&gt;conversation_id&lt;/code&gt; recently and isn&apos;t at 100% KV utilization, send the follow-up there so the warm cache gets reused.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Set hard limits&lt;/strong&gt; — if a GPU reaches 85% VRAM usage, take it out of the rotation for new prompts until some sessions finish.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/gpu-aware-load-balancing/metrics-dashboard.webp&quot; alt=&quot;GPU inference dashboard tracking time to first token, inter-token latency, and KV cache utilization&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Managing AI compute is about moving from &quot;black box&quot; infrastructure to &quot;context-aware&quot; infrastructure. When your load balancer knows the difference between a 10-token greeting and a 10,000-token context window, your costs go down and your users stay happy.&lt;/p&gt;
&lt;p&gt;It&apos;s easy to get lost in the hype of &quot;agentic systems&quot; and &lt;a href=&quot;https://ansezz.com/blog/mcp-context-aware-agents/&quot;&gt;context-aware agents&lt;/a&gt;, but none of that matters if your underlying infrastructure is buckling under the weight of unoptimized routing.&lt;/p&gt;
&lt;p&gt;If you are still using round-robin for your AI models, what is the biggest bottleneck you are seeing in your P99 latency right now? Drop a note via &lt;a href=&quot;https://ansezz.com/contact/&quot;&gt;contact&lt;/a&gt; — I love this conversation. 🤘&lt;/p&gt;
</content:encoded><category>architecture</category><category>llm-inference</category><category>networking</category><category>ai</category><category>llm</category><category>infrastructure</category></item><item><title>Circuit breakers: stopping vector DB failures</title><link>https://ansezz.com/blog/circuit-breakers-vector-db/</link><guid isPermaLink="true">https://ansezz.com/blog/circuit-breakers-vector-db/</guid><description>A slow vector DB kills SaaS faster than a dead one. The circuit-breaker pattern for AI infra — states, fallback tiers, and Laravel-friendly wiring.</description><pubDate>Sat, 23 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;You built a beautiful RAG pipeline. It works perfectly on your machine with a few hundred vectors. Then you launch. Traffic spikes. Suddenly your managed vector database starts sweating. A single similarity search that used to take 50ms is now taking 5 seconds. Your web workers are all tied up waiting for responses that aren&apos;t coming. The database isn&apos;t technically down — but it is slow enough to kill your entire application. Your users see spinning loaders until the request finally times out. This is a classic cascading failure — the kind a circuit breaker exists to stop — and it is the fastest way to drain your &quot;innovation budget&quot; and your users&apos; patience.&lt;/p&gt;
&lt;p&gt;The problem is that we often treat external APIs and databases as if they are always healthy. We write code that assumes the vector DB will return results. When it doesn&apos;t, we wait. And while we wait, we hold onto memory and CPU cycles. The solution is an old-school electrical engineering concept applied to software: the circuit breaker.&lt;/p&gt;
&lt;p&gt;This guide shows you how to wrap your AI infrastructure in protective logic so a slow dependency doesn&apos;t take your whole SaaS down with it.&lt;/p&gt;
&lt;h2&gt;How the circuit breaker pattern works&lt;/h2&gt;
&lt;p&gt;The circuit breaker pattern is a state machine that sits between your application code and your external service. The idea comes straight out of Michael Nygard&apos;s &lt;em&gt;Release It!&lt;/em&gt; — it monitors every call you make and has three states that dictate how it handles traffic.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/circuit-breakers-vector-db/states.webp&quot; alt=&quot;State machine diagram of the circuit breaker: closed (healthy), open (fail-fast), and half-open (recovery probe) with the transitions between them&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Closed — the healthy state.&lt;/strong&gt;
In the closed state, the circuit is complete. Requests flow through to your vector database or LLM provider normally. The breaker is silently watching. It keeps a count of how many requests failed or took too long. As long as the failure rate stays below your threshold, it stays closed. This is the &quot;everything is fine&quot; mode.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Open — the fail-fast state.&lt;/strong&gt;
Once the failure threshold is hit — let&apos;s say 50% of requests failed in the last 30 seconds — the breaker &quot;trips&quot; and moves to the open state. Now, every time your application tries to call the vector DB, the breaker immediately throws an error or returns a fallback response without even attempting the network call. This gives your database room to breathe and recover. It also ensures your application doesn&apos;t waste time waiting on a service that is clearly struggling.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Half-open — the recovery test.&lt;/strong&gt;
After a cooldown period, the breaker moves to the half-open state. It allows a small number of &quot;test&quot; requests to pass through. If these test calls succeed, the breaker assumes the service is healthy again and moves back to the closed state. If they fail, it immediately goes back to open for another cooldown cycle. This is a controlled way to probe the system before fully re-engaging.&lt;/p&gt;
&lt;h2&gt;Why your RAG pipeline needs this&lt;/h2&gt;
&lt;p&gt;RAG pipelines are particularly vulnerable because they usually involve multiple high-latency network hops. You have to embed the query, search the vector DB, and then call the LLM. If any of these pieces fail or slow down, the whole experience breaks.&lt;/p&gt;
&lt;p&gt;Most developers make the mistake of only handling hard errors like a &lt;code&gt;404&lt;/code&gt; or a &lt;code&gt;500&lt;/code&gt; status code. But in production, &quot;slow&quot; is often more dangerous than &quot;down.&quot; A slow vector DB creates a bottleneck that backs up your entire request queue. By the time you realize there is a problem, your server is out of memory because it is holding open thousands of connections.&lt;/p&gt;
&lt;p&gt;If you have read my previous post on &lt;a href=&quot;https://ansezz.com/blog/7-rag-mistakes-production/&quot;&gt;7 RAG mistakes in production&lt;/a&gt;, you know that reliability is the difference between a demo and a product. The circuit breaker is your insurance policy against these types of outages.&lt;/p&gt;
&lt;h2&gt;Implementing fallback strategies&lt;/h2&gt;
&lt;p&gt;Tripping the breaker shouldn&apos;t always mean showing an error message to the user. The best AI systems use fallbacks to maintain a level of service even when parts of the stack are failing.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/circuit-breakers-vector-db/fallback-flow.webp&quot; alt=&quot;Fallback flow diagram: a tripped circuit routing from the vector DB to keyword search, a Redis cache, and an LLM-only response&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Hot and cold tiers.&lt;/strong&gt;
You can think of your vector DB as your &quot;hot&quot; knowledge tier. If it fails, you should have a &quot;cold&quot; fallback. Maybe you fall back to a standard keyword search in your primary Postgres or MySQL database. The results might not be as contextually relevant as a vector search, but a &quot;decent&quot; answer is always better than a &quot;timed out&quot; error.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Cached responses.&lt;/strong&gt;
Another strong strategy is &lt;a href=&quot;https://ansezz.com/blog/redis-semantic-caching-rag/&quot;&gt;semantic caching with Redis&lt;/a&gt;. If the circuit is open, you can check the cache for similar queries that were answered recently. Even if you can&apos;t generate a fresh answer, you might be able to serve a cached one. This keeps the user moving while your backend recovers.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;LLM-only mode.&lt;/strong&gt;
If your retrieval step is what&apos;s failing, you can still send the user&apos;s prompt to the LLM with a note that external knowledge is currently unavailable. The LLM can then answer based on its general training data. It is a degraded experience, but it is still functional. Transparency here is key — tell the user that the &quot;live&quot; data isn&apos;t available so they know to verify the response.&lt;/p&gt;
&lt;h2&gt;Building it in Laravel&lt;/h2&gt;
&lt;p&gt;Since I spend a lot of time in the Laravel ecosystem, I lean on patterns that make this easy to implement. You don&apos;t need to write the state machine from scratch. A community package like &lt;code&gt;ackintosh/ganesha&lt;/code&gt; (a PHP circuit breaker), or a small custom wrapper around the &lt;code&gt;illuminate/http&lt;/code&gt; client with a Redis-backed failure counter, gets the job done.&lt;/p&gt;
&lt;p&gt;The goal is to wrap your API calls in a block that understands these states. Here is a simplified look at how that logic looks in practice.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/circuit-breakers-vector-db/code.webp&quot; alt=&quot;PHP code wrapping a vector DB client in a circuit breaker, catching CircuitBreakerOpenException to return fallback data&quot; /&gt;&lt;/p&gt;
&lt;p&gt;When you call your vector DB client, you wrap it in the breaker. If the call fails multiple times, the breaker trips. In the &lt;code&gt;catch&lt;/code&gt; block, you handle the &lt;code&gt;CircuitBreakerOpenException&lt;/code&gt; by returning your fallback data. This keeps your controllers clean and your architecture robust.&lt;/p&gt;
&lt;p&gt;You can also integrate this with your &lt;a href=&quot;https://ansezz.com/blog/coolify-docker-saas-hosting/&quot;&gt;SaaS hosting on Coolify&lt;/a&gt; to ensure that your containers don&apos;t get killed by health checks just because an external API is slow. The breaker prevents the resource bloat that usually triggers those health-check failures.&lt;/p&gt;
&lt;h2&gt;Live telemetry and smart routing&lt;/h2&gt;
&lt;p&gt;Senior engineers don&apos;t just set a circuit breaker and walk away. They monitor it. You need live telemetry to see how often your circuits are tripping. Tools like Prometheus or even simple logs piped to a dashboard can tell you a lot.&lt;/p&gt;
&lt;p&gt;If you see that your primary vector DB in &lt;code&gt;us-east-1&lt;/code&gt; is constantly tripping but your secondary in &lt;code&gt;eu-west-1&lt;/code&gt; is healthy, you can implement smart routing. Your circuit breaker can act as a signal to your &lt;a href=&quot;https://ansezz.com/blog/gpu-aware-load-balancing/&quot;&gt;load balancer&lt;/a&gt; or internal router to shift traffic to the healthy region.&lt;/p&gt;
&lt;p&gt;This kind of &lt;a href=&quot;https://ansezz.com/blog/event-driven-pubsub/&quot;&gt;event-driven architecture&lt;/a&gt; makes your system self-healing. It doesn&apos;t wait for a human to wake up at 3am to fix a database. It detects the failure, trips the breaker, uses the fallback, and tries to recover automatically.&lt;/p&gt;
&lt;h2&gt;Practical steps to get started&lt;/h2&gt;
&lt;p&gt;If you are ready to harden your AI infrastructure, start here:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Identify your weakest links&lt;/strong&gt; — list every external call in your RAG pipeline. Usually it is the embedding API and the vector DB.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Define your thresholds&lt;/strong&gt; — how many slow requests are you willing to tolerate? Start with a 50% failure rate over 30 seconds and a 2-second timeout.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Choose your fallbacks&lt;/strong&gt; — decide what happens when the breaker is open. Do you show an error, use a cache, or switch to keyword search?&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Wrap your clients&lt;/strong&gt; — use a library to wrap your HTTP or database calls. Don&apos;t try to build the state machine logic yourself unless you have a very specific use case.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Monitor the trips&lt;/strong&gt; — set up an alert when a circuit stays open for more than a few minutes. This usually indicates a major provider outage that needs your attention.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The goal is to fail gracefully. Every system has issues, but the ones that survive are the ones that don&apos;t let a small fire in a dependency burn down the whole house.&lt;/p&gt;
&lt;p&gt;Have you ever had a slow dependency take down your entire application, or are you still relying on long timeouts and luck? Drop a note via &lt;a href=&quot;https://ansezz.com/contact/&quot;&gt;contact&lt;/a&gt; — I love this conversation. 🤘&lt;/p&gt;
</content:encoded><category>architecture</category><category>observability</category><category>ai</category><category>rag</category><category>vector-search</category><category>laravel</category><category>infrastructure</category></item><item><title>Message queues for heavy-duty document processing</title><link>https://ansezz.com/blog/message-queues-document-processing/</link><guid isPermaLink="true">https://ansezz.com/blog/message-queues-document-processing/</guid><description>Stop running embeddings in the request cycle. Build a document pipeline on message queues with staged workers, retries, dead-letter queues, and autoscaling.</description><pubDate>Fri, 22 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;If you are running your document embeddings inside your request-response cycle instead of behind a message queue, you are playing with fire. I have seen too many junior devs build a beautiful RAG application that falls over the second a user uploads a 50MB PDF. The browser spins, the proxy timeout hits, and the database locks up while your worker tries to chunk 500 pages of legal jargon in real time.&lt;/p&gt;
&lt;p&gt;This is the classic &quot;heavy lifting&quot; problem in AI engineering. Document processing — OCR, text extraction, semantic chunking, and embedding — is slow, unpredictable, and resource-heavy. Trying to force it into a synchronous web request is a recipe for a bad user experience and a fragile system.&lt;/p&gt;
&lt;p&gt;The solution is decoupling. I&apos;m talking about message queues. In this guide, I&apos;ll walk you through why async work belongs in a queue and how to build a production-grade ingestion pipeline that doesn&apos;t melt your server. If you want the broker-level foundations first, I cover them in &lt;a href=&quot;https://ansezz.com/blog/scaling-with-rabbitmq/&quot;&gt;scaling with RabbitMQ&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;The synchronous trap&lt;/h2&gt;
&lt;p&gt;Imagine a user uploads a document to your SaaS. Your code receives the file, sends it to an extraction API, waits for the response, loops through the text to create chunks, sends each chunk to an embedding model, and finally saves it to pgvector.&lt;/p&gt;
&lt;p&gt;If the whole chain runs long, you hit a timeout somewhere — a proxy gateway timeout (Nginx defaults to 60s), a PHP &lt;code&gt;max_execution_time&lt;/code&gt;, or a platform request cap — and the connection drops. If the embedding API has a momentary blip, the whole process fails, and the user has to start over. Worse, while your server is busy doing this heavy work, it&apos;s not responding to other users.&lt;/p&gt;
&lt;p&gt;This is where we apply the first rule of senior engineering: if it takes more than 100ms, consider making it async. By moving this work to a message queue, you give your users immediate feedback (&quot;we&apos;re processing your file!&quot;) while the heavy lifting happens safely in the background.&lt;/p&gt;
&lt;h2&gt;The anatomy of a document pipeline&lt;/h2&gt;
&lt;p&gt;A robust RAG pipeline isn&apos;t just one big function. It&apos;s a series of decoupled stages. I like to break it down into modular steps, each triggered by a message in a queue. This lets you scale different parts of the system independently.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/message-queues-document-processing/pipeline-stages.webp&quot; alt=&quot;Pipeline stages — ingestion, parsing, chunking, embedding&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Here is how I usually structure it:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Ingestion &amp;amp; discovery&lt;/strong&gt; — a user uploads a file. You save it to S3 and push a small message to the queue containing the &lt;code&gt;file_path&lt;/code&gt; and &lt;code&gt;tenant_id&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Parsing &amp;amp; normalization&lt;/strong&gt; — a worker picks up the message, downloads the file, and runs it through a parser like pdfplumber or an OCR service. It emits the raw text to the next queue.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Chunking&lt;/strong&gt; — this worker takes the text and splits it into semantic sections. Doing this in its own stage means you can easily swap chunking strategies (e.g., recursive character vs semantic) without re-running the heavy parsing step.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Embedding &amp;amp; indexing&lt;/strong&gt; — the final stage batches the chunks, hits your embedding API (like OpenAI or a local model), and pushes the vectors into your vector DB.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This stage-based approach is exactly what I discuss in my post on &lt;a href=&quot;https://ansezz.com/blog/7-rag-mistakes-production/&quot;&gt;7 RAG mistakes to avoid in production&lt;/a&gt;. It provides backpressure control — if your vector DB slows down, the &quot;index&quot; queue grows, but the &quot;parsing&quot; workers keep humming along.&lt;/p&gt;
&lt;h2&gt;Retries and the beauty of dead letters&lt;/h2&gt;
&lt;p&gt;In the real world, things break. APIs time out. PDFs are malformed. Workers crash.&lt;/p&gt;
&lt;p&gt;A message queue like Redis (with BullMQ or Laravel Queues) or SQS gives you retries with almost no code — though you do have to opt in. BullMQ defaults to a single attempt, so set &lt;code&gt;attempts&lt;/code&gt; and a &lt;code&gt;backoff&lt;/code&gt; strategy explicitly; Laravel queues take &lt;code&gt;--tries&lt;/code&gt; and &lt;code&gt;backoff&lt;/code&gt;; SQS retries up to &lt;code&gt;maxReceiveCount&lt;/code&gt;. When a worker fails, the message goes back onto the queue to be tried again after a delay. Exponential backoff is your best friend here — don&apos;t hammer a failing API every 5 seconds. Wait 10, then 60, then 300.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/message-queues-document-processing/dlq-retries.webp&quot; alt=&quot;Retry strategy and dead-letter quarantine flow&quot; /&gt;&lt;/p&gt;
&lt;p&gt;But what happens when a document simply &lt;em&gt;cannot&lt;/em&gt; be processed? Maybe it&apos;s a password-protected PDF or a corrupted file. You don&apos;t want it retrying forever and clogging up your workers.&lt;/p&gt;
&lt;p&gt;This is where a &lt;strong&gt;dead-letter queue (DLQ)&lt;/strong&gt; comes in. After a certain number of failed attempts, the message is moved to the DLQ — a &quot;quarantine&quot; zone. SQS gives you this natively via the queue&apos;s redrive policy; with BullMQ or Laravel you route exhausted jobs there yourself (listen for the &lt;code&gt;failed&lt;/code&gt; event in BullMQ, or use &lt;code&gt;failed_jobs&lt;/code&gt; plus a handler in Laravel). I can then inspect these failed jobs, fix the underlying issue, and re-queue them. It&apos;s a safety net that keeps your main production line moving.&lt;/p&gt;
&lt;h2&gt;Batching for efficiency&lt;/h2&gt;
&lt;p&gt;If you are processing 10,000 chunks, you do not want to make 10,000 individual API calls to your embedding provider. That&apos;s slow and expensive.&lt;/p&gt;
&lt;p&gt;Most embedding APIs and vector databases perform much better with batches. A good worker pattern involves pulling multiple messages from the queue (or aggregating them in memory) and sending them as a single bulk request.&lt;/p&gt;
&lt;p&gt;In a Laravel environment, I often use job batching to track the progress of a large document. I can see exactly when 95% of a PDF is processed and update a progress bar for the user. If you&apos;re interested in how this fits into a larger architecture, check out my thoughts on &lt;a href=&quot;https://ansezz.com/blog/event-driven-pubsub/&quot;&gt;event-driven pub/sub systems&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Event-driven prefetching&lt;/h2&gt;
&lt;p&gt;Here is a &quot;senior&quot; tip — queues aren&apos;t just for ingestion. You can use them for &lt;strong&gt;prefetching&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;If a user is chatting with an AI agent and the conversation is heading toward a specific topic, you can fire off a background job to fetch related documents and warm up the cache before the user even asks the next question. This makes your AI feel lightning fast because the context is already &quot;ready&quot; when the retrieval step hits.&lt;/p&gt;
&lt;p&gt;By using an event bus, you can decouple the chat interface from these optimization tasks. The chat app just emits a &lt;code&gt;user_asked_question&lt;/code&gt; event, and a background worker decides whether it should pre-fetch more data or update the semantic cache.&lt;/p&gt;
&lt;h2&gt;Monitoring your message queue&lt;/h2&gt;
&lt;p&gt;Once you move to a queue-based system, your most important metric is no longer just &quot;request latency.&quot; You need to watch your &lt;strong&gt;queue depth&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/message-queues-document-processing/monitoring.webp&quot; alt=&quot;Queue-depth dashboard with worker autoscaler&quot; /&gt;&lt;/p&gt;
&lt;p&gt;If the queue depth is growing faster than your workers can clear it, you have a bottleneck. This is where tools like Docker and Coolify make life easy — I can spin up five more worker containers to handle a sudden surge in document uploads. You can read more about how I manage this infra in my &lt;a href=&quot;https://ansezz.com/blog/coolify-docker-saas-hosting/&quot;&gt;Coolify and Docker guide&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Practical takeaways for your pipeline&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Never store large files in the queue&lt;/strong&gt; — only pass references (like an S3 key). Keep messages small for better performance.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Make tasks idempotent&lt;/strong&gt; — assume a message might be processed twice. Use &lt;code&gt;upsert&lt;/code&gt; instead of &lt;code&gt;insert&lt;/code&gt; in your vector DB to avoid duplicates.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Use structured logging&lt;/strong&gt; — every worker log should include the &lt;code&gt;doc_id&lt;/code&gt; and &lt;code&gt;tenant_id&lt;/code&gt;. Searching for &quot;why did this file fail?&quot; is impossible without it.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Scale on queue depth&lt;/strong&gt; — set up your autoscaler to add workers based on how many messages are waiting, not just CPU usage.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Separate worker pools&lt;/strong&gt; — have one set of workers for &quot;fast&quot; tasks (like metadata updates) and another for &quot;slow&quot; tasks (like OCR/embedding). Don&apos;t let a huge PDF upload block a simple name change.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Building a document pipeline is about respecting the time it takes to process data. Move that work into a queue and you get a system that is resilient, scalable, and smooth for your users — instead of one that buckles on the first big upload.&lt;/p&gt;
&lt;p&gt;How are you currently handling long-running AI tasks? Are you still fighting with request timeouts, or have you embraced the queue? Drop a note via &lt;a href=&quot;https://ansezz.com/contact/&quot;&gt;contact&lt;/a&gt; — I love this conversation. 🤘&lt;/p&gt;
</content:encoded><category>architecture</category><category>messaging</category><category>ai</category><category>rag</category><category>redis</category><category>laravel</category><category>infrastructure</category></item><item><title>Rate limiting: protecting your AI wallet</title><link>https://ansezz.com/blog/rate-limiting-ai-wallet/</link><guid isPermaLink="true">https://ansezz.com/blog/rate-limiting-ai-wallet/</guid><description>One runaway agent loop can mean a $5,000 LLM bill. Why request-per-second limits lie, and how hierarchical token-bucket limits protect your margins.</description><pubDate>Thu, 21 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;One runaway agent loop is all it takes to wake up to a $5,000 OpenAI bill.&lt;/p&gt;
&lt;p&gt;If you&apos;re building AI-powered SaaS or RAG systems, your biggest threat isn&apos;t a server crash. It&apos;s a &quot;denial of wallet&quot; attack. A buggy client, a malicious user, or even your own experimental agent can spam your API endpoints and burn through your tokens (and credits) in minutes.&lt;/p&gt;
&lt;p&gt;Traditional web apps care about requests per second to keep the CPU from melting. In the world of LLMs, we care about tokens per minute to keep the bank account from draining. Standard rate limiting isn&apos;t enough anymore. You need an architecture that understands cost, context, and the &quot;noisy neighbor&quot; problem before a single prompt even hits your vector DB.&lt;/p&gt;
&lt;h2&gt;Why requests per second (QPS) is a lie for AI&lt;/h2&gt;
&lt;p&gt;In a standard Laravel or Node app, a request is a request. Sure, some take longer than others, but they generally consume similar resources. In AI engineering, one request might be a 50-token greeting, while another is a 128,000-token context dump for a RAG pipeline.&lt;/p&gt;
&lt;p&gt;If you only limit requests per second, a single user can stay within their &quot;10 requests per minute&quot; limit while still costing you 100× more than everyone else combined. This is where the &lt;a href=&quot;https://ansezz.com/blog/laravel-multi-tenancy/&quot;&gt;noisy neighbor problem&lt;/a&gt; becomes a financial crisis.&lt;/p&gt;
&lt;p&gt;You aren&apos;t just protecting your infrastructure. You&apos;re protecting your margins. To do this effectively, we have to move from counting &quot;pings&quot; to counting &quot;value.&quot;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/rate-limiting-ai-wallet/dashboard.webp&quot; alt=&quot;Token usage dashboard showing skewed cost per user&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;The anatomy of a denial of wallet (DoW) attack&lt;/h2&gt;
&lt;p&gt;A denial of wallet attack is the AI equivalent of a DDoS. The goal isn&apos;t necessarily to take your site down. It&apos;s to exhaust your API quotas or financial budget until your service stops functioning — or you&apos;re forced to pay a massive bill.&lt;/p&gt;
&lt;p&gt;I&apos;ve seen this happen in three ways:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;The agentic loop&lt;/strong&gt; — an autonomous agent gets stuck in a logic loop, calling your tool-use functions repeatedly without a &quot;max steps&quot; ceiling.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The scrapers&lt;/strong&gt; — malicious bots trying to exfiltrate your entire knowledge base by querying every possible permutation of your RAG system.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The dev mistake&lt;/strong&gt; — a frontend developer accidentally puts an LLM-powered &quot;autocomplete&quot; on a search bar that triggers on every keystroke.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Without token-aware rate limiting, your provider (like OpenAI or Anthropic) will eventually hit you with a &lt;code&gt;429&lt;/code&gt; error. But by that time, the damage to your wallet is already done.&lt;/p&gt;
&lt;h2&gt;Solving the noisy neighbor with hierarchical rate limiting&lt;/h2&gt;
&lt;p&gt;To solve this, I implement a three-layer rate limiting strategy at the &lt;a href=&quot;https://ansezz.com/blog/api-gateway-ai-stack/&quot;&gt;API gateway&lt;/a&gt; level. This ensures that even if one tenant goes rogue, the rest of the platform stays healthy.&lt;/p&gt;
&lt;h3&gt;1. The global provider layer&lt;/h3&gt;
&lt;p&gt;This is your final line of defense. If your OpenAI quota is 500,000 tokens per minute (TPM), set your internal global limit to 450,000. This leaves a safety buffer so you degrade gracefully on your own terms instead of eating a wall of provider &lt;code&gt;429&lt;/code&gt;s — which means failed user requests and a Retry-After you don&apos;t control.&lt;/p&gt;
&lt;h3&gt;2. The tenant layer&lt;/h3&gt;
&lt;p&gt;Every customer gets their own bucket. I usually tie this to their subscription tier. A &quot;Pro&quot; user might get 50,000 TPM, while a &quot;Free&quot; user is capped at 2,000. This ensures no single company can eat up your entire global quota.&lt;/p&gt;
&lt;h3&gt;3. The user/session layer&lt;/h3&gt;
&lt;p&gt;Inside a single tenant, you still need limits. You don&apos;t want one single employee at a customer&apos;s company hogging all the tokens allocated to that entire organization. I set these at about 20% of the total tenant capacity.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/rate-limiting-ai-wallet/architecture.webp&quot; alt=&quot;Hierarchical rate-limit architecture — global, tenant, user buckets&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Implementation: the token bucket algorithm&lt;/h2&gt;
&lt;p&gt;For most of my builds, I use a &lt;strong&gt;token bucket&lt;/strong&gt; algorithm backed by Redis. It refills tokens at a steady rate and lets a request through only if a token is available — which absorbs short bursts while capping the sustained rate. (For the difference between token bucket and leaky bucket, see &lt;a href=&quot;https://ansezz.com/blog/rate-limiting-vs-throttling/&quot;&gt;rate limiting vs throttling&lt;/a&gt;.)&lt;/p&gt;
&lt;p&gt;Here is the logic: each user has a &quot;bucket&quot; of tokens. Every time they send a prompt, we estimate the total cost (input tokens + expected &lt;code&gt;max_tokens&lt;/code&gt;). If the bucket has enough, they proceed and the tokens are deducted. The bucket refills at a constant rate over time.&lt;/p&gt;
&lt;p&gt;If you&apos;re building a SaaS on the LEMP stack, you can implement this efficiently in Laravel using middleware and a fast in-memory store like Redis, where atomic increment operations make per-tenant counters cheap and race-free.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// A simplified token-bucket check in Laravel middleware
public function handle($request, Closure $next)
{
    $tenantId = $request-&amp;gt;user()-&amp;gt;tenant_id;
    $estimatedTokens = $this-&amp;gt;tokenizer-&amp;gt;estimate($request-&amp;gt;input(&apos;prompt&apos;));

    if (!$this-&amp;gt;limiter-&amp;gt;consume(&quot;tenant:{$tenantId}:tokens&quot;, $estimatedTokens)) {
        return response()-&amp;gt;json([&apos;error&apos; =&amp;gt; &apos;token budget exceeded&apos;], 429);
    }

    return $next($request);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/rate-limiting-ai-wallet/code.webp&quot; alt=&quot;Laravel middleware token-bucket snippet&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Token-budget routing and adaptive throttling&lt;/h2&gt;
&lt;p&gt;What happens when a user hits their limit? Most devs just throw a &lt;code&gt;429&lt;/code&gt; error. But as a senior engineer, I prefer a more graceful degradation. We call this &lt;strong&gt;adaptive throttling&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Instead of a hard &quot;no,&quot; you can:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Degrade the model&lt;/strong&gt; — switch the request from GPT-4o to a cheaper, faster model like GPT-4o-mini.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Truncate the context&lt;/strong&gt; — if the user is over budget, strip out some of the retrieved RAG documents to lower the input token count.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Queue the request&lt;/strong&gt; — for non-interactive tasks (like background summarization), move the request to a message queue and process it when the token bucket refills.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This keeps the user experience intact while protecting your margins. It&apos;s about being smart, not just being a gatekeeper.&lt;/p&gt;
&lt;h2&gt;The RAG context: limiting the &quot;hidden&quot; calls&lt;/h2&gt;
&lt;p&gt;In a RAG (retrieval-augmented generation) system, one user query often triggers multiple backend actions:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;One embedding call for the query.&lt;/li&gt;
&lt;li&gt;One search query to the vector database.&lt;/li&gt;
&lt;li&gt;One (or more) LLM calls for the final answer.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;If you only rate limit the final LLM call, your vector database might still get hammered by search queries. You need to treat the entire &quot;RAG flow&quot; as a single unit of work with its own combined budget. There are plenty of &lt;a href=&quot;https://ansezz.com/blog/7-rag-mistakes-production/&quot;&gt;common RAG production mistakes&lt;/a&gt; to trip over, but rate limiting the whole flow rather than just the LLM call is one of the most overlooked.&lt;/p&gt;
&lt;h2&gt;Practical steps to protect your system today&lt;/h2&gt;
&lt;p&gt;If you&apos;re launching an AI feature this week, do these three things:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Set a budget threshold — but don&apos;t trust it as a hard cap&lt;/strong&gt; — OpenAI&apos;s &quot;monthly budget&quot; is now notification-only: when you cross it you get an email and a dashboard alert, but your API keys keep working and the bill keeps climbing. Anthropic similarly relies on user/org spend limits and alerts rather than a default hard ceiling. Treat these as smoke detectors, not circuit breakers. The real cap — daily or monthly — has to be enforced by you, at the application layer.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Enforce &lt;code&gt;max_tokens&lt;/code&gt;&lt;/strong&gt; — never let a user request an uncapped response. Always set a sane default for &lt;code&gt;max_tokens&lt;/code&gt; in every API call.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Implement per-request timeout&lt;/strong&gt; — if an LLM call takes longer than 30 seconds, kill it. Slow calls are often the symptom of a system that is about to spiral out of control.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Rate limiting isn&apos;t just a &quot;security&quot; feature. In the AI era, it&apos;s a core part of your business model. You can&apos;t scale a product that allows a single user to run up a thousand-dollar bill in their first hour.&lt;/p&gt;
&lt;p&gt;Build for fairness. Build for cost. Build for the &quot;noisy neighbor.&quot;&lt;/p&gt;
&lt;p&gt;Have you ever seen a &quot;denial of wallet&quot; happen in the wild, or are you still running on a wing and a prayer with global provider limits? Drop a note via &lt;a href=&quot;https://ansezz.com/contact/&quot;&gt;contact&lt;/a&gt; — I love this conversation. 🤘&lt;/p&gt;
</content:encoded><category>architecture</category><category>laravel</category><category>security</category><category>multi-tenancy</category><category>redis</category><category>api-design</category><category>llm</category><category>rag</category></item><item><title>API gateway: the front door of your AI stack</title><link>https://ansezz.com/blog/api-gateway-ai-stack/</link><guid isPermaLink="true">https://ansezz.com/blog/api-gateway-ai-stack/</guid><description>Stop exposing LLM providers to your frontend. The API gateway pattern for AI apps: tenant isolation, model aliases, rate limiting, and streaming-safe timeouts.</description><pubDate>Wed, 20 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Stop exposing your models to the wild.&lt;/p&gt;
&lt;p&gt;If you are building a production AI app, sending requests directly from your frontend to a RAG orchestrator or — god forbid — straight to an LLM provider is a liability. It is slow. It is insecure. And it is the fastest way to wake up to a five-figure bill you didn&apos;t plan for.&lt;/p&gt;
&lt;p&gt;I have spent over a decade building software, and if there is one thing I have learned, it is that engineering for &quot;it works&quot; is not the same as engineering for &quot;it scales.&quot; In the world of AI, scale isn&apos;t just about traffic. It is about cost, latency, and data safety.&lt;/p&gt;
&lt;p&gt;Imagine a &quot;denial of wallet&quot; attack where a malicious script spams your completions endpoint. Without a gatekeeper, your API keys are just sitting ducks. Or worse, imagine a multi-tenant app where one user&apos;s prompt accidentally retrieves another user&apos;s private data from your vector DB.&lt;/p&gt;
&lt;p&gt;This is where the API gateway comes in. It is the first line of defense and the brain of your infrastructure. It handles the boring but critical stuff so your RAG logic can stay focused on actually being smart.&lt;/p&gt;
&lt;h2&gt;The gatekeeper pattern&lt;/h2&gt;
&lt;p&gt;At its core, an API gateway is a reverse proxy that sits between your users and your backend services. But for an AI stack, it does more than just forward traffic. It acts as a centralized brain for auth, routing, and rate limiting. (If the distinction blurs for you, I break it down in &lt;a href=&quot;https://ansezz.com/blog/load-balancer-vs-api-gateway/&quot;&gt;load balancer vs API gateway&lt;/a&gt;.)&lt;/p&gt;
&lt;p&gt;When a request hits your gateway, it goes through a gauntlet of checks before it ever touches a model. This &quot;gatekeeper&quot; ensures that every millisecond of GPU time or every cent of token cost is intentional.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/api-gateway-ai-stack/gateway-stack.webp&quot; alt=&quot;Three API gateway responsibilities shown as cards: a key for auth, a route map for routing, and a gauge for rate limits&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Authentication and tenant isolation&lt;/h2&gt;
&lt;p&gt;In a typical SaaS, authentication is about knowing who the user is. In an AI-powered SaaS, it is about data sovereignty.&lt;/p&gt;
&lt;p&gt;If you are building a RAG system, your biggest risk is cross-tenant data leakage. If you want to avoid &lt;a href=&quot;https://ansezz.com/blog/7-rag-mistakes-production/&quot;&gt;common RAG mistakes&lt;/a&gt;, you must handle identity at the very edge.&lt;/p&gt;
&lt;p&gt;I prefer using JWTs (JSON Web Tokens) with custom claims. When a request hits the gateway, I validate the token and extract the &lt;code&gt;tenant_id&lt;/code&gt;. That ID is then injected into the headers of the request before it is passed to the RAG orchestrator.&lt;/p&gt;
&lt;p&gt;This means the orchestrator doesn&apos;t have to &quot;guess&quot; who the user is. It receives a verified &lt;code&gt;x-tenant-id&lt;/code&gt; header and uses it to apply metadata filters on the vector database. The user only &quot;sees&quot; data they are allowed to see. No tenant ID? No query. Period.&lt;/p&gt;
&lt;h2&gt;Smart routing for model flexibility&lt;/h2&gt;
&lt;p&gt;The AI world moves fast. Today you are using GPT-5. Tomorrow, Claude Sonnet 4.6 might be the better play. Next week, you might want to test a fine-tuned Llama 4 model running on your own infrastructure via &lt;a href=&quot;https://ansezz.com/blog/coolify-docker-saas-hosting/&quot;&gt;Docker and Coolify&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If your model logic is hardcoded into your frontend or a single monolithic backend, switching models is a nightmare. An API gateway solves this with smart routing.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/api-gateway-ai-stack/routing-flow.webp&quot; alt=&quot;Diagram of a user message passing through a shielded API gateway that routes it to one of three LLM backends&quot; /&gt;&lt;/p&gt;
&lt;p&gt;I use the gateway to create &quot;model aliases.&quot; Instead of the frontend calling a specific model, it calls a generic endpoint like &lt;code&gt;/v1/chat/completions&lt;/code&gt;. The gateway then decides where to send that request based on:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;User tier&lt;/strong&gt; — free users get routed to a cheaper, faster model like GPT-5 mini or Claude Haiku 4.5. Pro users get the heavy hitters.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Versioning&lt;/strong&gt; — run an A/B test by routing 10% of traffic to a new model version without changing a single line of client-side code.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Failover&lt;/strong&gt; — if OpenAI is having an outage, the gateway can automatically reroute traffic to an Anthropic backup.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This level of abstraction is what separates a weekend project from a resilient SaaS product.&lt;/p&gt;
&lt;h2&gt;Rate limiting: protecting the wallet&lt;/h2&gt;
&lt;p&gt;We used to rate limit to protect our CPUs. Now, we rate limit to protect our bank accounts.&lt;/p&gt;
&lt;p&gt;AI requests are asymmetric. A user sends a 50-word prompt, and the model might generate a 1,000-word response. The cost difference is massive.&lt;/p&gt;
&lt;p&gt;A good API gateway implementation allows for tiered rate limiting. Set global limits to prevent your entire system from being overwhelmed, but also set per-tenant or per-user limits.&lt;/p&gt;
&lt;p&gt;I usually implement this using Redis. The gateway checks the user&apos;s quota in real time. If they have exceeded their daily token limit or their requests-per-minute (RPM) cap, the gateway returns a &lt;code&gt;429 Too Many Requests&lt;/code&gt; immediately. I go deeper on the token-budget angle in &lt;a href=&quot;https://ansezz.com/blog/rate-limiting-ai-wallet/&quot;&gt;rate limiting to protect your AI wallet&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This saves your backend from doing expensive work that you won&apos;t get paid for. It also stops &quot;noisy neighbors&quot; — one user scripting an automated tool that hogs all your capacity and makes the app slow for everyone else.&lt;/p&gt;
&lt;h2&gt;Handling the AI-specific quirks&lt;/h2&gt;
&lt;p&gt;Gateways for AI need to handle two things differently than traditional web apps: streaming and long-running requests.&lt;/p&gt;
&lt;h3&gt;Streaming support&lt;/h3&gt;
&lt;p&gt;Most modern AI apps use Server-Sent Events (SSE) to stream responses word by word. Some older gateways or load balancers try to &quot;buffer&quot; the entire response before sending it to the client. This kills the user experience.&lt;/p&gt;
&lt;p&gt;Make sure your gateway (whether you are using &lt;a href=&quot;https://konghq.com/&quot;&gt;Kong&lt;/a&gt;, &lt;a href=&quot;https://tyk.io/&quot;&gt;Tyk&lt;/a&gt;, or a custom &lt;a href=&quot;https://ansezz.com/blog/laravel-multi-tenancy/&quot;&gt;Laravel solution&lt;/a&gt;) is configured to disable buffering for AI routes. The data should flow through the gateway like water through a pipe, not like a bucket that needs to be filled.&lt;/p&gt;
&lt;h3&gt;Extended timeouts&lt;/h3&gt;
&lt;p&gt;Traditional APIs expect a response in 1–2 seconds. A complex RAG query involving multiple vector searches and a large model generation might take 30 seconds or more.&lt;/p&gt;
&lt;p&gt;You need to adjust your gateway&apos;s &quot;upstream timeout&quot; settings. Kong defaults to a 60-second upstream timeout, but managed gateways are far stricter — AWS API Gateway integrations default to a 29-second cap, and you can only raise it beyond that on Regional or private REST APIs by requesting a service quota increase (which may cost you some account throttle headroom). Leave those defaults in place and your users will see &lt;code&gt;504 Gateway Timeout&lt;/code&gt; errors even when your models are working perfectly.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/api-gateway-ai-stack/clean-code.webp&quot; alt=&quot;A monitor showing a Kubernetes Gateway API YAML manifest next to a &apos;keep it clean&apos; sticky note and a coffee mug&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Practical steps for your stack&lt;/h2&gt;
&lt;p&gt;You don&apos;t need a massive team to set this up. Here is how I usually approach it depending on the project size:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;For startups&lt;/strong&gt; — use a cloud-native gateway like AWS API Gateway or Azure API Management. They are serverless, scale automatically, and integrate directly with Cognito or Entra ID for auth.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;For self-hosters&lt;/strong&gt; — Kong is the gold standard. It has a great ecosystem of plugins for rate limiting and auth. If you are comfortable with PHP, a thin Laravel app acting as a gateway works surprisingly well for custom logic.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;For Shopify devs&lt;/strong&gt; — if you are building &lt;a href=&quot;https://ansezz.com/blog/agentic-commerce-shopify/&quot;&gt;agentic commerce tools&lt;/a&gt;, use the gateway to handle the specific Shopify HMAC validation before passing the request to your AI agents.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Wrapping up&lt;/h2&gt;
&lt;p&gt;The API gateway isn&apos;t just a piece of infrastructure. It is a design philosophy. It says that your AI logic is too valuable — and too expensive — to be left unprotected.&lt;/p&gt;
&lt;p&gt;By centralizing auth, routing, and rate limiting, you make your system more modular. You can swap models, change pricing tiers, and update security policies without touching the core code that makes your AI &quot;smart.&quot;&lt;/p&gt;
&lt;p&gt;Are you still letting your frontend talk directly to your LLM providers? If so, what is the one thing stopping you from putting a gateway in front of it?&lt;/p&gt;
&lt;p&gt;Stay sharp.
— a senior dev&lt;/p&gt;
&lt;hr /&gt;
&lt;h3&gt;Actionable takeaways&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Centralize auth&lt;/strong&gt; — never let your RAG orchestrator handle raw user authentication. Do it at the gateway.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Inject tenant context&lt;/strong&gt; — use the gateway to verify the user and inject a &lt;code&gt;tenant_id&lt;/code&gt; header to enforce data isolation.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Implement global + per-user limits&lt;/strong&gt; — protect your wallet from both malicious attacks and accidental bugs.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Configure for streaming&lt;/strong&gt; — ensure your gateway doesn&apos;t buffer responses, or your &quot;typing&quot; effect will break.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Use model aliases&lt;/strong&gt; — route to &lt;code&gt;/chat/pro&lt;/code&gt; instead of a specific model name to keep your stack flexible.&lt;/li&gt;
&lt;/ol&gt;
</content:encoded><category>architecture</category><category>api-design</category><category>ai</category><category>rag</category><category>security</category><category>multi-tenancy</category><category>messaging</category><category>infrastructure</category></item><item><title>Shopify Storefront Web Components: headless light</title><link>https://ansezz.com/blog/shopify-storefront-web-components/</link><guid isPermaLink="true">https://ansezz.com/blog/shopify-storefront-web-components/</guid><description>Headless used to mean six engineers and a Hydrogen rebuild. Shopify Storefront Web Components drop products, collections, and cart into any page with a script.</description><pubDate>Tue, 19 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Headless used to mean hiring a team of six engineers just to show a &quot;Buy&quot; button. Shopify Storefront Web Components change that.&lt;/p&gt;
&lt;p&gt;If you wanted a custom Shopify experience outside of their Liquid-based themes, you were usually forced into a big architectural decision. You had to build a full React or Hydrogen app, manage server-side rendering, handle complex routing, and pray your SEO didn&apos;t tank. For many startups and established brands, this was like buying a semi-truck when all they needed was a bicycle.&lt;/p&gt;
&lt;p&gt;The complexity of traditional headless was a gatekeeper. It prevented simple content-driven sites on platforms like Astro or WordPress from easily selling products without a jarring transition to a subdomain. We&apos;ve spent years over-engineering solutions for problems that could have been solved with a few custom elements.&lt;/p&gt;
&lt;p&gt;Shopify Storefront Web Components have changed the math. Now, adding commerce to any site is as simple as dropping in a script tag.&lt;/p&gt;
&lt;h2&gt;The headless headache&lt;/h2&gt;
&lt;p&gt;Building a headless store often feels like building a house from scratch. You have to worry about the foundation (hosting), the plumbing (GraphQL queries), and the wiring (state management).&lt;/p&gt;
&lt;p&gt;If you are a senior engineer, you know the drill. You spend weeks configuring Shopify Hydrogen or a custom Next.js setup just to get a cart that actually works. And once it&apos;s live, you&apos;re the one who has to maintain the Node.js environment and the middleware.&lt;/p&gt;
&lt;p&gt;It&apos;s expensive. It&apos;s slow to deploy. And for 80% of use cases, it is total overkill. We need a way to get the flexibility of headless without the technical debt of a heavy framework.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/shopify-storefront-web-components/complexity-vs-simplicity.webp&quot; alt=&quot;Headless complexity vs simplicity&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Entering the era of custom elements&lt;/h2&gt;
&lt;p&gt;Shopify Storefront Web Components are framework-agnostic. They are standard Web Components (custom elements) that handle the heavy lifting of communicating with Shopify&apos;s Storefront API.&lt;/p&gt;
&lt;p&gt;You don&apos;t need React. You don&apos;t need a build step. You just need HTML.&lt;/p&gt;
&lt;p&gt;This &quot;headless light&quot; approach lets you embed products, collections, and a fully functional cart directly into your existing stack. Whether you are using a static site generator or a legacy CMS, these components act as bridge-builders. They let you keep your content where it is and pull the commerce in dynamically.&lt;/p&gt;
&lt;h2&gt;How to get started in 5 minutes&lt;/h2&gt;
&lt;p&gt;Getting these up and running is refreshing for any developer used to complex APIs. Here is the workflow I use when I want to move fast.&lt;/p&gt;
&lt;h3&gt;1. Connect your store&lt;/h3&gt;
&lt;p&gt;The first step is adding the script tag and the &lt;code&gt;&amp;lt;shopify-store&amp;gt;&lt;/code&gt; component to your HTML. This establishes the connection to your Shopify domain.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;!-- Load the components script — see the official docs:
     https://shopify.dev/docs/api/storefront-web-components --&amp;gt;
&amp;lt;script src=&quot;https://cdn.shopify.com/storefront/web-components.js&quot;&amp;gt;&amp;lt;/script&amp;gt;

&amp;lt;shopify-store store-domain=&quot;https://your-store.myshopify.com&quot;&amp;gt;&amp;lt;/shopify-store&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you need inventory counts or custom data, add a &lt;code&gt;public-access-token&lt;/code&gt; from the Shopify headless channel. For basic title and price display, you don&apos;t even need that.&lt;/p&gt;
&lt;h3&gt;2. Define the context&lt;/h3&gt;
&lt;p&gt;The magic happens with &lt;code&gt;&amp;lt;shopify-context&amp;gt;&lt;/code&gt;. This tells the page which product or collection it should be looking at. You just pass the handle from your Shopify admin.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;shopify-context type=&quot;product&quot; handle=&quot;awesome-tshirt&quot;&amp;gt;
  &amp;lt;template&amp;gt;
    &amp;lt;!-- your content goes here --&amp;gt;
  &amp;lt;/template&amp;gt;
&amp;lt;/shopify-context&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;3. Display the data&lt;/h3&gt;
&lt;p&gt;Inside that template, you use &lt;code&gt;&amp;lt;shopify-data&amp;gt;&lt;/code&gt; to pull specific fields. It uses dot notation, making it feel very familiar to anyone who has worked with Liquid or JavaScript objects.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;shopify-data query=&quot;product.title&quot;&amp;gt;&amp;lt;/shopify-data&amp;gt;
&amp;lt;shopify-money
  query=&quot;product.selectedOrFirstAvailableVariant.price&quot;
&amp;gt;&amp;lt;/shopify-money&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/shopify-storefront-web-components/ai-assisted-code.webp&quot; alt=&quot;AI-assisted Shopify code&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;The secret sauce: llms.txt&lt;/h2&gt;
&lt;p&gt;Here is the part where it gets interesting for modern developers. Shopify publishes a machine-readable &lt;a href=&quot;https://shopify.dev/llms.txt&quot;&gt;llms.txt&lt;/a&gt; file.&lt;/p&gt;
&lt;p&gt;If you&apos;ve been following the rise of &lt;a href=&quot;https://ansezz.com/blog/agentic-workflows-vibe-coding/&quot;&gt;agentic workflows and vibe coding&lt;/a&gt;, you know that providing context to an AI model is everything. By pointing your AI agent (like Claude or ChatGPT) to this text file, it learns exactly how to write code for these specific Web Components.&lt;/p&gt;
&lt;p&gt;This eliminates the hallucination problem. Instead of the AI guessing how a Shopify component should look, it uses the official spec. I&apos;ve found that including this link in my system prompt lets me generate entire product landing pages in seconds that actually work on the first try.&lt;/p&gt;
&lt;p&gt;It turns &quot;development&quot; into &quot;orchestration.&quot; For more on how to leverage these tools, check out how &lt;a href=&quot;https://ansezz.com/blog/mcp-context-aware-agents/&quot;&gt;MCP context-aware agents&lt;/a&gt; are changing the way we handle technical docs.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/shopify-storefront-web-components/llms-txt.webp&quot; alt=&quot;llms.txt verification&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;When to use Web Components vs Hydrogen&lt;/h2&gt;
&lt;p&gt;As a senior engineer, you have to pick the right tool for the job. Here is my rule of thumb.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Use Storefront Web Components if:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;You have an existing marketing site and want to add &quot;buy buttons&quot; or a mini-cart.&lt;/li&gt;
&lt;li&gt;You are building a landing page and need speed above all else.&lt;/li&gt;
&lt;li&gt;Your team doesn&apos;t want to maintain a React/Node.js infrastructure.&lt;/li&gt;
&lt;li&gt;You want to stay framework-agnostic.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Use Shopify Hydrogen if:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;You are building a mission-critical, full-scale custom storefront.&lt;/li&gt;
&lt;li&gt;You need complex server-side logic and deep integrations with multiple APIs.&lt;/li&gt;
&lt;li&gt;You require the absolute highest level of performance optimization via streaming and edge rendering.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For most people starting out or modernizing a digital presence, Web Components are the winning choice. They offer the best balance of &lt;a href=&quot;https://ansezz.com/blog/ai-vs-traditional-development/&quot;&gt;AI-driven development&lt;/a&gt; and production stability. If you&apos;re still weighing the bigger architectural call, see &lt;a href=&quot;https://ansezz.com/blog/shopify-liquid-vs-headless/&quot;&gt;Shopify Liquid vs. headless&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Takeaways for the modern dev&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Web Components are the &quot;headless light&quot; solution we&apos;ve been waiting for.&lt;/li&gt;
&lt;li&gt;They work anywhere — WordPress, Astro, plain HTML, or even a local static file.&lt;/li&gt;
&lt;li&gt;Use the &lt;code&gt;llms.txt&lt;/code&gt; file to train your AI assistant for perfect code generation.&lt;/li&gt;
&lt;li&gt;Avoid over-engineering — if you don&apos;t need a massive React app, don&apos;t build one.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Building for the web in 2026 is about reducing friction. Shopify has finally removed the friction from headless.&lt;/p&gt;
&lt;p&gt;Are you still building full React apps for simple stores, or are you ready to embrace the simplicity of custom elements? Drop a note via &lt;a href=&quot;https://ansezz.com/contact/&quot;&gt;contact&lt;/a&gt; — I love this conversation. 🤘&lt;/p&gt;
</content:encoded><category>shopify</category><category>shopify</category><category>hydrogen</category><category>agentic-commerce</category><category>agentic-ai</category></item><item><title>How agentic commerce changes building on Shopify</title><link>https://ansezz.com/blog/agentic-commerce-shopify/</link><guid isPermaLink="true">https://ansezz.com/blog/agentic-commerce-shopify/</guid><description>Agentic commerce is reshaping how you build Shopify. The shift from human-centric themes to agent-ready infrastructure: Catalog, UCP, and MCP.</description><pubDate>Mon, 18 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Your beautiful Shopify theme is about to become invisible.&lt;/p&gt;
&lt;p&gt;For a decade, we have obsessed over conversion rate optimization, pixel-perfect layouts, and fast-loading Liquid templates. We built for human eyes. But a new type of shopper is arriving at the digital storefront. They don&apos;t have eyes, they don&apos;t click buttons, and they certainly don&apos;t care about your hero slider.&lt;/p&gt;
&lt;p&gt;They are autonomous AI agents.&lt;/p&gt;
&lt;p&gt;The shift from human-centric browsing to agentic commerce is the biggest architectural pivot Shopify has ever seen. If you are still just &quot;the theme guy&quot; or &quot;the Liquid expert,&quot; your skillset is about to hit a massive wall. I have been building on the web for over ten years, and I can tell you that the era of building for agents is here. It is time to move from designing pixels to designing protocols.&lt;/p&gt;
&lt;h2&gt;The problem: the end of the traditional funnel&lt;/h2&gt;
&lt;p&gt;Most developers are still stuck in the old way of thinking. We build a site, drive traffic to a landing page, and hope the user navigates to the checkout. This is a manual, high-friction process.&lt;/p&gt;
&lt;p&gt;Today, buyers are increasingly using tools like ChatGPT, Gemini, or Microsoft Copilot to do the &quot;work&quot; of shopping. They ask for a waterproof hiking boot under $150 with specific shipping requirements. The AI doesn&apos;t visit your store to browse. It queries data. If your store isn&apos;t built to be &quot;read&quot; by an agent, you simply don&apos;t exist in that transaction.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/agentic-commerce-shopify/shopping-flows.webp&quot; alt=&quot;Bento grid showing how an AI agent queries product data to handle discovery, comparison, and checkout&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;The agitation: why your current apps aren&apos;t enough&lt;/h2&gt;
&lt;p&gt;We have spent years building &quot;utility&quot; apps that handle simple automations. But Shopify is moving fast to pull those features into the core. With tools like Shopify Magic and Sidekick, the basic stuff — copywriting, image editing, simple workflows — is being commoditized.&lt;/p&gt;
&lt;p&gt;The real value is moving deeper into the stack. Agents need more than just a product description. They need structured data, real-time inventory logic, and a way to execute a checkout without a browser window. If you are relying on &quot;fake&quot; variants or messy metafields, you are breaking the agent&apos;s ability to reason about your store.&lt;/p&gt;
&lt;p&gt;When an agent fails to understand your product structure, the buyer gets a &quot;no results found&quot; or a hallucinated alternative. That is a lost sale for your client and a failed project for you.&lt;/p&gt;
&lt;h2&gt;The solution: welcome to agentic commerce&lt;/h2&gt;
&lt;p&gt;Agentic commerce is a system where AI agents take over the discovery, comparison, and execution phases of shopping. Shopify is already laying the groundwork for this with three major pillars:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Universal Commerce Protocol (UCP)&lt;/strong&gt; — the open standard Shopify co-developed with Google for how agents discover, negotiate, and transact with any merchant. It is composable: it defines the conversation, then plugs into MCP for tool access and AP2 for payment authorization. (For a hands-on walkthrough, see my &lt;a href=&quot;https://ansezz.com/blog/shopify-ucp-quick-start/&quot;&gt;Shopify UCP quick-start&lt;/a&gt;.)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Catalog API&lt;/strong&gt; — UCP&apos;s discovery layer. It standardizes products into a universal taxonomy, verifies pricing and inventory in real time, and syndicates that structured data to connected AI surfaces.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Agentic Storefronts&lt;/strong&gt; — the management layer where you configure product data once in the Shopify admin, then distribute and sell across every AI channel (ChatGPT, Microsoft Copilot, Google&apos;s AI Mode, the Gemini app).&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;As developers, our job is shifting. We are no longer just building visual storefronts. We are building &quot;agent-ready&quot; infrastructure.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/agentic-commerce-shopify/ui-comparison.webp&quot; alt=&quot;Side-by-side comparison of a human-facing Shopify theme and the structured data an agent consumes&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Why schema is the new CSS&lt;/h2&gt;
&lt;p&gt;In the agentic era, your structured data is your most important asset. A perfectly optimized JSON-LD schema or a clean set of Shopify Metaobjects is now more valuable than a fancy hover effect.&lt;/p&gt;
&lt;p&gt;Agents rely on clarity. They need to know if a product is compatible with another, what the specific material breakdown is, and exactly when it will arrive. If you want to see how I&apos;ve helped businesses modernize their digital presence through better architecture, check out some of my &lt;a href=&quot;https://ansezz.com/work/&quot;&gt;previous work&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The focus for developers must move toward:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Native variants only&lt;/strong&gt; — stop using &quot;split products&quot; or custom Liquid hacks to show variants. Agents need standard &lt;code&gt;ProductVariant&lt;/code&gt; records to function.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Rich Metaobjects&lt;/strong&gt; — use these to build knowledge bases that agents can query. Think compatibility tables, brand story, and detailed technical specs.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;GraphQL mastery&lt;/strong&gt; — the Shopify Admin and Storefront APIs are the primary languages of agents. If you aren&apos;t comfortable with complex GraphQL queries, you are already behind.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Building custom agents with MCP and Shopify&lt;/h2&gt;
&lt;p&gt;The most exciting part of this shift is building our own agents. The &lt;a href=&quot;https://ansezz.com/blog/mcp-context-aware-agents/&quot;&gt;Model Context Protocol (MCP)&lt;/a&gt; lets us connect AI models to external data sources like the Shopify API — and it&apos;s the same binding UCP uses to expose commerce tools to agents.&lt;/p&gt;
&lt;p&gt;Imagine building an &quot;inventory agent&quot; for a merchant that doesn&apos;t just alert them when stock is low. Instead, it queries recent sales trends via the Admin API, checks lead times from a supplier, and suggests a restock amount — all through a chat interface.&lt;/p&gt;
&lt;p&gt;This is where tools like Laravel and Docker come in handy. We can build custom middleware that acts as an MCP server, exposing specific Shopify &quot;tools&quot; to an AI agent. This is the type of deep technical work that differentiates a senior engineer from a freelancer who just installs themes.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/agentic-commerce-shopify/mcp-architecture.webp&quot; alt=&quot;Architecture diagram of an MCP server exposing Shopify Admin API tools to an AI agent&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;The Shopify developer&apos;s new roadmap&lt;/h2&gt;
&lt;p&gt;To thrive in the next five years, I recommend focusing on these specific technical areas.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1. Data hygiene as a service&lt;/strong&gt;
Start offering &quot;agent-readiness&quot; audits. Clean up product data, normalize attributes, and ensure all metadata is machine-readable. This is the new SEO.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2. Headless and API-first builds&lt;/strong&gt;
While Liquid isn&apos;t dead, headless architectures using Hydrogen and Oxygen are much more aligned with the agentic future. They force you to think in terms of data and APIs rather than just templates. If you&apos;re weighing the trade-offs, I broke them down in &lt;a href=&quot;https://ansezz.com/blog/shopify-liquid-vs-headless/&quot;&gt;Shopify Liquid vs. headless&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3. RAG and vector databases&lt;/strong&gt;
Learn how to use RAG (retrieval-augmented generation) and tools like pgvector. This lets you build agents that can search through thousands of product reviews or support docs to give &quot;expert&quot; advice to shoppers. If you&apos;re new to this, start with &lt;a href=&quot;https://ansezz.com/blog/picking-the-right-rag-stack/&quot;&gt;picking the right RAG stack&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4. Understand agent trust tiers and the Agentic plan&lt;/strong&gt;
Agentic Storefronts gate access by agent trust tier — higher tiers unlock broader capabilities, up to completing checkout directly. There is also a dedicated Agentic plan that lets brands on any platform list products in Shopify Catalog and sell through agentic storefronts. Know where direct checkout is enabled and how to configure it.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/agentic-commerce-shopify/shopping-agent.webp&quot; alt=&quot;Shopper completing a purchase through an AI shopping agent inside a chat interface&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Final takeaways for the agentic shift&lt;/h2&gt;
&lt;p&gt;The change is happening faster than most of us realize. Shopify reports that in Q1 2026, AI-driven traffic to its stores grew roughly 8x year over year, orders from AI-powered searches climbed nearly 13x, and new buyers are ordering through AI channels at close to twice the rate of other channels.&lt;/p&gt;
&lt;p&gt;If you want to stay relevant, here is your checklist:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Move your product logic into native Shopify structures.&lt;/li&gt;
&lt;li&gt;Double down on GraphQL and API integration.&lt;/li&gt;
&lt;li&gt;Start experimenting with MCP servers and local LLMs for internal store operations.&lt;/li&gt;
&lt;li&gt;Remember that the agent is your new &quot;user.&quot; Optimize for its understanding first.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We are moving into a world where commerce is autonomous and frictionless. It is an incredible time to be a developer if you are willing to learn the new protocols. If you want to learn more about my background in high-quality web applications, you can read more &lt;a href=&quot;https://ansezz.com/about/&quot;&gt;about me&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The question is — are you building a store that only humans can see, or are you building a store that the whole world can buy from?&lt;/p&gt;
&lt;p&gt;Are you ready to start building your first AI-native Shopify tool, or are you still holding onto your CSS hacks? Drop a note via &lt;a href=&quot;https://ansezz.com/contact/&quot;&gt;contact&lt;/a&gt; — I love this conversation. 🤘&lt;/p&gt;
</content:encoded><category>shopify</category><category>shopify</category><category>agentic-commerce</category><category>ai</category><category>mcp</category><category>hydrogen</category><category>api-design</category></item><item><title>7 mistakes wrecking your production RAG stack</title><link>https://ansezz.com/blog/7-rag-mistakes-production/</link><guid isPermaLink="true">https://ansezz.com/blog/7-rag-mistakes-production/</guid><description>Naive chunking, no reranker, embedding drift, latency blowups — the structural mistakes that wreck a production RAG stack, and the fixes that ship.</description><pubDate>Sun, 17 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Getting a RAG (retrieval-augmented generation) demo working is easy. You take a few PDFs, throw them into a vector database like Chroma or Pinecone, and ask a question. It feels like magic. If you&apos;re still choosing your foundation, my guide to &lt;a href=&quot;https://ansezz.com/blog/picking-the-right-rag-stack/&quot;&gt;picking the right RAG stack&lt;/a&gt; covers the trade-offs before you commit.&lt;/p&gt;
&lt;p&gt;But shipping RAG to production is where the magic dies.&lt;/p&gt;
&lt;p&gt;I&apos;ve seen too many teams launch a feature only to realize that their users are getting irrelevant answers, waiting 10 seconds for a response, or worse, getting hit with &quot;I don&apos;t know&quot; for questions that are clearly in the documentation. When the &quot;vibe check&quot; fails at scale, your users lose trust.&lt;/p&gt;
&lt;p&gt;You&apos;re likely making at least one of these seven structural mistakes that turn a cool demo into a production nightmare. I&apos;ve spent the last few years building &lt;a href=&quot;https://ansezz.com/work/&quot;&gt;custom web applications&lt;/a&gt; and AI systems, and I&apos;ve had to fix these same leaks in my own stacks.&lt;/p&gt;
&lt;p&gt;Here is how to bridge the gap between &quot;it works on my machine&quot; and a production-grade AI system.&lt;/p&gt;
&lt;h2&gt;1. Naive chunking is killing your context&lt;/h2&gt;
&lt;p&gt;Most people start with a simple character-based or token-based splitter. You tell the library to &quot;give me chunks of 500 tokens with a 50-token overlap.&quot;&lt;/p&gt;
&lt;p&gt;This is a mistake.&lt;/p&gt;
&lt;p&gt;This &quot;naive chunking&quot; treats your data like raw soup. It might cut a sentence in half, split a table in the middle of a row, or separate a coding example from the explanation that precedes it. If the retriever pulls only one of those halves, the LLM has zero chance of giving a correct answer.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The fix:&lt;/strong&gt; use semantic or structural chunking.&lt;/p&gt;
&lt;p&gt;I always recommend chunking based on the actual structure of the document first. Use headers (H1, H2, H3), paragraphs, or even markdown delimiters to ensure related ideas stay together. If you&apos;re working with complex data, consider recursive character splitting that respects newlines and punctuation before falling back to raw token counts. Bad chunking is one of the most common reasons &lt;a href=&quot;https://ansezz.com/blog/why-your-rag-is-failing/&quot;&gt;your RAG fails in production&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/7-rag-mistakes-production/chunking.webp&quot; alt=&quot;Diagram comparing structural chunking that keeps a heading with its paragraph against naive chunking that splits a sentence and table mid-row&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;2. Skipping the reranker step&lt;/h2&gt;
&lt;p&gt;Vector search is great at finding &quot;roughly similar&quot; stuff, but it&apos;s not a precision instrument. It relies on cosine similarity, which can be easily fooled by documents that share a similar &quot;vibe&quot; but don&apos;t actually contain the answer.&lt;/p&gt;
&lt;p&gt;If you&apos;re just taking the top 5 results from your vector store and shoving them into your LLM prompt, you&apos;re leaving quality on the table.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The fix:&lt;/strong&gt; add a reranking step.&lt;/p&gt;
&lt;p&gt;I look at retrieval as a two-stage process. Stage one is the &quot;fast and broad&quot; search where you pull the top 20 or 50 candidates from your vector database. Stage two is using a cross-encoder or a specialized reranking model (like Cohere&apos;s Rerank or BGE-Reranker) to score those 50 candidates against the query more accurately.&lt;/p&gt;
&lt;p&gt;The reranker acts like a bouncer at a club. It doesn&apos;t care if a document looks &quot;okay.&quot; It only lets in the ones that are actually relevant to the question.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/7-rag-mistakes-production/reranker.webp&quot; alt=&quot;Two-stage retrieval funnel: a broad vector search returns 50 candidates, then a cross-encoder reranker narrows them to the most relevant few&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;3. Ignoring embedding drift and versioning&lt;/h2&gt;
&lt;p&gt;This is the silent killer. I&apos;ve seen teams upgrade their embedding model from &lt;code&gt;text-embedding-ada-002&lt;/code&gt; to &lt;code&gt;text-embedding-3-small&lt;/code&gt; without re-indexing their entire database.&lt;/p&gt;
&lt;p&gt;Suddenly, the vectors being generated for new queries don&apos;t &quot;line up&quot; with the vectors stored in the index. The similarity scores go haywire. Even worse is when you change the preprocessing logic (like how you format the chunks) but keep the old vectors.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The fix:&lt;/strong&gt; pin your models and version your index.&lt;/p&gt;
&lt;p&gt;Treat your embedding model like a database schema. If you change the model, you must re-index. I always include the model name and version in the metadata of every index I build. This way, if I need to test a new model, I can run them side-by-side without breaking the production flow. My &lt;a href=&quot;https://ansezz.com/about/&quot;&gt;experience in cloud infrastructure&lt;/a&gt; has taught me that consistency is better than a &quot;better&quot; model that doesn&apos;t match its data.&lt;/p&gt;
&lt;h2&gt;4. The &quot;lost in the middle&quot; latency problem&lt;/h2&gt;
&lt;p&gt;Everyone wants more context. We see context windows of 128k or even 1M tokens and think, &quot;great, I&apos;ll just give the LLM everything!&quot;&lt;/p&gt;
&lt;p&gt;This is a trap for two reasons. First, latency — feeding tens of thousands of tokens of context into an LLM inflates your time-to-first-token and can push response times into double-digit seconds. Second, models still struggle with the &lt;a href=&quot;https://arxiv.org/abs/2307.03172&quot;&gt;&quot;lost in the middle&quot;&lt;/a&gt; effect: they reliably use information at the start and end of a long context but tend to ignore facts buried in the center. A bigger &lt;a href=&quot;https://ansezz.com/blog/context-window-vs-memory/&quot;&gt;context window is not the same as memory&lt;/a&gt;, and stuffing it rarely helps.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The fix:&lt;/strong&gt; optimize your latency budget.&lt;/p&gt;
&lt;p&gt;I start with a &quot;latency budget.&quot; If the user expects a response in under 2 seconds, I can&apos;t afford to send 20 chunks. I limit my retrieval to the top 3–5 high-quality chunks and use streaming as soon as the first token is ready.&lt;/p&gt;
&lt;p&gt;If you need more data, consider using a multi-step approach: use a cheaper model to summarize the retrieved chunks before passing the refined info to your main model.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/7-rag-mistakes-production/latency.webp&quot; alt=&quot;Latency budget breakdown showing retrieval, reranking, and generation time fitting inside a two-second response target&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;5. Forgetting about hybrid search&lt;/h2&gt;
&lt;p&gt;Vector search is terrible at finding specific keywords or unique identifiers. If a user asks for &quot;error code XF-904,&quot; a vector search might return documents about &quot;general error handling&quot; because the &quot;vibe&quot; is similar. But it might miss the one specific document that actually mentions &quot;XF-904.&quot;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The fix:&lt;/strong&gt; implement hybrid search.&lt;/p&gt;
&lt;p&gt;I always combine dense vector search with traditional sparse search (like BM25). By blending these two results using something like Reciprocal Rank Fusion (RRF), you get the best of both worlds. You get the semantic understanding of vectors and the keyword precision of full-text search. This is non-negotiable for enterprise search or technical documentation. If keyword precision matters more than you expect, it&apos;s worth weighing &lt;a href=&quot;https://ansezz.com/blog/vector-search-vs-graph-search/&quot;&gt;vector search against graph search&lt;/a&gt; for your retrieval strategy.&lt;/p&gt;
&lt;h2&gt;6. Failing to filter by metadata&lt;/h2&gt;
&lt;p&gt;If your RAG system contains documents for different clients, versions, or dates, pure vector search will betray you. You might ask about &quot;API changes in 2024&quot; and get results from 2022 because they share similar keywords.&lt;/p&gt;
&lt;p&gt;Relying on the LLM to &quot;ignore&quot; the wrong dates in the context is a waste of tokens and a recipe for hallucinations.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The fix:&lt;/strong&gt; use hard metadata filters.&lt;/p&gt;
&lt;p&gt;Before the vector search even happens, apply filters. If you know the user is looking for &quot;v2&quot; of your documentation, filter the vector query to only include chunks with &lt;code&gt;version: &apos;2&apos;&lt;/code&gt;. This drastically reduces the search space and improves accuracy. I use this heavily when building &lt;a href=&quot;https://ansezz.com/work/&quot;&gt;Shopify apps&lt;/a&gt; where data must be strictly siloed by shop ID.&lt;/p&gt;
&lt;h2&gt;7. Vibe-based evaluation&lt;/h2&gt;
&lt;p&gt;How do you know your RAG stack is getting better? Most devs just ask a few questions, see that the answer looks okay, and ship it.&lt;/p&gt;
&lt;p&gt;This is called &quot;vibe-checking,&quot; and it doesn&apos;t work. When you change a prompt or a chunk size, you might improve one answer while breaking ten others you didn&apos;t check.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The fix:&lt;/strong&gt; build a golden evaluation set.&lt;/p&gt;
&lt;p&gt;I use the Ragas framework or simple LLM-as-a-judge patterns to run automated evals. I maintain a &quot;golden set&quot; of 50–100 questions with ground-truth answers. Every time I change the architecture, I run the eval and look for three metrics:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Faithfulness&lt;/strong&gt; — is the answer actually derived from the context?&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Answer relevance&lt;/strong&gt; — does it answer the user&apos;s question?&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Context precision&lt;/strong&gt; — are the retrieved chunks actually useful?&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/7-rag-mistakes-production/evals.webp&quot; alt=&quot;RAG evaluation harness scoring answers against a golden set on faithfulness, answer relevance, and context precision&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;Practical takeaways for your stack&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Start with metadata&lt;/strong&gt; — don&apos;t let the vector database guess. If you have categories or dates, use them as hard filters.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Rerank by default&lt;/strong&gt; — it&apos;s the single biggest quality jump you can make for the lowest effort.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Monitor retrieval, not just generation&lt;/strong&gt; — if your retriever fails, the best LLM in the world can&apos;t save you. Log your top-k retrieval results separately.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Don&apos;t over-engineer&lt;/strong&gt; — sometimes a simple long-context prompt is better than a complex agentic workflow. Measure before you add complexity.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Building production RAG is a game of millimeters. It&apos;s about cleaning your data, pinning your models, and actually measuring what&apos;s happening under the hood.&lt;/p&gt;
&lt;p&gt;I&apos;ve spent years moving from &quot;it works&quot; to &quot;it&apos;s reliable.&quot; If you&apos;re struggling with a specific part of your AI pipeline, what&apos;s the one thing that&apos;s currently keeping you from hitting that &quot;deploy&quot; button?&lt;/p&gt;
&lt;p&gt;Drop a comment or &lt;a href=&quot;https://ansezz.com/contact/&quot;&gt;reach out&lt;/a&gt; if you&apos;re hitting a wall with your architecture. 🤘&lt;/p&gt;
</content:encoded><category>ai</category><category>rag</category><category>ai</category><category>llm</category><category>pgvector</category><category>production</category></item><item><title>Scaling with RabbitMQ: why message brokers matter</title><link>https://ansezz.com/blog/scaling-with-rabbitmq/</link><guid isPermaLink="true">https://ansezz.com/blog/scaling-with-rabbitmq/</guid><description>Synchronous controllers are how monoliths die. RabbitMQ exchanges and queues, the strangler pattern for going async, and idempotent Laravel workers.</description><pubDate>Sat, 16 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;The monolith is screaming. Every time a user hits the &quot;checkout&quot; button, your server has to generate a PDF, send a welcome email, update the inventory, and ping three different third-party APIs. Your request/response cycle is hanging by a thread. If any of those external services take more than two seconds to respond, your user sees a 504 gateway timeout.&lt;/p&gt;
&lt;p&gt;It starts with a small delay. Then it becomes a bottleneck. Before you know it, you are throwing more RAM at a problem that cannot be solved by bigger hardware. This is the &quot;monolith wall.&quot; When everything is synchronous, a single failure in a secondary task brings down the entire user experience.&lt;/p&gt;
&lt;p&gt;I have been in these trenches. I have watched dashboards turn red during a marketing spike because the database was too busy processing background reports to handle new signups. The solution isn&apos;t just &quot;faster code.&quot; It is a change in how your services talk to each other. It is about &lt;strong&gt;decoupling&lt;/strong&gt;. It is about &lt;strong&gt;RabbitMQ&lt;/strong&gt;.&lt;/p&gt;
&lt;h2&gt;Why your request path is too crowded&lt;/h2&gt;
&lt;p&gt;In a standard web application, we often fall into the trap of doing too much inside the controller. A user makes a request, and we feel the need to finish every related task before sending back a &quot;200 OK.&quot; This is fine for a side project with ten users. For a scaling SaaS, it is a recipe for disaster.&lt;/p&gt;
&lt;p&gt;Think of it like a coffee shop. If the person taking your order also has to grind the beans, froth the milk, and hand-draw the logo on the cup before taking the next order, the line will wrap around the block. The shop fails because the cashier is &quot;tightly coupled&quot; to the barista&apos;s work.&lt;/p&gt;
&lt;p&gt;To scale, you need a system where the cashier takes the order, writes it on a slip, and hands it off. They are immediately free for the next customer. The work happens &quot;in the background.&quot; That slip of paper is your message. The counter where they put the slips is your message broker.&lt;/p&gt;
&lt;h2&gt;The RabbitMQ magic: more than just a queue&lt;/h2&gt;
&lt;p&gt;RabbitMQ is an open-source message broker that acts as the &quot;middleware&quot; for your architecture. It doesn&apos;t just store messages — it routes them with surgical precision.&lt;/p&gt;
&lt;p&gt;At its core, RabbitMQ uses a few key concepts:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Producers&lt;/strong&gt; — your web applications or APIs that create a task.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Exchanges&lt;/strong&gt; — the &quot;post office&quot; that decides which queue a message should go to based on rules.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Queues&lt;/strong&gt; — the temporary storage where messages sit until they are processed.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Consumers&lt;/strong&gt; — the background workers (often running in Docker containers) that actually do the heavy lifting.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;By putting RabbitMQ in the middle, your web tier only needs to do one thing: tell RabbitMQ that a task needs to be done. This takes milliseconds. The user gets an instant confirmation, while the heavy work happens whenever your workers are ready.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/scaling-with-rabbitmq/architecture-diagram.webp&quot; alt=&quot;Architecture diagram of producers, exchanges, queues, and consumers&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Smoothing out the spikes&lt;/h2&gt;
&lt;p&gt;One of the biggest pains in scaling is handling &quot;noisy neighbors&quot; or sudden traffic bursts. If a large enterprise client uploads a 100,000-row CSV for processing, you don&apos;t want that to slow down the login page for everyone else. This is exactly the kind of workload &lt;a href=&quot;https://ansezz.com/blog/message-queues-document-processing/&quot;&gt;message queues for heavy document processing&lt;/a&gt; are built to absorb.&lt;/p&gt;
&lt;p&gt;With RabbitMQ, those 100,000 rows become 100,000 individual messages in a queue. Your workers will chew through them at a steady pace. If the queue gets too long, you don&apos;t need to scale your entire application — you just spin up more worker instances.&lt;/p&gt;
&lt;p&gt;This is &lt;a href=&quot;https://ansezz.com/blog/horizontal-vs-vertical-scaling/&quot;&gt;horizontal scaling&lt;/a&gt; in its purest form. Since the workers are decoupled from the web server, you can scale them independently based on the specific load. In Laravel, you manage these background jobs through its queue system — and while Laravel ships drivers for Redis, SQS, and the database, talking directly to RabbitMQ uses a community package like &lt;code&gt;vladimir-yuldashev/laravel-queue-rabbitmq&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;How to move from sync to async&lt;/h2&gt;
&lt;p&gt;You don&apos;t have to rewrite your entire codebase overnight. I usually recommend the &quot;strangler pattern.&quot; Pick one slow, non-critical process. Maybe it is the &quot;forgot password&quot; email or an image resize task.&lt;/p&gt;
&lt;p&gt;Here is a simplified look at how you might dispatch a job in a modern PHP environment:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// instead of sending the email directly
// $emailService-&amp;gt;sendWelcome($user);

// we dispatch a job to RabbitMQ
ProcessWelcomeEmail::dispatch($user)-&amp;gt;onQueue(&apos;high-priority&apos;);

// the user gets a response instantly
return response()-&amp;gt;json([
    &apos;message&apos; =&amp;gt; &apos;welcome! check your inbox soon.&apos;,
]);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, even if your email provider (like SendGrid or Mailgun) is having a bad day, your application stays up. The message stays safely in the RabbitMQ queue until the service is back online.&lt;/p&gt;
&lt;h2&gt;Building for the future&lt;/h2&gt;
&lt;p&gt;Moving to a message-broker-first mindset is the first step in the journey &lt;a href=&quot;https://ansezz.com/blog/monolith-to-microservices/&quot;&gt;from a monolith to microservices&lt;/a&gt;. Once your monolith starts publishing &quot;events&quot; (like &lt;code&gt;order.placed&lt;/code&gt; or &lt;code&gt;user.registered&lt;/code&gt;), other services can start listening to those events without you ever changing the original code.&lt;/p&gt;
&lt;p&gt;It creates a system that is resilient, observable, and significantly easier to debug. You can look at the RabbitMQ management UI and see exactly how many tasks are pending and how fast they are being processed. No more guessing why the server is slow.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/scaling-with-rabbitmq/monitoring-dashboard.webp&quot; alt=&quot;Monitoring dashboard mockup of RabbitMQ queue depth and throughput&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Already running on GCP? The same patterns apply with &lt;a href=&quot;https://ansezz.com/blog/event-driven-pubsub/&quot;&gt;Google Pub/Sub&lt;/a&gt; — pick the broker that matches your hosting stack, not the trend cycle.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Key takeaways for your next build&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Don&apos;t block the user.&lt;/strong&gt; If a task takes more than 100ms, it probably belongs in a queue.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Decouple early.&lt;/strong&gt; Use RabbitMQ to separate your &quot;thinking&quot; (web tier) from your &quot;doing&quot; (workers).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Idempotency is key.&lt;/strong&gt; Since messages can sometimes be delivered twice, make sure your workers can handle the same task more than once without causing errors.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Monitor your queues.&lt;/strong&gt; A massive queue is a leading indicator that you need more workers or that a service is failing.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Scaling a SaaS isn&apos;t about working harder. It is about working smarter by giving your data room to breathe. RabbitMQ is that breathing room.&lt;/p&gt;
&lt;p&gt;What is the slowest part of your application right now? Could it be a background job instead? &lt;a href=&quot;https://ansezz.com/contact/&quot;&gt;Tell me&lt;/a&gt; — I bet we can move it off the request path.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/scaling-with-rabbitmq/final-visual.webp&quot; alt=&quot;Pop-art final visual of a calm web tier while workers chew through background jobs&quot; /&gt;&lt;/p&gt;
</content:encoded><category>architecture</category><category>messaging</category><category>microservices</category><category>scaling</category><category>laravel</category><category>architecture</category></item><item><title>Event-driven architecture with Google Pub/Sub</title><link>https://ansezz.com/blog/event-driven-pubsub/</link><guid isPermaLink="true">https://ansezz.com/blog/event-driven-pubsub/</guid><description>Decouple your services or drown in latency. Topics, fan-out, push vs pull, dead-letter queues, and idempotent consumers in a Laravel Pub/Sub blueprint.</description><pubDate>Sat, 02 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Building a modern web application usually starts simple. You have a request and you send a response. But as your business grows, that simple flow starts to feel heavy. Maybe you need to send a welcome email, update a CRM, and trigger a data warehouse sync all at once. If you do this synchronously, your users are stuck staring at a loading spinner. If one service fails, the whole request dies. Your system becomes a house of cards. An event-driven architecture breaks that chain, and Google Pub/Sub is one of the cleanest ways to build it.&lt;/p&gt;
&lt;p&gt;This is the problem of tight coupling. Your application logic is tangled like old headphones in a pocket. Every new feature adds more risk and more latency. You want to scale, but your monolithic approach is holding you back. You need a way to let your services talk without being glued together.&lt;/p&gt;
&lt;p&gt;The solution is &lt;strong&gt;event-driven architecture&lt;/strong&gt; (EDA). And in the Google Cloud world, the heart of that architecture is &lt;strong&gt;Google Pub/Sub&lt;/strong&gt;. It is a globally distributed messaging service that decouples the services that produce events from the services that consume them. It allows you to build systems that are truly scalable, resilient, and ready for the future of AI and big data. If you&apos;d rather self-host the broker, the same patterns translate directly to &lt;a href=&quot;https://ansezz.com/blog/scaling-with-rabbitmq/&quot;&gt;scaling with RabbitMQ&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Understanding topics and subscriptions&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/event-driven-pubsub/architecture-diagram.webp&quot; alt=&quot;Architecture diagram of a Pub/Sub topic with multiple subscriptions fanning out&quot; /&gt;&lt;/p&gt;
&lt;p&gt;At its core, Google Pub/Sub is built on two main concepts: &lt;strong&gt;topics&lt;/strong&gt; and &lt;strong&gt;subscriptions&lt;/strong&gt;. I like to think of a topic as a radio station. It broadcasts information out into the void. It doesn&apos;t care who is listening or what they do with the music. It just plays the hits.&lt;/p&gt;
&lt;p&gt;On the other side, you have subscriptions. These are the listeners. A subscription represents a stream of messages from a specific topic. The beauty of this system is the decoupling. The service sending the message (the publisher) only needs to know about the topic. It doesn&apos;t need to know if there are ten consumers or zero.&lt;/p&gt;
&lt;p&gt;This is the heart of the &lt;a href=&quot;https://ansezz.com/blog/synchronous-vs-asynchronous-communication/&quot;&gt;shift from synchronous to asynchronous communication&lt;/a&gt;. When a user signs up on your site, you publish a &lt;code&gt;UserSignedUp&lt;/code&gt; event to a topic. Your main app is done. It returns a success message to the user immediately. Meanwhile, various subscribers pick up that event and do their jobs in the background.&lt;/p&gt;
&lt;h2&gt;The power of fan-out&lt;/h2&gt;
&lt;p&gt;One of the most effective patterns in Google Pub/Sub is the fan-out. This is where you publish a single message to a topic, but multiple subscriptions receive a copy of that message.&lt;/p&gt;
&lt;p&gt;Imagine you are running an e-commerce store. When an order is placed, you might have three different services that need to act:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;An inventory service to update stock levels.&lt;/li&gt;
&lt;li&gt;A shipping service to generate a label.&lt;/li&gt;
&lt;li&gt;An analytics service to track revenue.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Instead of your checkout service calling three different APIs, it sends one message to an &lt;code&gt;order-events&lt;/code&gt; topic. Three separate subscriptions (one for inventory, one for shipping, one for analytics) each get their own copy of that order message. They process it at their own pace. If the analytics service is down for maintenance, it doesn&apos;t stop the shipping label from being created. The messages just wait in the queue until the service is back online.&lt;/p&gt;
&lt;h2&gt;Pull vs push delivery&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/event-driven-pubsub/dashboard-mockup.webp&quot; alt=&quot;Dashboard mockup comparing pull and push Pub/Sub subscription delivery metrics side by side&quot; /&gt;&lt;/p&gt;
&lt;p&gt;When you set up a subscription, you have to decide how you want to receive messages. Google Pub/Sub gives you two main options: &lt;strong&gt;pull&lt;/strong&gt; and &lt;strong&gt;push&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Push subscriptions&lt;/strong&gt; are great for serverless architectures. Google Cloud will literally &quot;push&quot; the message to a webhook URL you provide. This is perfect for &lt;a href=&quot;https://ansezz.com/work/&quot;&gt;cloud infrastructure&lt;/a&gt; built on Cloud Run or Cloud Functions. It scales automatically and you only pay for what you use. However, you have to make sure your endpoint can handle the sudden spikes in traffic.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Pull subscriptions&lt;/strong&gt; work differently. Your consumer service asks Google Pub/Sub for messages when it is ready. This gives you much more control over backpressure. If your worker is busy, it doesn&apos;t ask for more work. This is the preferred method for long-running services or when you are using tools like Laravel&apos;s queue workers. Pull delivery is generally more robust for heavy processing tasks where you want to fine-tune concurrency — exactly the model I use for &lt;a href=&quot;https://ansezz.com/blog/message-queues-document-processing/&quot;&gt;message queues in document processing&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Building resilient systems with DLQs&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/event-driven-pubsub/dlq-illustration.webp&quot; alt=&quot;Pop-art illustration of a dead-letter queue catching failed poison messages routed off the main pipeline&quot; /&gt;&lt;/p&gt;
&lt;p&gt;In a distributed system, things will fail. A database might time out or an external API might be down. If a message can&apos;t be processed, you don&apos;t want to lose it. This is where &lt;strong&gt;Dead Letter Queues&lt;/strong&gt; (DLQs) come in.&lt;/p&gt;
&lt;p&gt;A DLQ is just another topic where Google Pub/Sub sends messages that have failed to be acknowledged after a certain number of attempts. Instead of retrying forever and clogging up your main pipeline, the &quot;poison&quot; message is moved aside.&lt;/p&gt;
&lt;p&gt;I always recommend setting up a DLQ for every critical subscription. It acts as a safety net. You can then build a separate dashboard or a small script to inspect these failed messages, fix the underlying issue, and replay them. It is a professional approach to error handling that prevents data loss and keeps your system moving.&lt;/p&gt;
&lt;h2&gt;Integrating Google Pub/Sub with Laravel&lt;/h2&gt;
&lt;p&gt;For those of us in the PHP and Laravel ecosystem, integrating Google Pub/Sub is smooth. Laravel ships with solid queue support for Redis and SQS, but the &lt;code&gt;google/cloud-pubsub&lt;/code&gt; client lets you tap into GCP&apos;s global scale. This kind of decoupling is exactly what makes the &lt;a href=&quot;https://ansezz.com/blog/monolith-to-microservices/&quot;&gt;move from a monolith to microservices&lt;/a&gt; tractable instead of terrifying.&lt;/p&gt;
&lt;p&gt;You can treat Google Pub/Sub as a custom queue driver. Here is a quick look at how you might publish a message in a typical service class:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;use Google\Cloud\PubSub\PubSubClient;

$pubsub = new PubSubClient([
    &apos;projectId&apos; =&amp;gt; &apos;your-gcp-project-id&apos;,
]);

$topic = $pubsub-&amp;gt;topic(&apos;user-events&apos;);

$topic-&amp;gt;publish([
    &apos;data&apos; =&amp;gt; json_encode([
        &apos;user_id&apos; =&amp;gt; 123,
        &apos;action&apos; =&amp;gt; &apos;signup&apos;,
    ]),
    &apos;attributes&apos; =&amp;gt; [
        &apos;event_type&apos; =&amp;gt; &apos;UserSignedUp&apos;,
        &apos;priority&apos; =&amp;gt; &apos;high&apos;,
    ],
]);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;By using attributes, you can even filter messages at the subscription level. This means a subscriber can choose to only listen for messages where &lt;code&gt;event_type&lt;/code&gt; is &lt;code&gt;UserSignedUp&lt;/code&gt;. This saves compute power and money because your worker never even sees the messages it doesn&apos;t care about.&lt;/p&gt;
&lt;h2&gt;Monitoring and cost management&lt;/h2&gt;
&lt;p&gt;Monitoring is not an afterthought. It is a requirement. Google Cloud provides deep integration with Cloud Monitoring for Google Pub/Sub. You should keep a close eye on your &quot;unacked message count.&quot; If this number is climbing, it means your subscribers can&apos;t keep up with the producers.&lt;/p&gt;
&lt;p&gt;Cost is another factor to watch. Google Pub/Sub is cheap for low volumes, but as you scale to millions of messages, those bytes add up. Use batching on the publisher side to reduce the number of API calls. Also be mindful of message retention: the default is seven days, and you can configure anywhere from 10 minutes up to 31 days. If you don&apos;t need a long replay window, shorten the retention period to cut storage costs.&lt;/p&gt;
&lt;h2&gt;Wrap up and takeaways&lt;/h2&gt;
&lt;p&gt;Moving to an event-driven architecture with Google Pub/Sub is a major step toward building senior-level systems. It gives you the flexibility to grow your application without it becoming a tangled mess. It is the backbone of many high-performance systems I build for clients today.&lt;/p&gt;
&lt;p&gt;Here are the key takeaways for your next project:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Start by identifying &quot;facts&quot;&lt;/strong&gt; in your system (e.g., &lt;code&gt;OrderPlaced&lt;/code&gt;) and turn them into events.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Use the fan-out pattern&lt;/strong&gt; to keep your services decoupled and focused on one task.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Always implement a Dead Letter Queue&lt;/strong&gt; to handle failures gracefully.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Use message attributes for efficient filtering&lt;/strong&gt; at the subscription level.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Design your consumers to be idempotent.&lt;/strong&gt; If they receive the same message twice, it doesn&apos;t cause errors or double-charges.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Building these kinds of systems takes a bit more planning upfront, but the payoff in stability and scalability is worth every second.&lt;/p&gt;
&lt;p&gt;Are you still using synchronous API calls for everything, or have you started moving toward an event-driven flow? &lt;a href=&quot;https://ansezz.com/contact/&quot;&gt;Let me know what&apos;s stopping you&lt;/a&gt; from making the switch.&lt;/p&gt;
</content:encoded><category>architecture</category><category>messaging</category><category>cloud-platforms</category><category>laravel</category><category>scaling</category><category>architecture</category></item><item><title>Self-hosted SaaS with Coolify and Docker</title><link>https://ansezz.com/blog/coolify-docker-saas-hosting/</link><guid isPermaLink="true">https://ansezz.com/blog/coolify-docker-saas-hosting/</guid><description>Get Heroku-grade DX on your own server. How Coolify and Docker on a $5 VPS deliver one-click databases, automatic SSL, and zero-downtime deploys.</description><pubDate>Sun, 19 Apr 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Shipping a SaaS is hard enough without the constant anxiety of a &quot;surprise&quot; bill from your hosting provider. I have seen developers start a project on a managed platform only to find that as soon as they add a second team member or cross a certain bandwidth threshold, their costs skyrocket. You are trapped between paying a &quot;convenience tax&quot; that eats your margins or spending your entire weekend fighting with Nginx configurations and manual SSH commands. It feels like you are either overpaying for simplicity or overworking for control.&lt;/p&gt;
&lt;p&gt;The agitation grows when you realize that most managed platforms are essentially wrappers around the same open source tools you could run yourself. You are paying for a pretty dashboard and an easy git-push flow. But when you try to leave, you find yourself deep in vendor lock-in. Your databases, your environment variables, and your build pipelines are all tied to a proprietary ecosystem. If the platform goes down or changes its pricing, your business is at the mercy of their support team.&lt;/p&gt;
&lt;p&gt;There is a better way. &lt;strong&gt;Coolify&lt;/strong&gt; combined with &lt;strong&gt;Docker&lt;/strong&gt; gives you the same developer experience as high-end managed platforms, but on your own infrastructure. You get the one-click deploy feel and a clean dashboard while keeping full control over your servers. If you want the business case first, I make it in &lt;a href=&quot;https://ansezz.com/blog/coolify-self-hosted-saas/&quot;&gt;why I ditch expensive cloud providers for self-hosted SaaS&lt;/a&gt;; this guide is the hands-on build.&lt;/p&gt;
&lt;h2&gt;Why the cloud is getting more expensive&lt;/h2&gt;
&lt;p&gt;In the early days of a startup, a $20-per-month plan feels reasonable. But as you grow, those costs don&apos;t just add up — they multiply. Many platforms now charge per seat. If you have a team of five engineers, you might be paying a hundred dollars a month before you even deploy a single line of code. Then come the usage fees. Bandwidth, image optimization, and serverless function execution costs are often opaque and difficult to predict.&lt;/p&gt;
&lt;p&gt;I have worked with clients who moved their entire stack from managed services to a self-hosted Coolify setup and saw their monthly infrastructure bill drop by eighty percent. We are talking about moving from $500 a month down to $50 a month while maintaining the same performance and reliability. When you own the server, you own the resources. There are no &quot;hidden&quot; charges for extra build minutes or database connections.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/coolify-docker-saas-hosting/coolify-architecture.webp&quot; alt=&quot;Diagram of a Coolify control plane managing app and database nodes across servers&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;What exactly is Coolify?&lt;/h2&gt;
&lt;p&gt;Think of Coolify as an open source, self-hosted version of Heroku or Vercel. It is a control plane that sits on your server and manages everything for you. It handles your deployments, your reverse proxies, your SSL certificates, and your databases. It turns a raw Linux VPS into a powerful hosting platform.&lt;/p&gt;
&lt;p&gt;One of the best parts about Coolify is that it is built on Docker. Every application you deploy is containerized. This means your environment is consistent across development, staging, and production. No more &quot;it works on my machine&quot; excuses. If it runs in a container on your laptop, it will run exactly the same way on your Coolify server.&lt;/p&gt;
&lt;h2&gt;The power of Docker containerization&lt;/h2&gt;
&lt;p&gt;Docker is the silent engine that makes this entire workflow possible. Instead of installing PHP, Node.js, or Python directly on your server, you package them into a container image. This approach has several key benefits for SaaS founders:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Isolation.&lt;/strong&gt; Each app runs in its own sandbox. A memory leak in one app won&apos;t crash your entire server.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Portability.&lt;/strong&gt; You can move your containers from Hetzner to DigitalOcean to AWS in minutes.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Version control.&lt;/strong&gt; Your infrastructure is defined as code. You can version your Dockerfile just like your application code.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Scalability.&lt;/strong&gt; Adding more instances of your app is as simple as spinning up another container.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I prioritize Docker on every build I ship. It makes the handoff to the client seamless: they don&apos;t need to worry about the underlying server configuration, just a Docker-compatible environment. Coolify deliberately stays at the single-host Docker layer rather than orchestrating clusters — if you&apos;re weighing that trade-off, see &lt;a href=&quot;https://ansezz.com/blog/docker-vs-kubernetes/&quot;&gt;Docker vs Kubernetes: containers vs orchestration&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/coolify-docker-saas-hosting/docker-rack.webp&quot; alt=&quot;Pop-art rack of isolated Docker containers stacked in a modular grid&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Setting up your control plane&lt;/h2&gt;
&lt;p&gt;Getting started is surprisingly simple. Coolify&apos;s documented minimum is 2 CPU cores, 2 GB of RAM, and 30 GB of storage — though I&apos;d budget 4 GB if you plan to run more than a couple of apps alongside it. I typically recommend Ubuntu for the operating system. Once you have your server, you run the single installation command from the Coolify documentation.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# the only command you need to bootstrap Coolify
curl -fsSL https://cdn.coollabs.io/coolify/install.sh | sudo bash
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This script takes care of installing Docker, setting up the Traefik reverse proxy, and launching the Coolify dashboard. Within minutes, you can log in to your own private hosting panel. From there, you can connect your GitHub or GitLab account. Coolify will listen for webhooks and automatically trigger a new build whenever you push code to your main branch.&lt;/p&gt;
&lt;p&gt;It feels magical. You get the same feedback loop as the big-name platforms. You see the build logs in real time. You get a preview URL for your feature branches. And you do it all on a $5 VPS.&lt;/p&gt;
&lt;h2&gt;Handling databases and state&lt;/h2&gt;
&lt;p&gt;One of the biggest pain points of self-hosting is managing databases. Nobody wants to manually configure Postgres clusters or worry about backing up Redis instances. Coolify solves this by offering &quot;one-click&quot; services.&lt;/p&gt;
&lt;p&gt;You can spin up a Postgres, MySQL, MongoDB, or Redis instance in seconds. Coolify automatically generates secure credentials and provides you with the connection strings. It also handles persistent volumes, so even if your container restarts or you update the image, your data stays safe. Persistent volumes are not a backup strategy, though — schedule automated dumps and keep them off-box, because &lt;a href=&quot;https://ansezz.com/blog/replication-vs-backup-laravel/&quot;&gt;replication and backup solve different failure modes&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;For a SaaS, I usually recommend a dedicated server for your databases once traffic climbs. Coolify makes this easy because it supports multi-server setups. You can have one server acting as your control plane and several other servers acting as worker nodes where your apps and databases actually live. (I cover that scaling pattern in more depth in &lt;a href=&quot;https://ansezz.com/blog/scaling-with-coolify/&quot;&gt;scaling SaaS with advanced Coolify deployment strategies&lt;/a&gt;.)&lt;/p&gt;
&lt;h2&gt;Security and SSL by default&lt;/h2&gt;
&lt;p&gt;Security shouldn&apos;t be an afterthought. In the old days, setting up SSL with Let&apos;s Encrypt required cron jobs and manual certificates. With Coolify and Traefik, it is entirely automated.&lt;/p&gt;
&lt;p&gt;When you point a domain to your server and add it to your app configuration, Coolify automatically requests and installs an SSL certificate. It also handles the renewal process. Your SaaS is always served over HTTPS without you ever touching a terminal.&lt;/p&gt;
&lt;p&gt;Beyond encryption, Coolify helps you manage your environment variables securely. You don&apos;t need to hardcode secrets in your git repository. You can define them in the dashboard, and they are injected into your containers at runtime. This is a standard best practice that many developers skip when they are in a rush.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/coolify-docker-saas-hosting/ssl-security.webp&quot; alt=&quot;Pop-art illustration of automatic Let&apos;s Encrypt SSL certificates and runtime-injected secrets&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;The senior engineer&apos;s workflow&lt;/h2&gt;
&lt;p&gt;If you want to do this the &quot;pro&quot; way, here is how I structure my deployments.&lt;/p&gt;
&lt;h3&gt;Use a Dockerfile&lt;/h3&gt;
&lt;p&gt;While Coolify can automatically detect many frameworks like Laravel or Node.js, I always recommend writing your own Dockerfile. It gives you total control over the build process. You can optimize your image size by using multi-stage builds. This makes your deployments faster and saves disk space.&lt;/p&gt;
&lt;h3&gt;Leverage Nixpacks&lt;/h3&gt;
&lt;p&gt;If you don&apos;t want to write a Dockerfile, Coolify supports Nixpacks. It is a tool built by Railway that inspects your code and builds an optimized container image automatically, with detection for 20-plus languages. Railway itself has since moved to a successor called Railpack, but Nixpacks remains a solid zero-config option inside Coolify.&lt;/p&gt;
&lt;h3&gt;Set up health checks&lt;/h3&gt;
&lt;p&gt;Never deploy without a health check. You want to make sure your app is actually responding before the reverse proxy starts sending traffic to it. Coolify allows you to define a health check endpoint. If the check fails, the old container stays running, and the new one isn&apos;t promoted. This is the foundation of zero-downtime deployments.&lt;/p&gt;
&lt;h2&gt;Is self-hosting right for you?&lt;/h2&gt;
&lt;p&gt;Self-hosting isn&apos;t for everyone. If you are a solo developer with zero interest in learning how a server works, then paying the &quot;convenience tax&quot; might be worth it. Your time is valuable, and if a managed platform saves you five hours of frustration a month, it might pay for itself.&lt;/p&gt;
&lt;p&gt;However, if you are building a real business, you need to understand your stack. Owning your infrastructure is about more than just saving money. It is about autonomy. It is about knowing that no matter what happens to a specific provider, you can move your business elsewhere in a heartbeat.&lt;/p&gt;
&lt;p&gt;At &lt;a href=&quot;https://ansezz.com/about/&quot;&gt;Ansezz&lt;/a&gt;, I focus on building robust, scalable systems that empower clients. Whether the work is a complex e-commerce engine on Shopify or a custom Laravel application, the goal is always the same: high performance and long-term stability.&lt;/p&gt;
&lt;h2&gt;Final takeaways for your deployment strategy&lt;/h2&gt;
&lt;p&gt;Hosting shouldn&apos;t be a source of stress. By moving to a Docker-based workflow with Coolify, you reclaim your time and your budget. You get the professional features of a top-tier PaaS without the enterprise price tag.&lt;/p&gt;
&lt;p&gt;Here is your checklist for a successful transition:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Start with a clean VPS&lt;/strong&gt; and install Coolify using the official script.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Containerize your application&lt;/strong&gt; using a Dockerfile for maximum control.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Use one-click services&lt;/strong&gt; for your databases and enable automatic backups.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Set up your domain&lt;/strong&gt; and let Coolify handle the SSL certificates.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Implement health checks&lt;/strong&gt; to ensure zero-downtime updates.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;When you stop worrying about the &quot;how&quot; of deployment, you can spend more time on the &quot;what&quot; of your product. That is where the real value is created.&lt;/p&gt;
&lt;p&gt;What is the one thing stopping you from moving your SaaS to a self-hosted setup today? &lt;a href=&quot;https://ansezz.com/contact/&quot;&gt;Tell me about it&lt;/a&gt; — happy to share war stories.&lt;/p&gt;
</content:encoded><category>devops</category><category>coolify</category><category>docker</category><category>self-hosting</category><category>devops</category><category>multi-tenancy</category><category>deployment</category><category>networking</category></item><item><title>MCP tool-use: building context-aware agents</title><link>https://ansezz.com/blog/mcp-context-aware-agents/</link><guid isPermaLink="true">https://ansezz.com/blog/mcp-context-aware-agents/</guid><description>Build context-aware agents with MCP. How tools, resources, and prompts let one server talk to any client — and kill brittle one-off integrations for good.</description><pubDate>Sun, 05 Apr 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Building context-aware agents is harder than it should be, because the current state of AI tooling is a mess of fragmented integrations. Every time I want to give an LLM access to a new data source or a specific tool I find myself writing custom glue code that breaks the moment an API version changes. It is a frustrating cycle of brittle wrappers. We are effectively forcing highly intelligent models to peer through a keyhole when they should have a wide-open window into our data ecosystems.&lt;/p&gt;
&lt;p&gt;This fragmentation creates massive technical debt. You spend most of your time on plumbing and only a sliver on the actual intelligence of the agent. Without a unified way to share context the model often hallucinates because it lacks the grounding of real-time data. It is stuck in a loop of &quot;I don&apos;t have access to that&quot; or worse &quot;I&apos;ll guess what that data looks like&quot; — which leads to unreliable outputs and a poor user experience.&lt;/p&gt;
&lt;p&gt;The &lt;strong&gt;Model Context Protocol&lt;/strong&gt; (MCP) changes this dynamic entirely. It is an open standard, created by Anthropic and donated in late 2025 to the Agentic AI Foundation under the Linux Foundation, that lets me build context-aware agents that connect to any data source using a universal language. By standardizing how servers and clients communicate I can focus on building sophisticated logic rather than managing endless API endpoints. If you want the bigger picture on how this fits the wider toolbox, I broke down &lt;a href=&quot;https://ansezz.com/blog/api-vs-mcp/&quot;&gt;API vs MCP&lt;/a&gt; separately — MCP is the missing link in the agentic workflow.&lt;/p&gt;
&lt;h2&gt;Why MCP matters for developers&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/mcp-context-aware-agents/architecture-diagram.webp&quot; alt=&quot;MCP architecture diagram: a single AI client connecting through MCP servers to databases, APIs, and files&quot; /&gt;&lt;/p&gt;
&lt;p&gt;I have spent years building custom web applications and one of the biggest hurdles has always been data silos. When I work on &lt;a href=&quot;https://ansezz.com/work/&quot;&gt;complex technical challenges&lt;/a&gt; the goal is usually to make data actionable. Traditional tool-use requires the developer to define every schema and every function call manually for the model. MCP flips this script.&lt;/p&gt;
&lt;p&gt;MCP acts as a bridge. It defines a clear boundary between the AI application (the client) and the data sources (the servers). This separation of concerns means I can swap out the underlying model without rebuilding the entire data integration layer. If I move from Claude to another model that supports MCP, the tools and resources remain the same.&lt;/p&gt;
&lt;p&gt;It also eases the &lt;a href=&quot;https://ansezz.com/blog/context-window-vs-memory/&quot;&gt;context window&lt;/a&gt; problem. Instead of stuffing a massive document into the prompt I can expose it as an MCP resource. The model only pulls what it needs when it needs it. This is significantly more efficient and cost-effective. It lets me build agents that are aware of their environment without being overwhelmed by it.&lt;/p&gt;
&lt;h2&gt;The three pillars: tools, resources, and prompts&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/mcp-context-aware-agents/context-visual.webp&quot; alt=&quot;Visual breakdown of the three MCP primitives: model-controlled tools, application-controlled resources, and user-controlled prompts&quot; /&gt;&lt;/p&gt;
&lt;p&gt;To understand how to build with MCP I look at its three core primitives. These are the building blocks for any context-aware system.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Tools&lt;/strong&gt; are model-controlled actions. When I give an agent a tool I am giving it the ability to change the world. This could be writing a file to a disk or making a POST request to a Shopify API. The model decides when to call the tool based on the user&apos;s intent.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Resources&lt;/strong&gt; are application-controlled data. Think of these as read-only files or database entries that the agent can inspect. Resources provide the necessary grounding. If I am building a support agent the documentation for the product would be a resource. The agent can search and read it to provide accurate answers.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Prompts&lt;/strong&gt; are user-controlled templates. They help guide the interaction. By using MCP prompts I can standardize how users interact with the agent across different platforms. It ensures consistency in how the model interprets tasks.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Building your first MCP server&lt;/h2&gt;
&lt;p&gt;I prefer using TypeScript for building MCP servers because of the mature official SDK. However the protocol itself is language-agnostic, with first-party SDKs for Python, Java, Go, and more. Here is a simplified look at how I structure a basic server that exposes a weather tool. This uses the low-level &lt;code&gt;Server&lt;/code&gt; API to make the request handlers explicit; the higher-level &lt;code&gt;McpServer&lt;/code&gt; class wraps the same boilerplate if you want less ceremony.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import { Server } from &quot;@modelcontextprotocol/sdk/server/index.js&quot;;
import { StdioServerTransport } from &quot;@modelcontextprotocol/sdk/server/stdio.js&quot;;
import {
  CallToolRequestSchema,
  ListToolsRequestSchema,
} from &quot;@modelcontextprotocol/sdk/types.js&quot;;

const server = new Server(
  {
    name: &quot;weather-server&quot;,
    version: &quot;1.0.0&quot;,
  },
  {
    capabilities: {
      tools: {},
    },
  },
);

server.setRequestHandler(ListToolsRequestSchema, async () =&amp;gt; {
  return {
    tools: [
      {
        name: &quot;get_weather&quot;,
        description: &quot;get the current weather for a location&quot;,
        inputSchema: {
          type: &quot;object&quot;,
          properties: {
            location: { type: &quot;string&quot; },
          },
          required: [&quot;location&quot;],
        },
      },
    ],
  };
});

server.setRequestHandler(CallToolRequestSchema, async (request) =&amp;gt; {
  if (request.params.name === &quot;get_weather&quot;) {
    const location = request.params.arguments?.location;
    // logic to fetch weather from an api goes here
    return {
      content: [{ type: &quot;text&quot;, text: `it is sunny in ${location}` }],
    };
  }
  throw new Error(&quot;tool not found&quot;);
});

const transport = new StdioServerTransport();
await server.connect(transport);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This snippet illustrates the simplicity of the protocol. I define the tool and how to handle the call. The MCP client handles the rest. This modular approach is exactly what I look for when managing &lt;a href=&quot;https://ansezz.com/work/&quot;&gt;cloud infrastructure&lt;/a&gt; or complex backend systems. It is clean and scalable.&lt;/p&gt;
&lt;h2&gt;Security and the MCP ecosystem&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/mcp-context-aware-agents/dashboard-mockup.webp&quot; alt=&quot;Mockup of an MCP server admin dashboard showing fine-grained, per-tool access permissions&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Security is a major concern when giving an AI agent access to your data. I have seen many implementations where API keys are hardcoded or permissions are too broad. MCP addresses this by using a client-server architecture where the server controls exactly what is exposed.&lt;/p&gt;
&lt;p&gt;The server acts as a gatekeeper. I can implement fine-grained access control at the server level. For example an MCP server connecting to a database can be restricted to only specific tables or read-only queries. This level of control is essential for enterprise-grade applications.&lt;/p&gt;
&lt;p&gt;The ecosystem has matured fast. MCP has gone from an Anthropic experiment to a genuine industry standard, with OpenAI, Google, and Microsoft all backing it. On the tooling side, clients like Zed, Cursor, VS Code, and Claude Code ship MCP support so AI assistants write better code with real context. If you want a hands-on walkthrough, I wrote up &lt;a href=&quot;https://ansezz.com/blog/claude-mcp-dev-tools/&quot;&gt;connecting my own dev tools through Claude MCP&lt;/a&gt; separately.&lt;/p&gt;
&lt;h2&gt;Practical steps for getting started&lt;/h2&gt;
&lt;p&gt;If you are a developer looking to dive into MCP I recommend following these steps.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Explore the existing MCP servers&lt;/strong&gt; on GitHub. The reference repo ships servers for filesystem access, Git, web fetching, and persistent memory. See how they are structured.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Pick a simple data source you use every day.&lt;/strong&gt; It could be your Obsidian notes or a local directory of markdown files. Build a basic server to expose these as resources.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Use a client like Claude Desktop&lt;/strong&gt; to test your server. See how the model interacts with your data. Adjust the tool descriptions to make them more intuitive for the AI.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Compose multiple MCP servers&lt;/strong&gt; once you are comfortable. Imagine an agent that can read your calendar and then write a draft email based on your upcoming meetings.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Add the coordination layer&lt;/strong&gt; when one agent is no longer enough. MCP connects an agent to its tools; A2A connects it to other agents. The &lt;a href=&quot;https://ansezz.com/blog/mcp-vs-a2a-vs-acp/&quot;&gt;agent protocol stack&lt;/a&gt; breaks down where each one belongs.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;MCP is more than just a new protocol. It is a shift in how we build AI applications, moving us away from &quot;black box&quot; agents and toward transparent, context-aware assistants you can actually reason about in production.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;How are you planning to use MCP in your next project?&lt;/strong&gt; &lt;a href=&quot;https://ansezz.com/contact/&quot;&gt;Drop me a line&lt;/a&gt; — happy to swap notes on real-world MCP server design.&lt;/p&gt;
</content:encoded><category>ai</category><category>mcp</category><category>claude</category><category>agentic-ai</category><category>llm</category></item><item><title>Vibe coding: the shift to agentic workflows</title><link>https://ansezz.com/blog/agentic-workflows-vibe-coding/</link><guid isPermaLink="true">https://ansezz.com/blog/agentic-workflows-vibe-coding/</guid><description>MCP, agentic loops, and intent-based engineering. How vibe coding becomes a real architecture pattern, plus the Laravel and MCP stack I run today.</description><pubDate>Sun, 22 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I&apos;ve spent the last decade building Laravel applications, managing Docker clusters, and fine-tuning Shopify stores. For most of that time, &quot;coding&quot; meant one thing: translating a business requirement into a specific syntax that a machine could execute. It was a manual, linear process of writing line by line, debugging stack traces, and managing state.&lt;/p&gt;
&lt;p&gt;But recently, the ground has shifted. We&apos;re moving away from the era of &quot;writing code&quot; and into the era of &quot;orchestrating intent.&quot;&lt;/p&gt;
&lt;p&gt;This transition — often playfully called &lt;strong&gt;vibe coding&lt;/strong&gt; — is more than just a meme. It represents a fundamental architectural shift in how we build software: moving from sequential instruction to &lt;strong&gt;agentic workflows&lt;/strong&gt; powered by protocols like &lt;strong&gt;MCP&lt;/strong&gt; (Model Context Protocol). If you are new to the idea, my &lt;a href=&quot;https://ansezz.com/blog/vibe-coding/&quot;&gt;intro to vibe coding&lt;/a&gt; covers the philosophy and developer-experience side before this post dives into the architecture.&lt;/p&gt;
&lt;h2&gt;The friction of the manual syntax&lt;/h2&gt;
&lt;p&gt;The traditional development lifecycle is riddled with invisible friction. You have an idea (the &quot;vibe&quot;), you break it down into tasks, and then you spend 80% of your time fighting with syntax, configuration, and boilerplate.&lt;/p&gt;
&lt;p&gt;In a standard &lt;strong&gt;Laravel&lt;/strong&gt; environment, even a simple feature — say, an automated reporting tool — requires you to set up routes, controllers, service classes, and database migrations. You are the compiler. You are the architect. You are the labor.&lt;/p&gt;
&lt;p&gt;The problem is that our cognitive load gets consumed by the &quot;how&quot; rather than the &quot;what.&quot; We get stuck in the weeds of &lt;strong&gt;PHP&lt;/strong&gt; version compatibility or &lt;strong&gt;Docker&lt;/strong&gt; networking issues, losing sight of the actual user value. This manual micromanagement doesn&apos;t scale with the demands of modern business.&lt;/p&gt;
&lt;h2&gt;The agitation of the &quot;black box&quot; assistant&lt;/h2&gt;
&lt;p&gt;When AI first entered the scene with basic autocomplete, it felt like a shortcut. But it wasn&apos;t a solution. We ended up with what I call &quot;the Copilot paradox&quot;: the AI suggests code, but you still have to copy-paste it, test it, find the error, and feed it back to the AI.&lt;/p&gt;
&lt;p&gt;It&apos;s a broken feedback loop. The AI is a &quot;black box&quot; that doesn&apos;t actually know your system. It doesn&apos;t know your database schema, your &lt;strong&gt;MCP&lt;/strong&gt; servers, or your deployment status on &lt;strong&gt;Coolify&lt;/strong&gt;. You are still the manual bridge between the AI&apos;s logic and your local environment.&lt;/p&gt;
&lt;p&gt;This creates a new kind of fatigue. Instead of writing code, you&apos;re now a high-speed code reviewer, constantly context-switching between your editor and a chat interface. This isn&apos;t &quot;vibe coding&quot; — it&apos;s just accelerated manual labor.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/agentic-workflows-vibe-coding/architecture-diagram.webp&quot; alt=&quot;Diagram of the broken feedback loop where a developer manually bridges an AI assistant and their tools&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;The solution: agentic workflows and MCP&lt;/h2&gt;
&lt;p&gt;True &lt;strong&gt;vibe coding&lt;/strong&gt; isn&apos;t about being lazy; it&apos;s about shifting your role to that of a high-level system architect. This becomes possible through &lt;strong&gt;agentic workflows&lt;/strong&gt; — systems that don&apos;t just complete text but execute tasks in loops. That jump from a static model to an autonomous actor is exactly the &lt;a href=&quot;https://ansezz.com/blog/llm-vs-ai-agent/&quot;&gt;LLM vs AI agent&lt;/a&gt; distinction.&lt;/p&gt;
&lt;p&gt;The breakthrough here is the &lt;strong&gt;Model Context Protocol (MCP)&lt;/strong&gt; by Anthropic. MCP acts as the &quot;USB-C port&quot; for AI applications. Instead of you manually giving the AI context, the AI uses an MCP client to talk directly to your tools — your &lt;strong&gt;PostgreSQL&lt;/strong&gt; database, your &lt;strong&gt;Slack&lt;/strong&gt; channels, or your &lt;strong&gt;GitHub&lt;/strong&gt; repositories.&lt;/p&gt;
&lt;h3&gt;The shift from chains to loops&lt;/h3&gt;
&lt;p&gt;In a traditional chain, you give a prompt and get a result. In an agentic loop, the architecture looks like this:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Intent.&lt;/strong&gt; You describe the outcome (&quot;build a Laravel dashboard for my Shopify sales&quot;).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Reasoning.&lt;/strong&gt; The AI (like &lt;strong&gt;Claude&lt;/strong&gt;) determines it needs to see the schema.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Action.&lt;/strong&gt; It uses an &lt;strong&gt;MCP&lt;/strong&gt; tool to query the database.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Observation.&lt;/strong&gt; It sees a missing table and decides to create a migration.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Correction.&lt;/strong&gt; If the migration fails, it reads the error and fixes it itself.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I call this &quot;intent-based engineering.&quot; You aren&apos;t writing the migration — you are approving the architectural decision. Nobody lands here on day one, though: the jump from chat prompts to loops is a progression I broke into &lt;a href=&quot;https://ansezz.com/blog/ai-coding-workflow-levels/&quot;&gt;levels of an AI coding workflow&lt;/a&gt;, and MCP sits near the top of it.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/agentic-workflows-vibe-coding/agentic-loop.webp&quot; alt=&quot;Bento grid of the five-stage agentic loop: intent, reasoning, action, observation, and self-correction&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Implementing the agentic stack&lt;/h2&gt;
&lt;p&gt;As an engineer who values quality, I don&apos;t just let the &quot;vibe&quot; take over without guardrails. The loop only stays safe if something verifies what comes out of it — that is the whole argument in &lt;a href=&quot;https://ansezz.com/blog/testing-ai-generated-code/&quot;&gt;trust is not a QA strategy&lt;/a&gt;. Here is how I&apos;m currently structuring my agentic stack using &lt;strong&gt;Laravel&lt;/strong&gt; and &lt;strong&gt;AI&lt;/strong&gt;.&lt;/p&gt;
&lt;h3&gt;1. Defined MCP servers&lt;/h3&gt;
&lt;p&gt;I build small, dedicated &lt;strong&gt;MCP&lt;/strong&gt; servers that expose only the necessary tools to the AI. This keeps the context window clean and the security tight. I go deeper on this in &lt;a href=&quot;https://ansezz.com/blog/claude-mcp-dev-tools/&quot;&gt;Claude MCP dev tools&lt;/a&gt; and on designing &lt;a href=&quot;https://ansezz.com/blog/mcp-context-aware-agents/&quot;&gt;context-aware agents with MCP&lt;/a&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// Conceptual MCP tool definition in a PHP environment
public function defineTools(): array
{
    return [
        &apos;get_database_schema&apos; =&amp;gt; [
            &apos;description&apos; =&amp;gt; &apos;Retrieves the structure of the Laravel application tables.&apos;,
            &apos;parameters&apos; =&amp;gt; [],
        ],
        &apos;run_artisan_command&apos; =&amp;gt; [
            &apos;description&apos; =&amp;gt; &apos;Executes an artisan command safely.&apos;,
            &apos;parameters&apos; =&amp;gt; [&apos;command&apos; =&amp;gt; &apos;string&apos;],
        ],
    ];
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;2. Stateful loops&lt;/h3&gt;
&lt;p&gt;Instead of one-off chats, I use tools like &lt;strong&gt;Cursor&lt;/strong&gt;, &lt;strong&gt;Claude Code&lt;/strong&gt;, or &lt;strong&gt;Windsurf&lt;/strong&gt; that maintain a stateful connection to my local file system. This allows the agent to &quot;see&quot; the impact of its changes in real-time, just like a human developer would.&lt;/p&gt;
&lt;h3&gt;3. The human-in-the-loop (HITL)&lt;/h3&gt;
&lt;p&gt;The most important part of the architecture is the review gate. Even with agentic loops, the human architect must sign off on the &quot;plan&quot; before the &quot;action&quot; phase. This ensures the &lt;strong&gt;PHP&lt;/strong&gt; logic follows clean architecture principles rather than just &quot;making it work.&quot; What that gate should actually check is a separate question — I argue for &lt;a href=&quot;https://ansezz.com/blog/stop-reading-code-ai-review/&quot;&gt;dropping line-by-line reading&lt;/a&gt; in favor of specs and automated verification.&lt;/p&gt;
&lt;h2&gt;The takeaway for the modern founder&lt;/h2&gt;
&lt;p&gt;If you&apos;re a founder or a CTO, the takeaway is simple: stop hiring for syntax and start hiring for system design. The technical barrier is collapsing, but the architectural stakes are higher than ever.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Embrace the vibe.&lt;/strong&gt; Focus on the intent and the user experience.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Invest in infrastructure.&lt;/strong&gt; Build the &lt;strong&gt;MCP&lt;/strong&gt; connections and the data pipelines that allow AI to be effective.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Think in loops.&lt;/strong&gt; Design your internal processes so that AI can iterate autonomously, reducing your bottleneck role.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;At &lt;a href=&quot;https://ansezz.com/&quot;&gt;Ansezz&lt;/a&gt;, I&apos;m not just building apps anymore — I&apos;m building agent-ready ecosystems. Whether it&apos;s a complex &lt;strong&gt;Shopify&lt;/strong&gt; integration or a custom &lt;strong&gt;SaaS&lt;/strong&gt;, I ensure the architecture is ready for the agentic future.&lt;/p&gt;
&lt;p&gt;The code might be generated, but the vision is entirely yours.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Are you ready to stop writing code and start orchestrating your intent?&lt;/strong&gt; &lt;a href=&quot;https://ansezz.com/contact/&quot;&gt;Get in touch&lt;/a&gt; — let&apos;s design your agent stack together.&lt;/p&gt;
</content:encoded><category>architecture</category><category>vibe-coding</category><category>agentic-ai</category><category>mcp</category><category>claude</category><category>laravel</category><category>architecture</category></item><item><title>Why your RAG implementation is failing in production</title><link>https://ansezz.com/blog/why-your-rag-is-failing/</link><guid isPermaLink="true">https://ansezz.com/blog/why-your-rag-is-failing/</guid><description>Vector-only retrieval is the silent killer of production RAG. Hybrid search, BM25, rank fusion, re-rankers, and evals — the fixes that make it reliable.</description><pubDate>Sun, 08 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;You built a RAG (Retrieval-Augmented Generation) demo. On a local machine, with a handful of PDF files, it looked convincing. The answers felt coherent. The system appeared capable.&lt;/p&gt;
&lt;p&gt;Then you pushed it to production.&lt;/p&gt;
&lt;p&gt;That is usually where the illusion breaks.&lt;/p&gt;
&lt;p&gt;Users start reporting that the LLM is &quot;hallucinating&quot; when the real issue is retrieval. Obvious answers go missing even though they exist in the documentation. Irrelevant chunks surface because they are semantically adjacent, not actually useful.&lt;/p&gt;
&lt;p&gt;If your RAG system feels unreliable in production, you are not dealing with a model problem first. You are dealing with a retrieval design problem. Most production RAG systems fail because they rely too heavily on vector search and confuse a strong demo with a robust system. (For the broader catalogue of ways this goes wrong, see &lt;a href=&quot;https://ansezz.com/blog/7-rag-mistakes-production/&quot;&gt;7 mistakes you&apos;re making with your production RAG stack&lt;/a&gt;.)&lt;/p&gt;
&lt;p&gt;I&apos;ve spent a lot of time building custom AI solutions at &lt;a href=&quot;https://ansezz.com/&quot;&gt;Ansezz&lt;/a&gt;, and one pattern keeps showing up: &lt;strong&gt;a demo proves possibility, but production demands precision.&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;The &quot;vector noise&quot; trap&lt;/h2&gt;
&lt;p&gt;The philosophical shift from demo RAG to production RAG is simple: in a demo, semantic resemblance often feels good enough. In production, &quot;good enough&quot; is where failures begin.&lt;/p&gt;
&lt;p&gt;Embeddings are useful. They let us map text into vectors and retrieve by meaning rather than exact wording. That is powerful. But semantic similarity is not the same thing as retrieval accuracy.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The problem.&lt;/strong&gt; Vector search is strong at finding related concepts, but weak at handling specificity.&lt;/p&gt;
&lt;p&gt;If a user searches for &lt;em&gt;&quot;Project-X-99 deployment logs,&quot;&lt;/em&gt; a vector search might return documents about &quot;Project-A deployment&quot; or &quot;logging best practices&quot; because they are semantically close. It can miss the exact identifier &quot;X-99&quot; because that string carries little semantic weight in a high-dimensional space.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The agitation.&lt;/strong&gt; Once retrieval drifts, the LLM inherits the drift. The model cannot reason its way out of missing or irrelevant context. You end up paying for tokens that produce confident but unhelpful answers, and users lose trust for a reason that often sits one layer below the model itself.&lt;/p&gt;
&lt;h2&gt;The solution: hybrid search (vector + BM25)&lt;/h2&gt;
&lt;p&gt;The fix starts with one realization: meaning alone is not enough. You need semantic retrieval and lexical precision working together. This is &lt;strong&gt;hybrid search&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/why-your-rag-is-failing/hybrid-search-workflow.webp&quot; alt=&quot;Hybrid search workflow combining vector and BM25 retrieval&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;What is BM25?&lt;/h3&gt;
&lt;p&gt;BM25 (Best Matching 25, from the Okapi probabilistic retrieval framework) is the standard lexical ranking method behind classic search systems. It does not try to infer meaning. It rewards exact terms based on how important they are within a document and across the collection, while accounting for document length and diminishing returns from repeated terms.&lt;/p&gt;
&lt;h3&gt;Why you need both&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Vector search&lt;/strong&gt; handles synonyms, multi-lingual queries, and conceptual matching.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;BM25 search&lt;/strong&gt; handles exact matches, IDs, SKUs, product codes, and technical jargon.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Production systems need both because user questions are rarely pure meaning or pure keyword. They are usually a mix of the two. If you are still deciding how to structure retrieval in the first place, &lt;a href=&quot;https://ansezz.com/blog/vector-search-vs-graph-search/&quot;&gt;vector search vs graph search&lt;/a&gt; is a useful companion read.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/why-your-rag-is-failing/vector-vs-keyword.webp&quot; alt=&quot;Side-by-side comparison of vector search vs keyword search results&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Technical insight: reciprocal rank fusion (RRF)&lt;/h2&gt;
&lt;p&gt;When you run two different retrieval strategies, you also create a new design problem: how should they be combined?&lt;/p&gt;
&lt;p&gt;A practical answer is &lt;strong&gt;Reciprocal Rank Fusion (RRF)&lt;/strong&gt;. It is simple, reliable, and does not require you to pretend that scores from different retrieval systems are directly comparable.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/why-your-rag-is-failing/rrf-code-logic.webp&quot; alt=&quot;Annotated code snippet for reciprocal rank fusion logic&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The logic breakdown:&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Assign a score.&lt;/strong&gt; For every document returned by either search method, calculate a new rank-based score.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The formula.&lt;/strong&gt; &lt;code&gt;score = 1 / (k + rank)&lt;/code&gt;, where &lt;code&gt;rank&lt;/code&gt; is the item&apos;s 1-based position in each result list (so the code below adds &lt;code&gt;1&lt;/code&gt; to a 0-based index). The &lt;code&gt;k&lt;/code&gt; constant (often 60) prevents top-ranked items from dominating too aggressively.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Sum it up.&lt;/strong&gt; If a document appears in both the vector and BM25 result sets, its scores are added together.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Sort.&lt;/strong&gt; The documents with the highest combined scores are passed to the LLM.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Here&apos;s the minimal PHP version I drop into a Laravel service:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;function reciprocalRankFusion(array $resultSets, int $k = 60): array
{
    $scores = [];

    foreach ($resultSets as $results) {
        foreach ($results as $rank =&amp;gt; $docId) {
            $scores[$docId] = ($scores[$docId] ?? 0.0) + 1 / ($rank + 1 + $k);
        }
    }

    arsort($scores);

    return $scores;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This gives you a cleaner retrieval layer. If a document is semantically relevant &lt;em&gt;and&lt;/em&gt; lexically precise, it moves toward the top for a reason.&lt;/p&gt;
&lt;h2&gt;The &quot;second pass&quot;: using re-rankers&lt;/h2&gt;
&lt;p&gt;Hybrid search is a strong retrieval foundation, but production RAG usually needs one more layer of judgment.&lt;/p&gt;
&lt;p&gt;If you want more precise results, add a &lt;strong&gt;re-ranker&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;A re-ranker such as Cohere Rerank or BGE-Reranker is a cross-encoder model that evaluates the query and the document together. That matters because relevance is relational. It is not just about what a document contains. It is about whether that document answers &lt;em&gt;this&lt;/em&gt; question.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Step 1.&lt;/strong&gt; Retrieve the top 50 results using hybrid search.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Step 2.&lt;/strong&gt; Pass those 50 results through a re-ranker.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Step 3.&lt;/strong&gt; Send only the top 5 re-ranked results to your LLM.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This reduces context stuffing and improves the quality of what reaches the model. In practice, it is one of the clearest differences between a RAG demo and a production RAG system that behaves consistently. The extra hop does add latency, which is where a &lt;a href=&quot;https://ansezz.com/blog/redis-semantic-caching-rag/&quot;&gt;Redis and semantic caching layer&lt;/a&gt; earns its keep.&lt;/p&gt;
&lt;h2&gt;Your production RAG checklist&lt;/h2&gt;
&lt;h3&gt;The problem&lt;/h3&gt;
&lt;p&gt;A RAG system can feel impressive in a demo and still be structurally weak in production.&lt;/p&gt;
&lt;h3&gt;The agitation&lt;/h3&gt;
&lt;p&gt;Once real users, messy documents, and ambiguous queries enter the picture, weak retrieval turns the LLM into expensive guesswork. That is when confidence and correctness start drifting apart.&lt;/p&gt;
&lt;h3&gt;The solution&lt;/h3&gt;
&lt;p&gt;To move from demo RAG to production RAG, I focus on a few non-negotiables:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Stop relying on vector-only search.&lt;/strong&gt; Add a BM25 layer.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Implement RRF.&lt;/strong&gt; Fuse lexical and semantic retrieval without overcomplicating score calibration.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Tune chunking deliberately.&lt;/strong&gt; If chunks are too small, they lose context. If they are too large, they add noise. I usually find 512–1024 tokens with a 10–15% overlap works well for technical documentation.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Add a re-ranker.&lt;/strong&gt; Refine the final candidate set before anything reaches the LLM.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Evaluate with RAGAS.&lt;/strong&gt; Measure faithfulness and relevance instead of trusting intuition.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Building AI is easy. Building &lt;em&gt;reliable&lt;/em&gt; AI is hard. It requires a deeper understanding of retrieval, ranking, and context design, not just the ability to connect an API. If you are still choosing your foundation, &lt;a href=&quot;https://ansezz.com/blog/picking-the-right-rag-stack/&quot;&gt;picking the right RAG stack&lt;/a&gt; walks through the vector database trade-offs.&lt;/p&gt;
&lt;p&gt;If you are looking to build a high-performance SaaS or need help modernizing your digital presence with AI that actually works, check out what I do at &lt;a href=&quot;https://ansezz.com/work/&quot;&gt;Ansezz&lt;/a&gt;. I specialize in solving these exact types of technical problems.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Where does your own system still behave like a demo when it should be behaving like production?&lt;/strong&gt; &lt;a href=&quot;https://ansezz.com/contact/&quot;&gt;Get in touch&lt;/a&gt; — I read every war story.&lt;/p&gt;
</content:encoded><category>ai</category><category>rag</category><category>ai</category><category>vector-search</category><category>production</category></item><item><title>Monolith to microservices: a pragmatic guide</title><link>https://ansezz.com/blog/monolith-to-microservices/</link><guid isPermaLink="true">https://ansezz.com/blog/monolith-to-microservices/</guid><description>Skip the big-bang rewrite. The strangler fig pattern, anti-corruption layers, and Docker-first steps to move from monolith to microservices safely.</description><pubDate>Sun, 22 Feb 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Your monolith is a ticking time bomb and every feature you add makes the explosion more inevitable.&lt;/p&gt;
&lt;p&gt;I have seen it happen a dozen times. A startup begins with a clean Laravel or Rails app. It is fast. It is easy. It is productive. Then the team grows. The code base swells. Suddenly, a simple change to the checkout logic breaks the authentication system. Deployments that used to take five minutes now take forty. You are not scaling your business anymore — you are managing technical debt.&lt;/p&gt;
&lt;p&gt;This is the point where most developers start dreaming of micro-services. They imagine a world where every service is isolated and deployments are instant. But the reality is often a nightmare. If you do it wrong, you end up with a distributed monolith. You get all the complexity of networking with none of the benefits of isolation.&lt;/p&gt;
&lt;p&gt;The solution is not a &quot;big bang&quot; rewrite. It is pragmatic scaling. I use the &lt;strong&gt;strangler fig pattern&lt;/strong&gt; to move from monoliths to micro-services without losing my mind or my job. If you are still deciding whether to split at all, start with &lt;a href=&quot;https://ansezz.com/blog/monolith-vs-microservices/&quot;&gt;monolith vs microservices&lt;/a&gt; — this post assumes you have already made that call and want the migration playbook.&lt;/p&gt;
&lt;h2&gt;The problem with the big bang&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/monolith-to-microservices/strangler-fig.webp&quot; alt=&quot;Strangler fig pattern diagram — new services wrap the legacy monolith&quot; /&gt;&lt;/p&gt;
&lt;p&gt;When a monolith becomes too heavy, the immediate reaction is to want to scrap it. I have seen companies spend two years on a rewrite only to ship a product that has half the features of the original. The business dies while the engineers play with new toys.&lt;/p&gt;
&lt;p&gt;The monolith is not your enemy. It is just a phase. The real problem is coupling. When every part of your app knows too much about every other part, you cannot move. You are stuck in a web of dependencies. If you try to jump straight into micro-services, you will likely just port those dependencies into a network layer. Now, instead of a function call failing, you have a 500 error across a network socket.&lt;/p&gt;
&lt;p&gt;I prefer a slower, more deliberate approach. I focus on high-value extractions. I look for the parts of the app that hurt the most. Is the image processing service slowing down the web server? Is the reporting engine locking up the database? Those are your first candidates for micro-services.&lt;/p&gt;
&lt;h2&gt;The strangler fig pattern in practice&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://martinfowler.com/bliki/StranglerFigApplication.html&quot;&gt;Martin Fowler named this approach&lt;/a&gt; after a tree that grows around another tree. It starts as a small vine and eventually replaces the host entirely. In software, this means building new features as services while the old monolith remains.&lt;/p&gt;
&lt;p&gt;The process starts with an API gateway or a load balancer. I put a routing layer in front — &lt;a href=&quot;https://www.nginx.com/&quot;&gt;Nginx&lt;/a&gt; or a &lt;a href=&quot;https://cloud.google.com/&quot;&gt;Google Cloud&lt;/a&gt; HTTP(S) load balancer with &lt;a href=&quot;https://cloud.google.com/armor&quot;&gt;Cloud Armor&lt;/a&gt; bolted on for WAF and DDoS protection. If a request comes for &lt;code&gt;/api/v1/orders&lt;/code&gt;, the URL map sends it to the new service. Everything else goes to the old monolith. (If you&apos;re unsure which box does what here, see &lt;a href=&quot;https://ansezz.com/blog/load-balancer-vs-api-gateway/&quot;&gt;load balancer vs API gateway&lt;/a&gt;.)&lt;/p&gt;
&lt;p&gt;This allows me to test the new service in production with real traffic while the monolith acts as a safety net. If the new service fails, I just flip the routing back. I do not have to migrate everything at once. I can migrate one endpoint at a time.&lt;/p&gt;
&lt;h2&gt;Containerization with Docker&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/monolith-to-microservices/docker-snippet.webp&quot; alt=&quot;Annotated Dockerfile snippet for a Laravel micro-service&quot; /&gt;&lt;/p&gt;
&lt;p&gt;You cannot do micro-services without &lt;a href=&quot;https://www.docker.com/&quot;&gt;Docker&lt;/a&gt;. I treat every service as a black box. The monolith might be running on an old version of PHP, while the new service is a lean Go binary or a modern Laravel instance. Docker makes this possible.&lt;/p&gt;
&lt;p&gt;I start by containerizing the monolith. Even if it stays as a monolith for another year, putting it in a container forces me to define its environment. It makes the infrastructure reproducible.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# a simplified example of a service container
FROM php:8.4-fpm

WORKDIR /app
COPY . /app

RUN apt-get update &amp;amp;&amp;amp; apt-get install -y \
    libpq-dev \
    &amp;amp;&amp;amp; docker-php-ext-install pdo_pgsql

EXPOSE 9000
CMD [&quot;php-fpm&quot;]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Once the monolith is containerized, I can deploy it to a platform like &lt;a href=&quot;https://cloud.google.com/kubernetes-engine&quot;&gt;Google Kubernetes Engine (GKE)&lt;/a&gt;. This is where the real power of micro-services comes in. I can scale the order service to fifty instances during a sale while keeping the blog service at two.&lt;/p&gt;
&lt;h2&gt;Communication and the anti-corruption layer&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/monolith-to-microservices/routing-diagram.webp&quot; alt=&quot;Routing diagram showing API gateway dispatching between monolith and new services&quot; /&gt;&lt;/p&gt;
&lt;p&gt;The hardest part of micro-services is not the code. It is the data. Your monolith has a single database. Your micro-services should each have their own. But how do they talk?&lt;/p&gt;
&lt;p&gt;I use an &lt;strong&gt;anti-corruption layer (ACL)&lt;/strong&gt;. When I extract a service, I do not let it reach back into the monolith&apos;s database. That would be cheating. Instead, I create an interface. If the new service needs user data, it asks the monolith via a private API or a message queue like &lt;a href=&quot;https://cloud.google.com/pubsub&quot;&gt;Google Pub/Sub&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This keeps the new service clean. It does not care about the messy database schema of the legacy app. It only cares about the data it receives through the ACL. Eventually, when the user logic is also migrated, I just update the ACL to point to the new user service. For the asynchronous side of this — letting services react to events instead of calling each other directly — I lean on &lt;a href=&quot;https://ansezz.com/blog/event-driven-pubsub/&quot;&gt;event-driven Pub/Sub&lt;/a&gt; or &lt;a href=&quot;https://ansezz.com/blog/scaling-with-rabbitmq/&quot;&gt;a RabbitMQ-backed queue&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Cloud infrastructure and DevOps&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/monolith-to-microservices/cloud-infrastructure.webp&quot; alt=&quot;Cloud infrastructure overview — GKE, Pub/Sub, managed databases&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Scaling a monolith usually means buying a bigger server. Scaling micro-services means managing a fleet of small instances. I rely heavily on cloud-native tools to manage the complexity.&lt;/p&gt;
&lt;p&gt;I use &lt;a href=&quot;https://www.terraform.io/&quot;&gt;Terraform&lt;/a&gt; to manage my infrastructure as code. This ensures that my staging and production environments are identical. If I need a new database for a service, I define it in code. I do not click around in a dashboard.&lt;/p&gt;
&lt;p&gt;On the DevOps side, I use tools like &lt;a href=&quot;https://github.com/features/actions&quot;&gt;GitHub Actions&lt;/a&gt; or &lt;a href=&quot;https://coolify.io/&quot;&gt;Coolify&lt;/a&gt; for deployments. Every service has its own pipeline. If I update the checkout service, I only deploy the checkout service. I do not have to worry about the rest of the system.&lt;/p&gt;
&lt;h2&gt;The hidden costs of micro-services&lt;/h2&gt;
&lt;p&gt;I would be lying if I said this was all sunshine and rainbows. Micro-services come with a &quot;complexity tax.&quot; You now have to deal with distributed logging, service discovery, and eventual consistency.&lt;/p&gt;
&lt;p&gt;I tell my clients that they should only move to micro-services when the pain of the monolith is greater than the cost of the complexity tax. If your team is three people and your app is simple, stay in the monolith. You will move faster.&lt;/p&gt;
&lt;p&gt;But if you are hitting walls every day and your developers are afraid to touch the code, it is time to start strangling.&lt;/p&gt;
&lt;h2&gt;Pragmatic takeaways for your next move&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Start with an API gateway&lt;/strong&gt; to handle routing.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Containerize your monolith first&lt;/strong&gt; to normalize the environment.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Use the strangler fig pattern&lt;/strong&gt; to migrate one domain at a time.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Build an anti-corruption layer&lt;/strong&gt; to keep new services clean.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Invest in infrastructure as code&lt;/strong&gt; early on.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Only split when the monolith starts to hurt&lt;/strong&gt; your productivity.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Migration is a marathon, not a sprint. I have spent months on a single extraction just to make sure it was perfect. The goal is not to have micro-services. The goal is to have a system that can grow with your business.&lt;/p&gt;
&lt;p&gt;Have you ever tried a &quot;big bang&quot; rewrite only to regret it six months later? &lt;a href=&quot;https://ansezz.com/contact/&quot;&gt;Tell me about it&lt;/a&gt; — I collect these stories for a reason.&lt;/p&gt;
</content:encoded><category>architecture</category><category>microservices</category><category>scaling</category><category>docker</category><category>kubernetes</category><category>devops</category><category>laravel</category></item><item><title>Laravel multi-tenancy: a scalable SaaS architecture</title><link>https://ansezz.com/blog/laravel-multi-tenancy/</link><guid isPermaLink="true">https://ansezz.com/blog/laravel-multi-tenancy/</guid><description>Single DB vs multi-DB, global scopes that stop data leaks, stancl/tenancy in production, isolated storage, automated migrations, and a Docker plus Cloud setup.</description><pubDate>Sun, 08 Feb 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I still remember the panic of my first SaaS launch. I was watching the logs as the third customer signed up. Suddenly I realized I had no idea if customer A could see customer B&apos;s data. That realization is a rite of passage for every developer.&lt;/p&gt;
&lt;p&gt;Building a software-as-a-service (SaaS) platform is a massive technical challenge. The biggest hurdle is almost always data isolation. You need to ensure that every tenant feels like they have the whole application to themselves. If you get this wrong early on, it will haunt you forever.&lt;/p&gt;
&lt;p&gt;I have spent years refining a scalable Laravel multi-tenancy architecture. It is the most robust way to serve many customers from a single codebase without their data ever bleeding together. Here is how I approach it to keep both security and scalability intact.&lt;/p&gt;
&lt;h2&gt;Why simple database structures fail SaaS&lt;/h2&gt;
&lt;p&gt;Most developers start with a single database. They add a &lt;code&gt;user_id&lt;/code&gt; or &lt;code&gt;team_id&lt;/code&gt; to every table. It works fine for the first ten users. Then the complexity grows.&lt;/p&gt;
&lt;p&gt;You start adding more relationships. You forget to add a &lt;code&gt;where&lt;/code&gt; clause in one obscure controller. Suddenly one customer is seeing another customer&apos;s private invoices. This is a catastrophic failure. It kills trust and can end your business overnight.&lt;/p&gt;
&lt;p&gt;Performance also becomes a nightmare. As your database grows to millions of rows the queries get slower. Indexing helps, but it does not solve the fundamental problem of data bloat. You need a strategy that isolates data while keeping your infrastructure manageable.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/laravel-multi-tenancy/architecture.webp&quot; alt=&quot;Diagram comparing single-database tenant_id isolation with a multi-database setup where each tenant has its own database&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Choosing your isolation strategy&lt;/h2&gt;
&lt;p&gt;When I build custom web solutions I always start by choosing between two main paths. You either go with a single database or a multi-database setup.&lt;/p&gt;
&lt;p&gt;The &lt;strong&gt;single database&lt;/strong&gt; approach uses a shared schema. Every row has a &lt;code&gt;tenant_id&lt;/code&gt;. It is cheap to run and easy to update. I recommend this for startups where costs need to stay low. You can manage thousands of small tenants on a single server this way.&lt;/p&gt;
&lt;p&gt;The &lt;strong&gt;multi-database&lt;/strong&gt; approach is the gold standard for enterprise. Every customer gets their own database. This offers the strongest isolation and makes per-tenant &lt;a href=&quot;https://ansezz.com/blog/replication-vs-backup-laravel/&quot;&gt;backups and replication&lt;/a&gt; straightforward. If one database crashes the others stay online. I use this for high-value clients who have strict compliance needs.&lt;/p&gt;
&lt;p&gt;I often use the &lt;code&gt;stancl/tenancy&lt;/code&gt; package for Laravel. It is the most flexible tool in the ecosystem. It allows you to switch between these strategies as your business grows. You can find out more about how I handle these complex technical challenges on the &lt;a href=&quot;https://ansezz.com/work/&quot;&gt;Ansezz work page&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Building with global scopes&lt;/h2&gt;
&lt;p&gt;The secret to sleeping well at night is automation. I never rely on my memory to filter data. Instead I use Laravel global scopes.&lt;/p&gt;
&lt;p&gt;A global scope automatically adds a filter to every query on a model. It ensures that &lt;code&gt;Customer::all()&lt;/code&gt; only returns customers for the current tenant. It happens behind the scenes so you cannot forget it.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/laravel-multi-tenancy/code-snippet.webp&quot; alt=&quot;Annotated PHP code of a BelongsToTenant trait registering a global scope and a creating event to set tenant_id&quot; /&gt;&lt;/p&gt;
&lt;p&gt;I create a &lt;code&gt;BelongsToTenant&lt;/code&gt; trait. I apply this trait to every model that needs isolation. It handles the filtering and automatically sets the &lt;code&gt;tenant_id&lt;/code&gt; when a new record is created. It is a simple solution that prevents 99 percent of data leaks.&lt;/p&gt;
&lt;p&gt;Here&apos;s what that trait looks like in practice:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?php

namespace App\Concerns;

use App\Models\Tenant;
use App\Scopes\TenantScope;
use Illuminate\Database\Eloquent\Relations\BelongsTo;

trait BelongsToTenant
{
    protected static function bootBelongsToTenant(): void
    {
        static::addGlobalScope(new TenantScope());

        static::creating(function ($model): void {
            if (! $model-&amp;gt;tenant_id &amp;amp;&amp;amp; app()-&amp;gt;bound(&apos;tenant&apos;)) {
                $model-&amp;gt;tenant_id = app(&apos;tenant&apos;)-&amp;gt;id;
            }
        });
    }

    public function tenant(): BelongsTo
    {
        return $this-&amp;gt;belongsTo(Tenant::class);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You also need to isolate your cache and your file storage. If two tenants upload a file named &lt;code&gt;logo.png&lt;/code&gt; they should not overwrite each other. I configure Laravel to use tenant-specific prefixes for all storage paths. This creates a true &quot;sandbox&quot; environment for every user.&lt;/p&gt;
&lt;h2&gt;Scaling on the cloud&lt;/h2&gt;
&lt;p&gt;Your architecture is only as good as the infrastructure it runs on. I usually deploy my Laravel apps using Docker and cloud providers like Google Cloud or AWS.&lt;/p&gt;
&lt;p&gt;Containerization is key. It lets me &lt;a href=&quot;https://ansezz.com/blog/horizontal-vs-vertical-scaling/&quot;&gt;scale the application horizontally&lt;/a&gt;: when traffic spikes I spin up more instances of the web server. That only works because the web tier stays &lt;a href=&quot;https://ansezz.com/blog/stateless-vs-stateful-apps/&quot;&gt;stateless&lt;/a&gt; — tenant context is resolved per request, not stored on the box. Because the multi-tenancy logic lives at the application level, the infrastructure stays clean. When a single tenant starts pushing serious request volume, I reach for &lt;a href=&quot;https://ansezz.com/blog/laravel-octane-high-traffic/&quot;&gt;Laravel Octane to handle high traffic&lt;/a&gt; before throwing more servers at the problem.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/laravel-multi-tenancy/infrastructure.webp&quot; alt=&quot;Cloud infrastructure diagram for a multi-tenant Laravel deployment&quot; /&gt;&lt;/p&gt;
&lt;p&gt;I use managed database services like Google Cloud SQL. They handle the heavy lifting of backups and scaling. For multi-database setups I use automated scripts to provision new databases whenever a customer signs up. This &quot;infrastructure as code&quot; approach ensures that scaling is a button click away.&lt;/p&gt;
&lt;p&gt;If you are looking to modernize your digital presence with a scalable cloud setup you can see my full range of services at &lt;a href=&quot;https://ansezz.com/&quot;&gt;ansezz.com&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;The tenant switcher experience&lt;/h2&gt;
&lt;p&gt;The final piece of the puzzle is the user interface. Customers need a seamless way to move between different accounts if they own multiple businesses.&lt;/p&gt;
&lt;p&gt;I build clean dashboards using Vue, with the frontend talking to the Laravel backend over a GraphQL API for a reactive feel. The tenant switcher is always accessible and shows the user exactly which context they are working in.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/laravel-multi-tenancy/tenant-switcher.webp&quot; alt=&quot;Tenant switcher UI mockup in a Vue dashboard&quot; /&gt;&lt;/p&gt;
&lt;p&gt;I focus on making the transition between tenants feel instant. I cache the tenant configuration in the frontend to avoid unnecessary API calls. It is these small details that separate a basic app from a high-quality pro solution.&lt;/p&gt;
&lt;h2&gt;My Laravel multi-tenancy checklist&lt;/h2&gt;
&lt;p&gt;If you are starting a new project today, here are the steps I recommend you follow.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Choose your package early.&lt;/strong&gt; I prefer &lt;code&gt;stancl/tenancy&lt;/code&gt; because of its flexibility. It handles subdomain routing and database switching out of the box.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Implement global scopes immediately.&lt;/strong&gt; Do not wait until you have ten models. Add the trait to your base model and make it a standard part of your workflow.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Automate your deployment.&lt;/strong&gt; Use Docker from day one. It makes local development identical to production. This avoids the &quot;it works on my machine&quot; bugs that plague SaaS launches.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Plan for data migration.&lt;/strong&gt; As you update your schema you need a way to run migrations across hundreds of databases. Tools like &lt;code&gt;stancl/tenancy&lt;/code&gt; have built-in commands for this.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Keep it simple.&lt;/strong&gt; Don&apos;t build a multi-database setup if you only have five users. Start small and scale as the revenue grows. My goal is always to deliver exceptional results without over-complicating the technical stack.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Are you building a SaaS or looking to migrate your current app to a multi-tenant structure? What is the biggest technical hurdle you are facing right now? &lt;a href=&quot;https://ansezz.com/services/&quot;&gt;Here&apos;s how I help teams architect and ship multi-tenant SaaS&lt;/a&gt;, and you can always &lt;a href=&quot;https://ansezz.com/contact/&quot;&gt;get in touch&lt;/a&gt; — I&apos;d love to hear about it. 🤙&lt;/p&gt;
</content:encoded><category>laravel</category><category>laravel</category><category>multi-tenancy</category><category>architecture</category><category>docker</category><category>databases</category></item><item><title>AI vs traditional development: which fits?</title><link>https://ansezz.com/blog/ai-vs-traditional-development/</link><guid isPermaLink="true">https://ansezz.com/blog/ai-vs-traditional-development/</guid><description>AI vs traditional development: when AI-assisted speed pays off, when traditional engineering is non-negotiable, and the hybrid workflow I favor.</description><pubDate>Sun, 25 Jan 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Most teams frame AI vs traditional development as a binary choice. That is the wrong question.&lt;/p&gt;
&lt;p&gt;The real problem is not &quot;AI or traditional?&quot; — it is what kind of speed, control, and risk your business can actually afford.&lt;/p&gt;
&lt;p&gt;I see this mistake a lot. Teams chase AI because it feels faster, or reject it because it feels messy. Both directions land in the same place: rushed systems with weak foundations, or polished systems that ship too late.&lt;/p&gt;
&lt;p&gt;The better move is to understand where each approach wins, where it breaks, and where a hybrid model gives you the best return. Both work. They just solve different problems.&lt;/p&gt;
&lt;h2&gt;AI-powered development: the speed revolution&lt;/h2&gt;
&lt;p&gt;AI integration changes how I build software. Instead of manually writing every repetitive piece, I can use tools that understand context, generate scaffolding, speed up testing, and remove a lot of the drag from delivery. I dig into the day-to-day of this in my post on &lt;a href=&quot;https://ansezz.com/blog/vibe-coding/&quot;&gt;vibe coding&lt;/a&gt;, where taste and intent matter more than raw typing speed.&lt;/p&gt;
&lt;h3&gt;The core advantage: speed&lt;/h3&gt;
&lt;p&gt;This is where AI shines.&lt;/p&gt;
&lt;p&gt;For standard workflows, admin panels, CRUD-heavy systems, internal tools, and first-pass prototypes, AI can cut a serious amount of time. What used to take weeks can often be reduced to days if the scope is clear and the review process is tight.&lt;/p&gt;
&lt;p&gt;That speed usually comes from a few places:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Automated code generation.&lt;/strong&gt; Prompts turn into usable boilerplate and feature drafts.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Faster testing.&lt;/strong&gt; AI can draft test cases and edge-case coverage quickly.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Debugging support.&lt;/strong&gt; It helps narrow down likely failures faster.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Documentation help.&lt;/strong&gt; It can turn rough implementation details into clean internal docs.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;How much of that speed you actually capture depends on where you sit on the &lt;a href=&quot;https://ansezz.com/blog/ai-coding-workflow-levels/&quot;&gt;AI coding workflow levels&lt;/a&gt; — copy-pasting into a chat window and orchestrating agents against your real codebase are not the same tool.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/ai-vs-traditional-development/workflow.webp&quot; alt=&quot;Diagram contrasting AI-assisted, traditional, and hybrid engineering workflows&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;Who benefits most from AI development&lt;/h3&gt;
&lt;p&gt;I would lean toward AI-heavy workflows when speed matters more than perfect customization on day one.&lt;/p&gt;
&lt;p&gt;That usually means:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Startups trying to reach product-market fit before the runway gets tight.&lt;/li&gt;
&lt;li&gt;Small teams that need leverage more than headcount.&lt;/li&gt;
&lt;li&gt;Businesses shipping standard features that already follow familiar patterns.&lt;/li&gt;
&lt;li&gt;Teams where non-technical stakeholders want to contribute to discovery and prototyping.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In those cases, AI acts like a power tool. It does not replace the builder. It just makes the first cut much faster.&lt;/p&gt;
&lt;h3&gt;The trade-offs to consider&lt;/h3&gt;
&lt;p&gt;This is where a lot of teams get burned.&lt;/p&gt;
&lt;p&gt;AI is fast at common patterns. It is weaker at deep product nuance, strange business rules, and systems that need careful long-term architecture. If you skip review, you can ship something that looks finished but behaves like a prototype wearing a production costume. It also helps to know what kind of AI you are actually leaning on — the trade-offs differ between predictive models and generative ones, which I break down in &lt;a href=&quot;https://ansezz.com/blog/ml-vs-genai/&quot;&gt;machine learning vs generative AI&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;There is a longer-term cost too. Recent studies of AI-accelerated codebases report more duplicated code, larger pull requests, and faster technical-debt accumulation, so the upfront speed can quietly shift effort into later debugging and rework. That is the trap. The fix is treating AI output as a draft, not as truth — the same discipline I argue for in &lt;a href=&quot;https://ansezz.com/blog/agentic-workflows-vibe-coding/&quot;&gt;the architectural shift to agentic workflows&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Traditional development: the control champion&lt;/h2&gt;
&lt;p&gt;Traditional development is slower, but it gives me tighter control over how the system is shaped.&lt;/p&gt;
&lt;p&gt;This is the path I trust most when the business rules are complex, the architecture matters, or the cost of failure is high. Every part of the system is designed with intent instead of inferred from a prompt.&lt;/p&gt;
&lt;h3&gt;The core advantage: control&lt;/h3&gt;
&lt;p&gt;Traditional development is better when the software needs precision.&lt;/p&gt;
&lt;p&gt;That matters for:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Complex enterprise systems&lt;/strong&gt; — lots of moving parts and layered business logic.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Regulated industries&lt;/strong&gt; — where auditability and traceability matter.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Mission-critical applications&lt;/strong&gt; — where downtime or bad behavior is expensive.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Custom architectures&lt;/strong&gt; — where the product does not fit common patterns.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;The predictability factor&lt;/h3&gt;
&lt;p&gt;One underrated benefit of traditional development is predictability.&lt;/p&gt;
&lt;p&gt;Manual design, explicit code reviews, architecture decisions, and planned testing give me a clearer picture of trade-offs. It is like building with blueprints instead of assembling furniture from a photo.&lt;/p&gt;
&lt;p&gt;That slower process often saves time later because fewer assumptions make it into production.&lt;/p&gt;
&lt;h3&gt;The time investment reality&lt;/h3&gt;
&lt;p&gt;The downside is obvious.&lt;/p&gt;
&lt;p&gt;Manual coding, reviews, debugging, refactoring, and testing take time. You need stronger engineering talent, and you need the discipline to keep standards high when deadlines start squeezing the team.&lt;/p&gt;
&lt;p&gt;Traditional development gives more control, but you pay for it in time and cost.&lt;/p&gt;
&lt;h2&gt;AI vs traditional development: head-to-head&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Factor&lt;/th&gt;
&lt;th&gt;AI-assisted development&lt;/th&gt;
&lt;th&gt;Traditional development&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Upfront speed&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Often significantly faster&lt;/td&gt;
&lt;td&gt;Standard industry timelines&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Cost structure&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Cheaper to start, debt-prone later&lt;/td&gt;
&lt;td&gt;Higher labor cost, steadier&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Team requirements&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Still needs senior review&lt;/td&gt;
&lt;td&gt;Requires senior expertise&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Customization level&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Strong on common patterns&lt;/td&gt;
&lt;td&gt;Unlimited customization&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Quality assurance&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Fast drafts, human review required&lt;/td&gt;
&lt;td&gt;Manual review from the start&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Risk management&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Variable, depends on review rigor&lt;/td&gt;
&lt;td&gt;Predictable risk factors&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Scalability&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Scales output, not judgment&lt;/td&gt;
&lt;td&gt;Scales with team growth&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;Making the right choice for your business&lt;/h2&gt;
&lt;h3&gt;Choose AI integration when&lt;/h3&gt;
&lt;p&gt;Choose AI when your bottleneck is delivery speed and the work is close to known patterns.&lt;/p&gt;
&lt;p&gt;That usually applies when:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Your market window is tight.&lt;/li&gt;
&lt;li&gt;You are building standard business apps like portals, dashboards, e-commerce flows, or content systems.&lt;/li&gt;
&lt;li&gt;Your team wants quick prototypes before committing engineering time.&lt;/li&gt;
&lt;li&gt;Your budget is better spent on iteration than on deep custom engineering from day one.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Choose traditional development when&lt;/h3&gt;
&lt;p&gt;Choose traditional development when the cost of being wrong is higher than the cost of being slower.&lt;/p&gt;
&lt;p&gt;That usually means:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The app needs a unique architecture.&lt;/li&gt;
&lt;li&gt;Compliance and audit trails are mandatory.&lt;/li&gt;
&lt;li&gt;Reliability matters more than release velocity.&lt;/li&gt;
&lt;li&gt;Your team wants direct ownership of code quality and system design.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;The hybrid strategy: best of both worlds&lt;/h3&gt;
&lt;p&gt;This is the option I recommend most often.&lt;/p&gt;
&lt;p&gt;The strongest teams do not treat this like a religion. They use AI where speed helps and switch to traditional engineering where judgment matters.&lt;/p&gt;
&lt;p&gt;A practical hybrid setup looks like this:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Generate boilerplate and first drafts with AI, then review and reshape manually.&lt;/li&gt;
&lt;li&gt;Use AI for prototyping, then rebuild critical paths carefully.&lt;/li&gt;
&lt;li&gt;Automate repetitive testing tasks, but keep human review for logic and architecture.&lt;/li&gt;
&lt;li&gt;Use AI to accelerate docs and support material, while keeping final technical decisions human-led.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The hybrid model works because it treats AI like a junior accelerator, not like an autopilot.&lt;/p&gt;
&lt;h2&gt;Implementation guidelines&lt;/h2&gt;
&lt;h3&gt;Starting with AI integration&lt;/h3&gt;
&lt;p&gt;If I were introducing AI into an existing team, I would start small.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Begin with low-risk features.&lt;/li&gt;
&lt;li&gt;Define a review process for all AI-generated code — one that &lt;a href=&quot;https://ansezz.com/blog/stop-reading-code-ai-review/&quot;&gt;scales past line-by-line reading&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Choose tools that fit the current workflow.&lt;/li&gt;
&lt;li&gt;Train the team on prompting, verification, and code quality checks.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Maintaining traditional excellence&lt;/h3&gt;
&lt;p&gt;If the team stays mostly traditional, I would protect the basics.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Invest in strong senior review.&lt;/li&gt;
&lt;li&gt;Keep documentation current.&lt;/li&gt;
&lt;li&gt;Use clear architecture standards.&lt;/li&gt;
&lt;li&gt;Avoid rushing complex work into fragile implementations.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Building hybrid capabilities&lt;/h3&gt;
&lt;p&gt;If the goal is balance, then the workflow matters more than the tools.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Identify which tasks are repetitive and safe to automate.&lt;/li&gt;
&lt;li&gt;Keep humans responsible for architecture and business logic.&lt;/li&gt;
&lt;li&gt;Add quality gates before merge and deployment — &lt;a href=&quot;https://ansezz.com/blog/testing-ai-generated-code/&quot;&gt;here is the gate I run on AI-authored PRs&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Measure outcomes, not just speed.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;The future-ready approach&lt;/h2&gt;
&lt;p&gt;The teams that will win in 2026 are not the ones that blindly choose AI or reject it.&lt;/p&gt;
&lt;p&gt;They are the ones that know where speed is enough, where control is non-negotiable, and where a hybrid model gives them leverage without chaos.&lt;/p&gt;
&lt;p&gt;That is the real solution.&lt;/p&gt;
&lt;p&gt;Use AI to remove friction. Use traditional engineering to protect the parts that matter. Combine both when the business needs speed and reliability at the same time.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/ai-vs-traditional-development/workspace.webp&quot; alt=&quot;Senior developer&apos;s desk in pop-art comic style, blending AI tools and hand-written code&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Your development strategy should match your business goals, not the trend cycle. If you had to choose today, which matters more for your next product: speed, control, or a hybrid path? &lt;a href=&quot;https://ansezz.com/services/&quot;&gt;Here&apos;s how I help teams pick and ship the right mix&lt;/a&gt;, and if you&apos;d rather just talk it through, &lt;a href=&quot;https://ansezz.com/contact/&quot;&gt;reach out&lt;/a&gt; — I&apos;d love to hear which side you&apos;re leaning toward.&lt;/p&gt;
</content:encoded><category>architecture</category><category>ai</category><category>architecture</category><category>vibe-coding</category></item><item><title>Scaling SaaS with Coolify: deployment strategies</title><link>https://ansezz.com/blog/scaling-with-coolify/</link><guid isPermaLink="true">https://ansezz.com/blog/scaling-with-coolify/</guid><description>Move past the single-server trap. Multi-node Coolify, zero-downtime rolling deploys, dedicated build servers, and GitHub Actions wiring for production.</description><pubDate>Sun, 11 Jan 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;You&apos;ve finally moved your apps off that messy manual VPS and into Coolify. It feels great. Everything is in one place. But then the traffic starts to spike. You realize that hosting your production database, three web apps, and a memory-heavy build process on a single $10 DigitalOcean droplet is a recipe for disaster.&lt;/p&gt;
&lt;p&gt;The &quot;single server trap&quot; is real. It&apos;s fine for a side project or a quick MVP. But when you&apos;re building for real customers, you need more than just a dashboard. You need a strategy. You&apos;re worried about what happens when that one server hits 100% CPU or when a simple deployment takes your whole site down for five minutes.&lt;/p&gt;
&lt;p&gt;I&apos;ve spent the last decade scaling web applications and building custom solutions at &lt;a href=&quot;https://ansezz.com/&quot;&gt;Ansezz&lt;/a&gt;. I&apos;ve seen self-hosted setups crumble under pressure because they lacked the right architecture. The good news is that Coolify is more than capable of handling high-scale workloads. You just need to know how to pull the right levers.&lt;/p&gt;
&lt;p&gt;In this guide, I&apos;m going to show you how to move from a basic setup to a production-grade infrastructure using advanced Coolify strategies. We&apos;re talking multi-server nodes, zero-downtime deployments, and offloading the heavy lifting so your apps stay snappy. If you&apos;re just getting started, my walkthroughs on &lt;a href=&quot;https://ansezz.com/blog/coolify-docker-saas-hosting/&quot;&gt;Coolify and Docker for SaaS hosting&lt;/a&gt; and running a &lt;a href=&quot;https://ansezz.com/blog/coolify-self-hosted-saas/&quot;&gt;fully self-hosted SaaS on Coolify&lt;/a&gt; cover the foundations this post builds on.&lt;/p&gt;
&lt;h2&gt;Moving beyond the single-server monolith&lt;/h2&gt;
&lt;p&gt;The biggest mistake I see engineers make is keeping everything on one node. When your build process starts, it eats up CPU and RAM. Your web app starts to lag. Your database gets starved for resources.&lt;/p&gt;
&lt;p&gt;The solution is to decouple your &quot;control plane&quot; from your &quot;workloads.&quot;&lt;/p&gt;
&lt;p&gt;In a professional setup, you want one small server dedicated solely to running the Coolify instance itself. This is your mission control. Then, you add separate &quot;app servers&quot; where your actual containers live.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/scaling-with-coolify/multi-server.webp&quot; alt=&quot;Multi-server architecture bento grid showing control plane and app nodes&quot; /&gt;&lt;/p&gt;
&lt;p&gt;To do this in Coolify, you go to the &lt;strong&gt;Servers&lt;/strong&gt; tab and add a new server via SSH. Once it&apos;s connected, you can choose which server a specific resource should be deployed to. This gives you &lt;a href=&quot;https://ansezz.com/blog/horizontal-vs-vertical-scaling/&quot;&gt;horizontal scalability&lt;/a&gt;: if one server is getting full, you just spin up another one, add it to Coolify, and point your next app there.&lt;/p&gt;
&lt;p&gt;This separation of concerns is a core pillar of what we do when building &lt;a href=&quot;https://ansezz.com/work/&quot;&gt;custom web applications&lt;/a&gt;. It prevents a single point of failure from taking down your entire digital presence.&lt;/p&gt;
&lt;h2&gt;The art of the zero-downtime deploy&lt;/h2&gt;
&lt;p&gt;Nothing kills user trust faster than a &quot;502 Bad Gateway&quot; every time you push a small CSS fix. By default, many self-hosted setups just kill the old container and start the new one. There&apos;s a gap. That gap is where your users get frustrated.&lt;/p&gt;
&lt;p&gt;Coolify handles this beautifully with &quot;rolling updates,&quot; but it only works if you tell it how to check the health of your app.&lt;/p&gt;
&lt;p&gt;If you don&apos;t configure health checks, Traefik (the reverse proxy Coolify uses) might start sending traffic to your new container before the app inside it has even finished booting up.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/scaling-with-coolify/health-checks.webp&quot; alt=&quot;Dashboard visualization of health check monitoring across rolling deploy&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Here is the workflow I use to keep deploys seamless:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Create a health endpoint.&lt;/strong&gt; In your Laravel, Vue, or Node app, create a simple route like &lt;code&gt;/healthz&lt;/code&gt;. It should return a 200 status code only when the app is ready to serve traffic.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Configure Coolify.&lt;/strong&gt; In your application settings, go to the &lt;strong&gt;Health Check&lt;/strong&gt; section. Set the path to &lt;code&gt;/healthz&lt;/code&gt; and the interval to something like 5 seconds.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The rollout.&lt;/strong&gt; When you hit deploy, Coolify starts the new container alongside the old one. Traefik only routes traffic to a container once its health check passes, so requests keep hitting the old container until the new &lt;code&gt;/healthz&lt;/code&gt; reports healthy. The old container is then drained and killed.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This is a non-negotiable step for any SaaS or e-commerce store where every second of downtime equals lost revenue.&lt;/p&gt;
&lt;h2&gt;Offloading builds to a dedicated Coolify server&lt;/h2&gt;
&lt;p&gt;If you&apos;re building a modern app with Docker, the build process can be incredibly resource-intensive. Compiling assets, installing npm packages, and building images can spike your server usage to the moon.&lt;/p&gt;
&lt;p&gt;If you&apos;re running that build on the same server that&apos;s trying to serve your customers, they&apos;re going to feel the slowdown.&lt;/p&gt;
&lt;p&gt;Advanced users leverage a &lt;strong&gt;dedicated build server&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;You can designate a high-CPU server in Coolify specifically for builds. When you trigger a deployment, Coolify builds the image on that server, pushes it to a container registry, and your production app server pulls the finished image down. That registry step is mandatory: a build server only works if you&apos;ve wired up a registry (Docker Hub, GitHub Container Registry, or a self-hosted one) for both servers to authenticate against. Note that a server flagged as a build server can&apos;t also run deployed apps, so this is a dedicated role.&lt;/p&gt;
&lt;p&gt;Your production server never feels the build. It just pulls a fresh, ready-to-run image.&lt;/p&gt;
&lt;h3&gt;What about the database?&lt;/h3&gt;
&lt;p&gt;While Coolify makes it easy to click &quot;New Database,&quot; running your production Postgres or MySQL inside a Docker container on the same server as your app is risky.&lt;/p&gt;
&lt;p&gt;For production workloads, I almost always recommend using an external managed database like AWS RDS or Google Cloud SQL. It handles backups, point-in-time recovery, and scaling automatically.&lt;/p&gt;
&lt;p&gt;In Coolify, you simply provide the connection string as an environment variable. This keeps your state (the data) separate from your compute (the app) — the same &lt;a href=&quot;https://ansezz.com/blog/stateless-vs-stateful-apps/&quot;&gt;stateless app&lt;/a&gt; discipline that makes horizontal scaling possible. If your app server goes up in flames, your data is safe on a managed platform.&lt;/p&gt;
&lt;h2&gt;Automation at scale with CI/CD&lt;/h2&gt;
&lt;p&gt;Manual deployments are for hobbyists. For a professional workflow, you want your code to move from GitHub to production without you touching a single button in the Coolify UI.&lt;/p&gt;
&lt;p&gt;I prefer using GitHub Actions for this. While Coolify has a great GitHub App integration, using Actions gives you more control. You can run your test suite, lint your code, and only if everything passes, trigger the Coolify deployment via a webhook.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/scaling-with-coolify/cicd-pipeline.webp&quot; alt=&quot;Pop-art illustration of a CI/CD pipeline flowing from GitHub to Coolify&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Here is a snippet of how I usually structure a simple deployment step in a &lt;code&gt;.github/workflows/deploy.yml&lt;/code&gt; file:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;name: deploy to production
on:
  push:
    branches:
      - main

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - name: trigger coolify webhook
        run: |
          curl --fail -X GET &quot;${{ secrets.COOLIFY_WEBHOOK_URL }}&quot; \
            -H &quot;Authorization: Bearer ${{ secrets.COOLIFY_TOKEN }}&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It&apos;s simple, direct, and ensures that broken code never reaches your servers. It keeps your development cycle clean and your mental health intact.&lt;/p&gt;
&lt;h2&gt;Advanced configuration tips&lt;/h2&gt;
&lt;p&gt;Managing a multi-server setup requires a bit of extra care. Here are a few practical takeaways to keep in your back pocket:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Resource limits.&lt;/strong&gt; Always set CPU and RAM limits in Coolify for each application. This prevents a single &quot;leaky&quot; container from hogging all the resources and crashing the whole server.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;External backups.&lt;/strong&gt; If you do choose to run databases inside Coolify, use the S3-compatible backup feature. I personally use Backblaze B2 or Cloudflare R2 for this. Never rely on local backups alone.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Docker pruning.&lt;/strong&gt; Coolify is good at cleaning up, but it&apos;s worth checking your disk space regularly. Large images can eat up your SSD fast. Set up a cron job or use Coolify&apos;s built-in cleanup settings.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Monitoring.&lt;/strong&gt; Use a tool like Better Stack or GlitchTip to monitor your endpoints. Coolify tells you if the container is running, but an external monitor tells you if a human can actually use the site.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Scaling is a journey&lt;/h2&gt;
&lt;p&gt;Scaling isn&apos;t about having the most expensive hardware. It&apos;s about having a system that is predictable and resilient. Coolify gives us the tools to act like a giant tech company without the massive overhead of a dedicated DevOps team.&lt;/p&gt;
&lt;p&gt;By splitting your servers, mastering health checks, and automating your builds, you move from &quot;hoping it works&quot; to &quot;knowing it scales.&quot;&lt;/p&gt;
&lt;p&gt;I&apos;ve helped dozens of founders and tech leads navigate these waters. Whether you&apos;re building a Shopify app or a complex Laravel SaaS, the principles are the same. Keep your compute separate from your data, and your builds separate from your traffic.&lt;/p&gt;
&lt;p&gt;Have you ever had a deployment go sideways because a build process crashed your production server? What&apos;s your current &quot;war story&quot; from the world of self-hosting? &lt;a href=&quot;https://ansezz.com/contact/&quot;&gt;Drop me a line&lt;/a&gt;.&lt;/p&gt;
</content:encoded><category>devops</category><category>coolify</category><category>deployment</category><category>devops</category><category>docker</category><category>self-hosting</category><category>ci-cd</category><category>scaling</category></item><item><title>Shopify Liquid vs. headless: picking the right stack</title><link>https://ansezz.com/blog/shopify-liquid-vs-headless/</link><guid isPermaLink="true">https://ansezz.com/blog/shopify-liquid-vs-headless/</guid><description>Hydrogen looks great on paper, but Liquid still ships more revenue per week. A practical framework for choosing Liquid, headless Hydrogen, or the messy middle.</description><pubDate>Sun, 28 Dec 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Your store is making money, but every new change feels like surgery on a moving car.&lt;/p&gt;
&lt;p&gt;Traffic is up. Orders are up. Pressure is up. But the theme layer is starting to fight back. Simple feature requests turn into fragile hacks. App scripts pile up. Mobile performance gets softer with every install. What used to feel fast and convenient now feels like a ceiling.&lt;/p&gt;
&lt;p&gt;This is where a lot of brands start weighing Shopify Liquid vs. headless. The promise sounds great. More freedom. Better performance. Cleaner frontend architecture. But this is also where expensive mistakes happen. A headless rebuild can solve real problems — or create a second system your team now has to babysit forever.&lt;/p&gt;
&lt;p&gt;The real question is not which stack sounds more modern. It is which stack fits your stage, your team, and your operational reality.&lt;/p&gt;
&lt;h2&gt;The Liquid reality: why it still wins for most stores&lt;/h2&gt;
&lt;p&gt;Liquid is still the default winner for a reason. It is tightly integrated with Shopify, fast to ship, and much easier to maintain than a custom headless frontend.&lt;/p&gt;
&lt;p&gt;For most growth-stage stores, Liquid gives the best speed-to-market. Online Store 2.0 made theme architecture much more modular than the old days, so a solid theme setup can go a long way before it becomes a real blocker.&lt;/p&gt;
&lt;p&gt;This is the part people underestimate. A good Liquid stack is like a well-tuned production van. It is not flashy, but it moves product reliably, gets updated quickly, and does not need a pit crew every week.&lt;/p&gt;
&lt;p&gt;The problem shows up when business needs outgrow theme constraints. Maybe the storefront needs deeply custom interactions. Maybe product data has to come from an ERP, a PIM, and a custom backend at the same time. Maybe merchandising logic is getting too complex for theme code to stay clean. That is usually the point where headless becomes a serious conversation, not just a trendy one.&lt;/p&gt;
&lt;h2&gt;Performance is the primary driver&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/shopify-liquid-vs-headless/performance-metrics.webp&quot; alt=&quot;Performance metrics dashboard comparing Liquid vs headless storefront&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Performance is the most common reason teams start looking at headless, and milliseconds genuinely matter. On e-commerce storefronts, small delays compound into lower conversion rates, weaker ad efficiency, and a worse mobile experience.&lt;/p&gt;
&lt;p&gt;A well-built headless stack using Hydrogen and Oxygen can push performance much further than a typical theme setup. You get finer control over rendering, data loading, caching, and frontend execution. That opens the door for lower LCP and a more responsive storefront.&lt;/p&gt;
&lt;p&gt;But this is where the hype needs a reality check. Headless does not automatically mean faster. It only gets faster when the frontend architecture is actually good.&lt;/p&gt;
&lt;p&gt;If the team over-fetches Storefront API data, hydrates too much JavaScript, or ships a bloated component tree, the custom storefront can end up slower than a decent Liquid theme — and that happens more often than people admit. There is also a middle path worth knowing about: dropping in &lt;a href=&quot;https://ansezz.com/blog/shopify-storefront-web-components/&quot;&gt;Shopify storefront web components&lt;/a&gt; lets you add dynamic, framework-light interactivity without committing to a full headless rebuild.&lt;/p&gt;
&lt;p&gt;So yes, headless can be a performance win. But only if the implementation is disciplined.&lt;/p&gt;
&lt;h2&gt;The hidden complexity of going headless&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/shopify-liquid-vs-headless/architecture-diagram.webp&quot; alt=&quot;Architecture diagram of a headless Shopify stack with custom frontend and integrations&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Going headless means splitting commerce from presentation. Shopify still handles products, checkout, and admin workflows. Your custom frontend handles the customer experience.&lt;/p&gt;
&lt;p&gt;That sounds clean on paper. In practice, it means you now own two systems instead of one.&lt;/p&gt;
&lt;p&gt;You need to manage hosting, deployments, caching, GraphQL queries, error handling, observability, and integration behavior across services. Every app in the stack has to be checked for API compatibility. If an app only works by injecting snippets into a theme, that convenience is gone.&lt;/p&gt;
&lt;p&gt;This is the part that catches teams off guard. In Liquid, many features feel plug-and-play. In headless, the same features often become custom integration work. Reviews, loyalty, search, subscriptions, personalization, analytics. All of it may need extra engineering. The same calculus applies to newer surfaces like &lt;a href=&quot;https://ansezz.com/blog/agentic-commerce-shopify/&quot;&gt;agentic commerce on Shopify&lt;/a&gt;, where the storefront has to expose clean, machine-readable APIs no matter which stack you pick.&lt;/p&gt;
&lt;p&gt;The easiest way to think about it is this: Liquid is renting a well-equipped shop. Headless is designing your own building. You get more freedom, but now plumbing, wiring, and maintenance are your problem too.&lt;/p&gt;
&lt;h2&gt;Time and money: the real cost of freedom&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/shopify-liquid-vs-headless/timeline-comparison.webp&quot; alt=&quot;Timeline comparison illustration showing Liquid vs headless project costs&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Time and budget usually decide this faster than architecture opinions do.&lt;/p&gt;
&lt;p&gt;A custom Liquid theme can often be launched in weeks. That means faster testing, faster iteration, and lower implementation cost. If the business mainly needs merchandising flexibility, better UX, and cleaner performance hygiene, Liquid usually gives a better return.&lt;/p&gt;
&lt;p&gt;Headless is a different category of investment. The build takes longer. The team needs stronger frontend engineering. The integration surface is bigger. And maintenance does not stop after launch.&lt;/p&gt;
&lt;p&gt;This is the important part. With headless, you are not just paying for a redesign. You are taking on a software product that needs ongoing care. Deployments, monitoring, API changes, dependency updates, caching strategy, and developer ownership all become part of normal operations.&lt;/p&gt;
&lt;p&gt;For many stores, that trade-off is not worth it yet. More freedom is great, but freedom is expensive when the business does not fully need it.&lt;/p&gt;
&lt;h2&gt;My decision framework: which one should you choose?&lt;/h2&gt;
&lt;p&gt;I like to reduce this decision to a few practical questions.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Is the current theme actually blocking revenue?&lt;/strong&gt; If conversion is healthy and the main pain is taste or minor flexibility, Liquid is probably still the right call.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Does the team have the engineering depth to own a custom storefront long term?&lt;/strong&gt; Headless is not a one-time build. It is an operating model.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Are the integration requirements genuinely complex?&lt;/strong&gt; If the storefront needs to combine Shopify with custom product logic, external systems, or a bespoke application layer, headless starts to make more sense.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Is performance a code problem or an architecture problem?&lt;/strong&gt; Many slow stores do not need headless. They need script cleanup, better image handling, less app bloat, and tighter theme code.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The simplest stack that solves the real bottleneck is usually the best stack. Building a spaceship to cross the street is still a bad decision. You can see how this framework plays out across &lt;a href=&quot;https://ansezz.com/work/&quot;&gt;real Shopify builds I&apos;ve shipped&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Practical takeaways for your next move&lt;/h2&gt;
&lt;p&gt;If you are stuck between Liquid and headless, start here:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Audit site speed before making architectural decisions.&lt;/strong&gt; If LCP is acceptable, the problem may not be the stack.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Remove app bloat.&lt;/strong&gt; A lot of slow Liquid stores are just carrying too many scripts and too much leftover code.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Map every integration.&lt;/strong&gt; List what works natively, what depends on theme injection, and what would break in headless.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Estimate ownership cost, not just build cost.&lt;/strong&gt; Launch is only the first invoice.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Look at Hydrogen if the business really needs headless&lt;/strong&gt; — but keep the scope tight.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Choosing a stack is a long-term commitment. The right answer is not the most advanced one. It is the one that solves the current bottleneck without creating three new ones.&lt;/p&gt;
&lt;p&gt;Are you dealing with real technical limits in Shopify, or just feeling the pull of a more customizable stack? &lt;a href=&quot;https://ansezz.com/contact/&quot;&gt;Drop me a line&lt;/a&gt; — happy to do a 30-minute architecture sanity check.&lt;/p&gt;
</content:encoded><category>shopify</category><category>shopify</category><category>hydrogen</category><category>performance</category><category>architecture</category></item><item><title>Picking the right RAG stack: vector databases for AI</title><link>https://ansezz.com/blog/picking-the-right-rag-stack/</link><guid isPermaLink="true">https://ansezz.com/blog/picking-the-right-rag-stack/</guid><description>pgvector, Pinecone, Weaviate, Qdrant — a 2026 field guide to picking the right vector store for your AI app, with hybrid search and scaling tips.</description><pubDate>Sun, 14 Dec 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;You built a cool chatbot. It works great on your local machine until you feed it 50,000 internal documents. Suddenly, it&apos;s hallucinating. It&apos;s slow. It&apos;s pulling data from three years ago when you specifically asked for last week&apos;s report. Nine times out of ten, the weak link is your RAG stack — specifically the vector database underneath it.&lt;/p&gt;
&lt;p&gt;Building a Retrieval-Augmented Generation (RAG) system sounds like a weekend project. But once you move past the &quot;hello world&quot; stage, you hit the database wall. Choosing the wrong vector store early on is a silent killer. It leads to high latency, soaring cloud costs, and a painful migration six months down the line when your data outgrows your infrastructure.&lt;/p&gt;
&lt;p&gt;I&apos;ve spent over a decade building &lt;a href=&quot;https://ansezz.com/&quot;&gt;custom web applications&lt;/a&gt; and scaling cloud infrastructure. I&apos;ve seen teams get paralyzed by the sheer number of options in the AI ecosystem. You don&apos;t need a perfect database. You need the right tool for your specific scale and team.&lt;/p&gt;
&lt;p&gt;Let&apos;s break down the 2026 vector database landscape so you can stop scrolling and start shipping.&lt;/p&gt;
&lt;h2&gt;Why the database matters in RAG&lt;/h2&gt;
&lt;p&gt;An LLM like Claude or GPT-5 is a genius without a memory. RAG gives it that memory. Your vector database is the librarian. If the librarian is slow or loses books, the genius can&apos;t do its job. (If you&apos;re still deciding whether RAG is even the right tool versus baking knowledge into the model, my breakdown of &lt;a href=&quot;https://ansezz.com/blog/rag-vs-fine-tuning/&quot;&gt;RAG vs fine-tuning&lt;/a&gt; is the better starting point.)&lt;/p&gt;
&lt;p&gt;When we talk about RAG stacks, we&apos;re looking for three things:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Latency&lt;/strong&gt; — can it find the right &quot;memory&quot; in tens of milliseconds? (When it can&apos;t, a &lt;a href=&quot;https://ansezz.com/blog/redis-semantic-caching-rag/&quot;&gt;Redis semantic cache in front of the store&lt;/a&gt; often does more than swapping databases.)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Hybrid search&lt;/strong&gt; — can it search by meaning (vectors) and exact keywords (full-text)?&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Developer experience&lt;/strong&gt; — how much time are you going to spend on DevOps?&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/picking-the-right-rag-stack/comparison-bento.webp&quot; alt=&quot;Comparison bento grid of vector databases&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;The contenders: which one is yours?&lt;/h2&gt;
&lt;h3&gt;1. pgvector — the &quot;I already have a database&quot; choice&lt;/h3&gt;
&lt;p&gt;If you are already running &lt;a href=&quot;https://ansezz.com/&quot;&gt;Postgres for your web applications&lt;/a&gt;, pgvector is usually your first stop. It&apos;s not a new database. It&apos;s an extension that adds vector support to the database you already trust.&lt;/p&gt;
&lt;p&gt;It&apos;s perfect if you have under 10 million vectors. You get ACID compliance, easy backups, and your relational data stays right next to your embeddings. No new infra. No new security audits.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Pros&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Zero new infrastructure if you use Postgres.&lt;/li&gt;
&lt;li&gt;Perfect for joining vector data with user metadata.&lt;/li&gt;
&lt;li&gt;Huge ecosystem support (Laravel, Django, Node.js).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Cons&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Scaling to 100M+ vectors requires serious server muscle.&lt;/li&gt;
&lt;li&gt;Hybrid search requires manual tuning with Postgres full-text search.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;2. Pinecone — the &quot;I want zero ops&quot; choice&lt;/h3&gt;
&lt;p&gt;Pinecone is the gold standard for managed service. It&apos;s a serverless vector database. You don&apos;t manage clusters. You don&apos;t tune indexes. You just send vectors and get results.&lt;/p&gt;
&lt;p&gt;In 2026, Pinecone is the go-to for teams that want to scale from zero to a billion vectors without hiring a dedicated DevOps engineer. Their serverless architecture means you only pay for what you use.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Pros&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Truly managed. Pick a region and go.&lt;/li&gt;
&lt;li&gt;World-class performance and low latency.&lt;/li&gt;
&lt;li&gt;Great enterprise features like SOC2 compliance.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Cons&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It&apos;s a black box. You can&apos;t self-host it.&lt;/li&gt;
&lt;li&gt;Costs can scale quickly if you have high write/read volume.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;3. Weaviate &amp;amp; Qdrant — the hybrid powerhouses&lt;/h3&gt;
&lt;p&gt;If your RAG app needs to combine semantic search with old-school keyword search, these two are the leaders. Weaviate and Qdrant are built from the ground up for high-performance vector retrieval.&lt;/p&gt;
&lt;p&gt;Weaviate excels at &quot;out-of-the-box&quot; hybrid search. Qdrant, written in Rust, is incredibly fast and efficient with memory. Both offer open-source versions and managed cloud options.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Pros&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Best-in-class hybrid search (BM25 + Vector).&lt;/li&gt;
&lt;li&gt;Flexible hosting (self-hosted Docker or managed cloud).&lt;/li&gt;
&lt;li&gt;Highly optimized for filtering (e.g., &quot;find documents from &apos;2025&apos; that talk about &apos;security&apos;&quot;).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Cons&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;More operational overhead than Pinecone.&lt;/li&gt;
&lt;li&gt;Requires learning a new database API.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/picking-the-right-rag-stack/rag-architecture.webp&quot; alt=&quot;Reference RAG architecture diagram&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;How to choose: the engineering trade-offs&lt;/h2&gt;
&lt;p&gt;Picking a database isn&apos;t about finding the &quot;best&quot; one. It&apos;s about matching the tool to your engineering constraints.&lt;/p&gt;
&lt;h3&gt;Factor 1: the &quot;billions&quot; problem&lt;/h3&gt;
&lt;p&gt;Most startups don&apos;t have a billion vectors. They have a few thousand PDFs. If you&apos;re in the sub-1M range, pgvector is almost always the right answer. It&apos;s simple and it works.&lt;/p&gt;
&lt;p&gt;If you are building something like a global legal search engine or a massive e-commerce recommendation system, you need the distributed architecture of Milvus or Pinecone. Don&apos;t build a massive distributed system if you don&apos;t have a massive amount of data.&lt;/p&gt;
&lt;h3&gt;Factor 2: hybrid search is non-negotiable&lt;/h3&gt;
&lt;p&gt;Pure vector search is actually pretty bad at finding specific technical terms. If you search for &quot;PHP 8.4 features,&quot; a pure vector search might give you general &quot;PHP&quot; articles. A hybrid search combines the &quot;vibe&quot; of the vector with the &quot;exactness&quot; of a keyword search. If your data is highly relational, it&apos;s also worth weighing &lt;a href=&quot;https://ansezz.com/blog/vector-search-vs-graph-search/&quot;&gt;vector search vs graph search&lt;/a&gt; before committing to a pure-vector approach.&lt;/p&gt;
&lt;p&gt;If search quality is your #1 metric, look at Weaviate or Qdrant. They handle the blending of these two search types natively.&lt;/p&gt;
&lt;h3&gt;Factor 3: the &quot;DevOps&quot; tax&lt;/h3&gt;
&lt;p&gt;I&apos;m a huge fan of &lt;a href=&quot;https://ansezz.com/&quot;&gt;cloud infrastructure and deployment&lt;/a&gt;. But I also know that every new piece of infra you add to your stack is another thing that can break at 3 AM.&lt;/p&gt;
&lt;p&gt;If you have a small team, lean on managed services like Pinecone or Zilliz. If you have a strong infra team and want to save on cloud margins at high scale, self-hosting Qdrant on a tool like Coolify or Kubernetes is the move.&lt;/p&gt;
&lt;h2&gt;Implementing pgvector with Laravel&lt;/h2&gt;
&lt;p&gt;Since I work a lot with &lt;a href=&quot;https://ansezz.com/&quot;&gt;custom web development using Laravel&lt;/a&gt;, I want to show you how easy this looks in practice. You don&apos;t need a PhD in math to run a vector query.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// finding the most relevant document chunks
$embedding = Ai::embed($query); // get vector from OpenAI/Claude

$results = Document::query()
    -&amp;gt;select(&apos;content&apos;)
    -&amp;gt;orderByRaw(&apos;embedding &amp;lt;=&amp;gt; ?&apos;, [$embedding]) // the &amp;lt;=&amp;gt; operator is pgvector&apos;s magic
    -&amp;gt;limit(5)
    -&amp;gt;get();
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That snippet is essentially the core of a RAG system. You find the content, send it to the LLM, and get a grounded answer.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/picking-the-right-rag-stack/code-snippet.webp&quot; alt=&quot;Annotated code snippet showing pgvector similarity query&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Three practical tips for your RAG stack&lt;/h2&gt;
&lt;p&gt;Before you commit to a database, keep these three things in mind. They will save you weeks of refactoring.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1. Index early, but not too early.&lt;/strong&gt;
Vector indexes like HNSW are fast for searching but slow for inserting data. If you are doing a massive initial data load, insert your vectors first, then create the index. It&apos;s the difference between minutes and hours.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2. Normalize your vectors.&lt;/strong&gt;
Make sure your embedding model and your vector database are on the same page. If you use cosine similarity, normalize your vectors. It keeps your results consistent and prevents weird ranking bugs.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3. Keep the metadata lean.&lt;/strong&gt;
It&apos;s tempting to store the entire JSON object of a document inside your vector database. Don&apos;t. Store the vector and a simple ID. Keep the heavy data in your primary database (like Postgres). This keeps your vector index small and fast.&lt;/p&gt;
&lt;p&gt;These are the cheap wins. For the deeper traps — bad chunking, missing reranking, stale embeddings — see the &lt;a href=&quot;https://ansezz.com/blog/7-rag-mistakes-production/&quot;&gt;7 mistakes you&apos;re making with your production RAG stack&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;My personal rule of thumb&lt;/h2&gt;
&lt;p&gt;I&apos;ve built systems for &lt;a href=&quot;https://ansezz.com/work/&quot;&gt;startups and established businesses&lt;/a&gt;. Here is how I usually guide them:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Default to pgvector.&lt;/strong&gt; It&apos;s the path of least resistance for most web apps.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Move to Pinecone&lt;/strong&gt; if you need high performance and don&apos;t want to manage servers.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Choose Weaviate&lt;/strong&gt; if your application relies heavily on complex hybrid search and metadata filtering.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The &quot;right&quot; stack is the one that lets you ship your AI features today, not the one that looks the best on a benchmark chart.&lt;/p&gt;
&lt;p&gt;Are you building a RAG system right now? What&apos;s the biggest hurdle you&apos;ve hit with your data retrieval?&lt;/p&gt;
&lt;p&gt;Drop a line or &lt;a href=&quot;https://ansezz.com/contact/&quot;&gt;reach out&lt;/a&gt;. I&apos;d love to hear your war stories.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;&lt;strong&gt;Summary takeaways&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;pgvector&lt;/strong&gt; is king for teams already on Postgres.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Pinecone&lt;/strong&gt; is the best zero-ops solution for scaling.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Hybrid search&lt;/strong&gt; (keyword + vector) is usually better than vector search alone.&lt;/li&gt;
&lt;li&gt;Keep your architecture simple. Don&apos;t over-engineer for &quot;billions&quot; of vectors if you only have thousands.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/picking-the-right-rag-stack/mentor.webp&quot; alt=&quot;Mentor robot character offering advice&quot; /&gt;&lt;/p&gt;
</content:encoded><category>ai</category><category>rag</category><category>vector-search</category><category>pgvector</category><category>laravel</category></item><item><title>Vibe coding: why projects need more than just logic</title><link>https://ansezz.com/blog/vibe-coding/</link><guid isPermaLink="true">https://ansezz.com/blog/vibe-coding/</guid><description>Taste, intent, and feel are the new senior-engineer superpowers in the Cursor and Claude era — and how to keep the codebase from becoming a ball of mud.</description><pubDate>Sun, 30 Nov 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Most developers are obsessed with logic. We spend years mastering syntax, optimizing database queries, and debating architectural patterns. We build systems that are technically perfect but somehow feel hollow. They work, but they don&apos;t sing — and that gap is exactly what vibe coding is meant to close.&lt;/p&gt;
&lt;p&gt;The problem is that your users don&apos;t care about your clean code or your clever recursive functions. They care about how the software feels. They care about the &quot;vibe.&quot;&lt;/p&gt;
&lt;p&gt;If you keep building purely for the machine, you are going to lose. The next generation of successful products won&apos;t be the ones with the most features or the tightest algorithms. They will be the ones that master the art of vibe coding.&lt;/p&gt;
&lt;h2&gt;The logic trap&lt;/h2&gt;
&lt;p&gt;I&apos;ve spent over a decade in the trenches of software development. I&apos;ve built custom web applications for startups and managed complex cloud infrastructure on Google Cloud and AWS. For a long time, I thought my job was to be a logic machine. I thought that if I followed every best practice and wrote the most efficient Laravel code possible, the project would be a success.&lt;/p&gt;
&lt;p&gt;I was wrong.&lt;/p&gt;
&lt;p&gt;Logic is just the foundation. It is the skeleton that keeps the building from falling down. But nobody wants to live in a skeleton. People want a home with character, warmth, and a specific feeling. In software, that character comes from the vibe.&lt;/p&gt;
&lt;p&gt;When we focus purely on logic, we end up with &quot;boring&quot; software. It is the kind of software that does what it says on the tin but leaves the user feeling nothing. Or worse, it feels frustrating because the developer didn&apos;t think about the emotional friction of a slow-loading button or a confusing layout.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/vibe-coding/logic-vs-vibe.webp&quot; alt=&quot;Side-by-side illustration of cold logic vs warm vibe coding&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Entering the era of vibe coding&lt;/h2&gt;
&lt;p&gt;Andrej Karpathy coined &quot;vibe coding&quot; in a February 2025 post — &quot;fully give in to the vibes, embrace exponentials, and forget that the code even exists.&quot; He framed it as a throwaway, weekend-project mode and has since distanced the term from production work (he now prefers &quot;agentic engineering&quot; for serious builds). I&apos;m using the broader sense the industry latched onto: a shift from being a writer of code to being a curator of intent in the age of AI tools like Cursor and Claude.&lt;/p&gt;
&lt;p&gt;Vibe coding is about letting go of the need to micro-manage every semicolon. It is about using natural language to describe the &lt;em&gt;feel&lt;/em&gt; and &lt;em&gt;behavior&lt;/em&gt; you want, and then letting AI handle the heavy lifting of the implementation.&lt;/p&gt;
&lt;p&gt;In this world, your value as an engineer isn&apos;t in how fast you can type. It&apos;s in your taste. It&apos;s in your ability to recognize when a user interface feels &quot;off&quot; and knowing how to steer the AI to fix it. It is about prioritizing the outcome over the output — which is really a question of &lt;a href=&quot;https://ansezz.com/blog/prompt-engineering-vs-context-engineering/&quot;&gt;prompt engineering versus context engineering&lt;/a&gt;: giving the model enough of the right context to make good calls on its own.&lt;/p&gt;
&lt;p&gt;I&apos;ve seen this shift firsthand in my own work at &lt;a href=&quot;https://ansezz.com/&quot;&gt;Ansezz&lt;/a&gt;. When I&apos;m working on a Shopify store development project, the technical logic of the checkout is important. But the &lt;em&gt;vibe&lt;/em&gt; of the checkout — the smooth transitions, the reassuring feedback, the perfect typography — is what actually drives conversions for the business.&lt;/p&gt;
&lt;h2&gt;Tools that fuel the flow&lt;/h2&gt;
&lt;p&gt;To embrace vibe coding, you need tools that don&apos;t get in your way. You need tools that allow you to stay in a state of flow where the distance between your idea and the execution is as small as possible.&lt;/p&gt;
&lt;p&gt;Tools like Cursor have changed the game for me. Instead of spending twenty minutes setting up boilerplate for a new Vue component, I can describe the &quot;vibe&quot; of the component in the chat. I can say, &quot;build me a dashboard widget that feels airy and modern, uses a bento grid layout, and gives the user a sense of calm control over their data.&quot;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/vibe-coding/ai-ide.webp&quot; alt=&quot;AI-powered IDE pairing with a developer&quot; /&gt;&lt;/p&gt;
&lt;p&gt;The AI generates the code. I review it. If the vibe isn&apos;t right, I don&apos;t fix the code line-by-line. I talk to the model again. I give it feedback on the &lt;em&gt;feeling&lt;/em&gt;. &quot;This feels too cramped. Give it more white space and make the shadows softer.&quot;&lt;/p&gt;
&lt;p&gt;This is the essence of vibe coding. It&apos;s a high-level conversation about intent.&lt;/p&gt;
&lt;h2&gt;The senior developer guardrails&lt;/h2&gt;
&lt;p&gt;Now, I know what some of you are thinking. &quot;This sounds like a recipe for a messy, unmaintainable codebase.&quot;&lt;/p&gt;
&lt;p&gt;You are right to be worried. If you just &quot;vibe&quot; your way through a project without any discipline, you will end up with a &quot;ball of mud.&quot; This is where the senior engineer perspective becomes more critical than ever.&lt;/p&gt;
&lt;p&gt;Vibe coding isn&apos;t about being lazy. It&apos;s about shifting your focus. You use your senior-level expertise to build the &quot;robust core&quot; that allows the &quot;vibe layer&quot; to exist.&lt;/p&gt;
&lt;p&gt;For me, that core is often built with Laravel and Docker. I use Laravel because it is built for &quot;developer happiness.&quot; The framework itself has a vibe of elegance and simplicity. It provides the solid, logical foundation — the authentication, the database migrations, the API structures — that I can trust.&lt;/p&gt;
&lt;p&gt;Once that robust core is in place, I can afford to be more exploratory with the frontend and the user experience. I can &quot;vibe code&quot; the top layer because I know the foundation is solid.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ansezz.com/blog/vibe-coding/architecture.webp&quot; alt=&quot;Architecture diagram — robust core under the vibe layer&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Why Shopify and vibe coding are a perfect match&lt;/h2&gt;
&lt;p&gt;If you work in e-commerce, vibe coding is your secret weapon. Shopify is a platform that already understands the importance of the feel. They have spent years perfecting the checkout flow and the admin experience.&lt;/p&gt;
&lt;p&gt;When I do &lt;a href=&quot;https://ansezz.com/work/&quot;&gt;Shopify customization&lt;/a&gt;, I&apos;m not just writing Liquid code. I&apos;m trying to match the brand&apos;s vibe. A luxury jewelry brand needs a completely different &quot;vibe&quot; than a high-energy fitness store.&lt;/p&gt;
&lt;p&gt;One should feel slow, deliberate, and expensive. The other should feel fast, punchy, and motivating. You can&apos;t achieve that through logic alone. You achieve it by obsessing over the details that the logic-only dev ignores.&lt;/p&gt;
&lt;h2&gt;How to start vibe coding today&lt;/h2&gt;
&lt;p&gt;If you want to move beyond being a logic-only developer, here are some practical steps you can take:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Prioritize your taste.&lt;/strong&gt; Start looking at software not just as a tool, but as an experience. What apps do you love using? Why? Is it the speed? The animations? The way the buttons click? Start building a &quot;swipe file&quot; of great vibes.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Embrace AI as a partner, not a tool.&lt;/strong&gt; Stop using Copilot just for autocompletion. Start using tools like Claude or Cursor to brainstorm high-level concepts. Describe the &quot;feel&quot; you want and see what it gives you. Wiring your editor into the rest of your stack with &lt;a href=&quot;https://ansezz.com/blog/claude-mcp-dev-tools/&quot;&gt;MCP to give the model real context&lt;/a&gt; is what makes this practical. Once you are comfortable here, the natural next step is real &lt;a href=&quot;https://ansezz.com/blog/agentic-workflows-vibe-coding/&quot;&gt;agentic workflows with MCP and agentic loops&lt;/a&gt;. If you want the whole progression laid out, I mapped it &lt;a href=&quot;https://ansezz.com/blog/ai-coding-workflow-levels/&quot;&gt;level by level, from gateway prompts to multi-agent orchestration&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Build a solid core.&lt;/strong&gt; Don&apos;t let the vibe turn into chaos. Use frameworks like Laravel or tools like Docker to keep your infrastructure predictable and clean. The more you trust your foundation, the more you can play with the surface.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Iterate on the feeling.&lt;/strong&gt; Instead of trying to get the code perfect the first time, get the &quot;vibe&quot; right first. Build a messy prototype that feels great, and then use your technical skills to refactor and harden it.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Focus on user empathy.&lt;/strong&gt; Every time you write a piece of logic, ask yourself: &quot;how will this make the user feel?&quot; If the answer is &quot;nothing,&quot; you have more work to do.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;The future is felt, not just calculated&lt;/h2&gt;
&lt;p&gt;We are entering a time where &quot;coding&quot; as we knew it is becoming a commodity. Anyone can generate a function to sort an array. But not everyone can create an experience that moves people.&lt;/p&gt;
&lt;p&gt;The future of software development belongs to the engineers who can bridge the gap between the machine and the human heart. It belongs to the people who understand that the best code is the code you don&apos;t even notice because you&apos;re too busy enjoying the vibe.&lt;/p&gt;
&lt;p&gt;I&apos;ve seen the results of this approach in my own projects and for the clients I work with. When you stop fighting the logic and start leaning into the flow, the work gets more fun and the results land harder.&lt;/p&gt;
&lt;p&gt;What is the one app you use that just &quot;feels&quot; right — and what can you steal from its vibe for your next project?&lt;/p&gt;
</content:encoded><category>ai</category><category>vibe-coding</category><category>ai</category><category>claude</category><category>laravel</category></item><item><title>Hello, world. Yes, another developer blog.</title><link>https://ansezz.com/blog/hello-world/</link><guid isPermaLink="true">https://ansezz.com/blog/hello-world/</guid><description>Why this developer blog exists, what I&apos;ll write about — Laravel, AI, Shopify — and why neobrutalism is the right call for an engineer&apos;s site in 2026.</description><pubDate>Sun, 16 Nov 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import SpeechBubble from &quot;@/components/neobrutalist/SpeechBubble.astro&quot;;
import StickyNote from &quot;@/components/neobrutalist/StickyNote.astro&quot;;
import CodeBlock from &quot;@/components/neobrutalist/CodeBlock.astro&quot;;
import ComparisonTable from &quot;@/components/neobrutalist/ComparisonTable.astro&quot;;
import BurstBadge from &quot;@/components/neobrutalist/BurstBadge.astro&quot;;&lt;/p&gt;
&lt;p&gt;Most developer blogs read like a LinkedIn post had a baby with a Medium article.
This one won&apos;t. I&apos;m Anass. I ship Laravel + Shopify + AI for a living, and this is
where I take notes out loud.&lt;/p&gt;
&lt;h2&gt;Why now&lt;/h2&gt;
&lt;p&gt;I&apos;ve spent over a decade remote, mostly head-down in client codebases. The patterns I keep
reaching for — &lt;a href=&quot;https://ansezz.com/blog/laravel-octane-high-traffic/&quot;&gt;Laravel Octane&lt;/a&gt; at scale, &lt;a href=&quot;https://ansezz.com/blog/why-your-rag-is-failing/&quot;&gt;RAG done
right&lt;/a&gt;, &lt;a href=&quot;https://ansezz.com/blog/agentic-commerce-shopify/&quot;&gt;agentic commerce&lt;/a&gt; — don&apos;t
show up in tutorials. So I&apos;m writing them down.&lt;/p&gt;

  Strong opinions, loosely held. Code that runs in production.

&lt;h2&gt;What you&apos;ll find here&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Laravel internals you actually use day-to-day&lt;/li&gt;
&lt;li&gt;AI engineering with &lt;a href=&quot;https://ansezz.com/blog/claude-mcp-dev-tools/&quot;&gt;Claude and MCP&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Shopify Plus app patterns&lt;/li&gt;
&lt;li&gt;Architecture and DevOps lessons learned the hard way&lt;/li&gt;
&lt;/ul&gt;

  Subscribe via [RSS](/rss.xml) — no newsletter, no popup, no LinkedIn dance.

&lt;h2&gt;This blog vs the AI-slop blog&lt;/h2&gt;
&lt;p&gt;&amp;lt;ComparisonTable
columns={[
{ label: &quot;This blog&quot;, tone: &quot;yellow&quot; },
{ label: &quot;AI-slop blog&quot;, tone: &quot;red&quot; },
]}
rows={[
{
icon: &quot;lucide:check&quot;,
label: &quot;Real production code&quot;,
cells: [true, false],
},
{ icon: &quot;lucide:zap&quot;, label: &quot;Opinions&quot;, cells: [true, false] },
{ icon: &quot;lucide:bot&quot;, label: &quot;GPT-written filler&quot;, cells: [false, true] },
{ icon: &quot;lucide:smile&quot;, label: &quot;Personality&quot;, cells: [true, false] },
]}
/&amp;gt;&lt;/p&gt;
&lt;h2&gt;Code you can actually run&lt;/h2&gt;
&lt;p&gt;Every post that needs code gets the syntax treatment — real, runnable, not pseudo-code:&lt;/p&gt;
&lt;p&gt;&amp;lt;CodeBlock
code={&lt;code&gt;use Prism\\Prism\\Prism;\n\n$response = Prism::text()\n    -&amp;gt;using(&apos;anthropic&apos;, &apos;claude-sonnet-4-5&apos;)\n    -&amp;gt;withSystemPrompt(&apos;You are a senior Laravel engineer.&apos;)\n    -&amp;gt;withPrompt(&apos;Refactor this controller for clarity.&apos;)\n    -&amp;gt;asText();&lt;/code&gt;}
lang=&quot;php&quot;
filename=&quot;app/Actions/AskClaude.php&quot;
/&amp;gt;&lt;/p&gt;

  01

&lt;p&gt;That&apos;s it. First post live. More incoming.&lt;/p&gt;
</content:encoded><category>career</category><category>career</category><category>laravel</category><category>ai</category></item></channel></rss>