<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Dario Zadro</title>
    <description>The latest articles on Forem by Dario Zadro (@zadro).</description>
    <link>https://forem.com/zadro</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F361540%2F022544ef-637d-4307-8a78-0a384e700573.jpg</url>
      <title>Forem: Dario Zadro</title>
      <link>https://forem.com/zadro</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/zadro"/>
    <language>en</language>
    <item>
      <title>GEO Requires More Than SEO: The Technical Distinction Nobody Is Making</title>
      <dc:creator>Dario Zadro</dc:creator>
      <pubDate>Wed, 29 Apr 2026 23:48:00 +0000</pubDate>
      <link>https://forem.com/zadro/geo-requires-more-than-seo-the-technical-distinction-nobody-is-making-3hp</link>
      <guid>https://forem.com/zadro/geo-requires-more-than-seo-the-technical-distinction-nobody-is-making-3hp</guid>
      <description>&lt;p&gt;GEO is mostly SEO. But mostly is not all of it.&lt;/p&gt;

&lt;p&gt;The conversation keeps collapsing into two useless camps. Camp one says GEO is just repackaged SEO with a shinier invoice. Camp two says SEO is dead. Both are wrong.&lt;/p&gt;

&lt;p&gt;Here's the technical case for why.&lt;/p&gt;




&lt;h2&gt;
  
  
  Eligibility vs. Selection: The Distinction Nobody Is Making
&lt;/h2&gt;

&lt;p&gt;Two completely separate problems are being treated as one.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Eligibility&lt;/strong&gt; is whether an AI system can reach your content at all. Crawled, indexed, snippet-eligible. &lt;a href="https://developers.google.com/search/docs/appearance/ai-features" rel="noopener noreferrer"&gt;Google explicitly documents&lt;/a&gt; that to be considered for AI Overviews, a page must be indexed and eligible to show a snippet. If you don't clear that bar, nothing else matters.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Selection&lt;/strong&gt; is what happens after retrieval fires. Of everything the system pulled in, which &lt;em&gt;chunks&lt;/em&gt; actually end up in the LLM response?&lt;/p&gt;

&lt;p&gt;SEO governs eligibility. GEO governs selection. These are sequential problems, not the same problem.&lt;/p&gt;

&lt;p&gt;The data backs this up. A &lt;a href="https://ahrefs.com/blog/ai-overview-citations-top-10/" rel="noopener noreferrer"&gt;March 2026 Ahrefs analysis&lt;/a&gt; of 863,000 keywords and 4 million AI Overview URLs found that only &lt;strong&gt;38% of cited pages ranked in the top 10&lt;/strong&gt; for the same query, down from 76% just seven months earlier. A separate &lt;a href="https://www.brightedge.com/resources/weekly-ai-search-insights/ai-overviews-one-year-presence-size-citing" rel="noopener noreferrer"&gt;BrightEdge analysis&lt;/a&gt; from February 2026 puts that overlap even lower, at around 17%.&lt;/p&gt;

&lt;p&gt;So roughly two out of three AI citations come from pages a user would never see on page one. The AI is not drawing from what ranks. It's drawing from what it can &lt;em&gt;use&lt;/em&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  RAG: A Term the SEO Community Is Misusing
&lt;/h2&gt;

&lt;p&gt;You can't open a LinkedIn feed right now without someone referencing RAG. Most of those posts are technically wrong.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://arxiv.org/abs/2005.11401" rel="noopener noreferrer"&gt;Retrieval-Augmented Generation&lt;/a&gt; (Lewis et al., 2020) is a specific architecture. It combines what a model already knows from training (parametric knowledge) with documents pulled in at query time (non-parametric retrieved context). That's it.&lt;/p&gt;

&lt;p&gt;RAG is &lt;strong&gt;not&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A synonym for "search API call"&lt;/li&gt;
&lt;li&gt;The same thing as embeddings, which are a vector representation method used &lt;em&gt;within&lt;/em&gt; retrieval, not the architecture itself&lt;/li&gt;
&lt;li&gt;Something that always happens. Many queries get model-only responses with no retrieval at all&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Retrieval is conditional, not constant. When someone tells you "rank well and you'll get retrieved," they're giving you advice that only applies to a subset of queries.&lt;/p&gt;

&lt;h3&gt;
  
  
  Not All Retrieval Runs Through Google or Bing
&lt;/h3&gt;

&lt;p&gt;Each platform retrieves differently. This is where "just do SEO" advice collapses:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Google AI Mode / AI Overviews.&lt;/strong&gt; Query fan-out across subtopics and data sources, with supporting pages identified &lt;em&gt;during&lt;/em&gt; response generation. The citation set extends well beyond what ranked for the original query.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Claude.&lt;/strong&gt; Runs on Brave Search, not Google or Bing. A &lt;a href="https://www.tryprofound.com/blog/what-is-claude-web-search-explained" rel="noopener noreferrer"&gt;2025 analysis by Profound&lt;/a&gt; found 86.7% overlap between Claude citations and Brave's top organic results. Not indexed by Brave? Claude won't cite you. Doesn't matter where you rank on Google.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Perplexity.&lt;/strong&gt; Runs its &lt;a href="https://docs.perplexity.ai/docs/resources/perplexity-crawlers" rel="noopener noreferrer"&gt;own continuously refreshed index&lt;/a&gt; using two distinct crawlers, after moving away from Bing Web Search API in 2022. Strong freshness bias: 90% of top cited sources answer the core question within the first 100 words (BLUF pattern).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Microsoft Copilot.&lt;/strong&gt; &lt;a href="https://learn.microsoft.com/en-us/microsoft-copilot-studio/data-privacy-security-web-search" rel="noopener noreferrer"&gt;Grounds through Bing APIs&lt;/a&gt; across three distinct modes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;OpenAI Assistants.&lt;/strong&gt; &lt;a href="https://platform.openai.com/docs/assistants/tools/file-search" rel="noopener noreferrer"&gt;Documents hybrid keyword plus semantic vector retrieval&lt;/a&gt;, query rewriting, parallel searches, and reranking. No search API involved.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Two distinct mechanisms underlie all of this that most agencies never separate.&lt;/p&gt;

&lt;p&gt;The first is &lt;strong&gt;training data inclusion&lt;/strong&gt;. Being present in the sources models learned from. Wikipedia, major publications, Reddit, high-authority industry sites. You build toward it through long-term brand authority and earning mentions across those sources. That's digital PR and link outreach. SEO under the hood. And Google has been parsing contextual relevance and brand mentions longer than most realize. But the real divergence is sentiment parsing across trained data and RAG methods. Feeding those systems is where SEO ends and GEO begins.&lt;/p&gt;

&lt;p&gt;The second is &lt;strong&gt;retrieval-time inclusion&lt;/strong&gt;. What happens when an LLM does live web retrieval during a prompt response. Results get re-ranked by the model based on probability and relevance. That retrieval step does not care much about rankings.&lt;/p&gt;

&lt;p&gt;That second mechanism is what 90% of GEO tactics actually target. Even when practitioners claim otherwise.&lt;/p&gt;

&lt;p&gt;Your brand appearing as a citation is an output address. It's not a retrieval receipt. It doesn't tell you how the content got there.&lt;/p&gt;




&lt;h2&gt;
  
  
  Fan-Out Coverage: The Most Underrated GEO Signal
&lt;/h2&gt;

&lt;p&gt;A single user query gets decomposed into multiple sub-queries before retrieval even happens. Your content has to survive that fan-out, showing up across several rewritten versions of the original question, not just one.&lt;/p&gt;

&lt;p&gt;A &lt;a href="https://surferseo.com/blog/query-fan-out-impact/" rel="noopener noreferrer"&gt;2025 Surfer SEO study&lt;/a&gt; analyzing 173,902 URLs found that pages ranking for fan-out queries are &lt;strong&gt;161% more likely to be cited&lt;/strong&gt; in AI Overviews than pages ranking only for the main query. The Spearman correlation between fan-out coverage and citation likelihood was 0.77. And ranking for fan-out sub-queries alone was &lt;strong&gt;49% more likely to earn a citation&lt;/strong&gt; than ranking exclusively for the head term.&lt;/p&gt;

&lt;p&gt;You can actually see fan-out in action. Open dev tools on any AI chat interface, fire a query, and inspect the Network tab. In Claude, you'll see sequential &lt;code&gt;tool_use&lt;/code&gt; blocks in the JSON response payload showing your single query decomposed into multiple sub-queries in real time.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"tool_use_blocks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"tool_use"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"web_search"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"input"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"query"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"top SEO agencies Chicago 2026"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"sequence"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"tool_use"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"web_search"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"input"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"query"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"generative engine optimization GEO agency Chicago"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"sequence"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One user query. Two retrieval events. Two separate source pools. Your content needs to be present across all of them.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Real Model
&lt;/h2&gt;

&lt;p&gt;Here's how most of the SEO community is currently thinking about this:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Rank. Get retrieved. Get cited.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Here's what's actually happening:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Be indexable. Be verifiable. Be extractable at the chunk level. Have fan-out coverage. Increase selection probability.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Those are not the same instructions.&lt;/p&gt;

&lt;p&gt;Here's a realistic split: 70% of effective GEO is SEO. 20% is new tactical work around extractability and entity signals. And 10% is genuinely novel. AI visibility monitoring, citation tracking, scrubbing fan-outs, and earning brand mentions inside answers users actually trust.&lt;/p&gt;

&lt;p&gt;GEOs who close that gap now won't need to scramble when it becomes obvious to everyone else.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Full article with selection signals, platform breakdown, and measurement guidance: &lt;a href="https://zadroweb.com/blog/seo-vs-geo/" rel="noopener noreferrer"&gt;zadroweb.com/blog/seo-vs-geo/&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>seo</category>
      <category>ai</category>
      <category>webdev</category>
      <category>machinelearning</category>
    </item>
    <item>
      <title>Move Over Python: PHP Is the Sleeping Giant of AI Agents</title>
      <dc:creator>Dario Zadro</dc:creator>
      <pubDate>Wed, 29 Apr 2026 23:26:59 +0000</pubDate>
      <link>https://forem.com/zadro/move-over-python-php-is-the-sleeping-giant-of-ai-agents-577b</link>
      <guid>https://forem.com/zadro/move-over-python-php-is-the-sleeping-giant-of-ai-agents-577b</guid>
      <description>&lt;p&gt;I've been coding in PHP for over 20 years. I've watched it get declared dead at least a dozen times. And every time, it just keeps running more of the internet.&lt;/p&gt;

&lt;p&gt;So when AI started entering project needs, I didn't reach for Python. I didn't buy a seat in some workflow builder. I simply opened my existing codebase, wrote a service, and made an API call.&lt;/p&gt;

&lt;p&gt;That's it. That was the whole unlock.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Assumption Nobody Is Questioning
&lt;/h2&gt;

&lt;p&gt;Python owns AI. Everybody knows this. If you're training models or building research pipelines, Python is the right tool. Not up for debate.&lt;/p&gt;

&lt;p&gt;But here's the thing most teams aren't asking: are you actually training a model? Or are you calling an API?&lt;/p&gt;

&lt;p&gt;Because those are wildly different problems. Most businesses building "AI features" today are doing the latter. They're sending a prompt to Claude, GPT, or Gemini and doing something useful with the response. That's a REST call. REST calls are language-agnostic. And PHP has been making REST calls since before most AI startups existed.&lt;/p&gt;

&lt;h2&gt;
  
  
  You Already Have the Stack
&lt;/h2&gt;

&lt;p&gt;PHP powers over 71% of all websites with a known server-side language. Your CRM, your CMS, your admin panel -- already PHP. And AI agents at the production layer aren't exotic research software. They're authenticated API calls, database reads, webhook handlers, queue workers.&lt;/p&gt;

&lt;p&gt;Here's something the AI industry won't tell you: most "agents" are just good old services. Strip away the marketing, and a typical AI agent is a class that accepts input, calls an external API, applies some logic, and returns output. We've been writing those since the early 2000s.&lt;/p&gt;

&lt;p&gt;The agentic wrapper adds planning, memory, and tool-calling on top. Real, and it matters. But the underlying pattern is not foreign to anyone who has spent time in an MVC framework.&lt;/p&gt;

&lt;p&gt;So why spin up a Python microservice to sit next to your PHP application, duplicate your data context across a process boundary, and introduce an entirely new runtime to maintain, just to make an HTTP request to an LLM?&lt;/p&gt;

&lt;h2&gt;
  
  
  The Overkill Problem
&lt;/h2&gt;

&lt;p&gt;I've seen teams reach for AWS Bedrock to summarize a support ticket. I've seen n8n workflows with 14 nodes to do what is functionally a single API call. Make.com is a fantastic tool for connecting SaaS apps without code, but it is not the right answer for embedding an AI feature in a PHP application you already control.&lt;/p&gt;

&lt;p&gt;A working Claude integration in PHP:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;\GuzzleHttp\Client&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="nv"&gt;$response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$client&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'https://api.anthropic.com/v1/messages'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="s1"&gt;'headers'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="s1"&gt;'x-api-key'&lt;/span&gt;         &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$_ENV&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'ANTHROPIC_API_KEY'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="s1"&gt;'anthropic-version'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'2023-06-01'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'content-type'&lt;/span&gt;      &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'application/json'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="s1"&gt;'json'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="s1"&gt;'model'&lt;/span&gt;      &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'claude-haiku-4-5-20251001'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'max_tokens'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1024&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'messages'&lt;/span&gt;   &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'role'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'user'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'content'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$userPrompt&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;]);&lt;/span&gt;

&lt;span class="nv"&gt;$data&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;json_decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$response&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getBody&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nv"&gt;$reply&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'content'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s1"&gt;'text'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No platform. No monthly seat. No new infrastructure. Composer, Guzzle, an API key, and you're live.&lt;/p&gt;

&lt;h2&gt;
  
  
  What an Actual Agent Looks Like
&lt;/h2&gt;

&lt;p&gt;An API call waits for a response and returns it. An agent decides what to do next, calls tools, checks its own output, and loops until the task is done. Here's that shift in PHP using &lt;a href="https://neuron-ai.dev" rel="noopener noreferrer"&gt;Neuron AI&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kn"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;App\Neuron&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;NeuronAI\Agent\Agent&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;NeuronAI\Agent\SystemPrompt&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;NeuronAI\Chat\Messages\UserMessage&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;NeuronAI\Providers\AIProviderInterface&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;NeuronAI\Providers\Anthropic\Anthropic&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;NeuronAI\Tools\PropertyType&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;NeuronAI\Tools\Tool&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;NeuronAI\Tools\ToolProperty&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;FitnessAgent&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Agent&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;AIProviderInterface&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Anthropic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;   &lt;span class="nv"&gt;$_ENV&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'ANTHROPIC_API_KEY'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'claude-haiku-4-5-20251001'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;instructions&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;SystemPrompt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;background&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'You are a knowledgeable fitness assistant.'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="n"&gt;steps&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;      &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'Use available tools to look up workout plans before answering questions about them.'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;     &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'Give clear, practical guidance based on the workout data returned.'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;tools&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;array&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="nc"&gt;Tool&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;make&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'get_workout'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Look up a workout plan by name or muscle group.'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;addProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                    &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ToolProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                        &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;        &lt;span class="s1"&gt;'workout_name'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                        &lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;        &lt;span class="nc"&gt;PropertyType&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;STRING&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                        &lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'The name or muscle group of the workout to retrieve.'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                        &lt;span class="n"&gt;required&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;    &lt;span class="kc"&gt;true&lt;/span&gt;
                    &lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;setCallable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$workout_name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="nv"&gt;$pdo&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;\PDO&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$_ENV&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'DB_DSN'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nv"&gt;$_ENV&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'DB_USER'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nv"&gt;$_ENV&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'DB_PASS'&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
                    &lt;span class="nv"&gt;$stmt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$pdo&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;prepare&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"SELECT exercises, sets, reps FROM workouts WHERE name = ?"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                    &lt;span class="nv"&gt;$stmt&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nv"&gt;$workout_name&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
                    &lt;span class="nv"&gt;$row&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$stmt&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;\PDO&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;FETCH_ASSOC&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$row&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="nb"&gt;json_encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$row&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'Workout not found.'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="p"&gt;}),&lt;/span&gt;
        &lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nv"&gt;$reply&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;FitnessAgent&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;make&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;chat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;UserMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'How many sets should I do for a beginner chest workout?'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getMessage&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getContent&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$reply&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The agent receives the question, decides it needs the &lt;code&gt;get_workout&lt;/code&gt; tool, calls it with the extracted muscle group or plan name, and folds the result into its response. All without you wiring that logic manually. That's the shift from API wrapper to agent.&lt;/p&gt;

&lt;h2&gt;
  
  
  PHP's Territory
&lt;/h2&gt;

&lt;p&gt;Python wins at model training, ML research, data science pipelines, and computer vision. Uncontested. Go use Python for that.&lt;/p&gt;

&lt;p&gt;PHP's territory is the production web layer. CRMs, dashboards, ecommerce platforms, CMS systems, admin panels, business workflow tools. That layer is already PHP. The agents should be too.&lt;/p&gt;

&lt;p&gt;Your agent needs access to your authenticated user session, your database schema, your business rules, your caching layer. Transferring all of that to an external Python service isn't just overhead. It's a technical debt liability.&lt;/p&gt;

&lt;p&gt;You need an API key, a service class, and the PHP codebase you've probably already been running in production.&lt;/p&gt;

&lt;p&gt;The giant was never asleep. Everyone else just has their eyes closed.&lt;/p&gt;




&lt;p&gt;For the full breakdown including RAG in PHP, MCP, FrankenPHP, and the complete ecosystem rundown, read the full article at &lt;a href="https://zadroweb.com/blog/php-ai-agents" rel="noopener noreferrer"&gt;zadroweb.com&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>php</category>
      <category>laravel</category>
    </item>
    <item>
      <title>Missing Database Indexes Are Killing Your Page Speed</title>
      <dc:creator>Dario Zadro</dc:creator>
      <pubDate>Thu, 12 Mar 2026 15:54:21 +0000</pubDate>
      <link>https://forem.com/zadro/missing-database-indexes-are-killing-your-page-speed-4kod</link>
      <guid>https://forem.com/zadro/missing-database-indexes-are-killing-your-page-speed-4kod</guid>
      <description>&lt;p&gt;I provide SEO. I also build apps. And the performance problem I see most often has nothing to do with meta tags, Core Web Vitals scores, or caching plugins.&lt;/p&gt;

&lt;p&gt;It's missing database indexes.&lt;/p&gt;

&lt;p&gt;Everyone chases the green scores. Images get lazy-loaded. Fonts get preloaded. Someone installs a caching plugin and calls it done. Green across the board. Screenshot posted on LinkedIn. Everybody claps.&lt;/p&gt;

&lt;p&gt;But the uncached request still exists. The logged-in user still exists. The admin dashboard still exists. The report page pulling from six tables still exists. Caching hides the problem. It doesn't fix it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Fast Diagnostic
&lt;/h2&gt;

&lt;p&gt;Enable MySQL's slow query log:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="k"&gt;GLOBAL&lt;/span&gt; &lt;span class="n"&gt;slow_query_log&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'ON'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="k"&gt;GLOBAL&lt;/span&gt; &lt;span class="n"&gt;long_query_time&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;SHOW&lt;/span&gt; &lt;span class="n"&gt;VARIABLES&lt;/span&gt; &lt;span class="k"&gt;LIKE&lt;/span&gt; &lt;span class="s1"&gt;'slow_query_log_file'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Reproduce the slow page. Check the log. Then run EXPLAIN on whatever shows up:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;EXPLAIN&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;parent_table&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;
&lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;child_table&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parent_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;account_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;42&lt;/span&gt; &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Four columns tell the whole story: &lt;code&gt;type&lt;/code&gt;, &lt;code&gt;key&lt;/code&gt;, &lt;code&gt;rows&lt;/code&gt;, and &lt;code&gt;Extra&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;ALL&lt;/code&gt; in &lt;code&gt;type&lt;/code&gt; with &lt;code&gt;NULL&lt;/code&gt; in &lt;code&gt;key&lt;/code&gt; is the thing you don't want to see. Especially inside a subquery running once per parent row. Not mysterious. Just waste.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Fixes
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Foreign key columns&lt;/strong&gt; — index every column used in a JOIN:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;ALTER&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;child_table&lt;/span&gt; &lt;span class="k"&gt;ADD&lt;/span&gt; &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="n"&gt;idx_parent_id&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;parent_id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Composite indexes&lt;/strong&gt; — column order matters. Most-filtered column goes first:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;ALTER&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;parent_table&lt;/span&gt;
&lt;span class="k"&gt;ADD&lt;/span&gt; &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="n"&gt;idx_account_status_created&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;account_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;created_at&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;JSON extractions&lt;/strong&gt; — can't index a computed expression directly. Use a generated column:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;ALTER&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;event_log&lt;/span&gt;
&lt;span class="k"&gt;ADD&lt;/span&gt; &lt;span class="k"&gt;COLUMN&lt;/span&gt; &lt;span class="n"&gt;event_source&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;GENERATED&lt;/span&gt; &lt;span class="n"&gt;ALWAYS&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;JSON_UNQUOTE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;JSON_EXTRACT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'$.source'&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt; &lt;span class="n"&gt;VIRTUAL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="k"&gt;ADD&lt;/span&gt; &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="n"&gt;idx_event_source&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event_source&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Don't overdo it&lt;/strong&gt; — every index slows writes. Index what you actually query. That's it.&lt;/p&gt;

&lt;h2&gt;
  
  
  WordPress Specifically
&lt;/h2&gt;

&lt;p&gt;WordPress runs 42.5% of the web (W3Techs, March 2026). And &lt;code&gt;wp_postmeta&lt;/code&gt; does not scale gracefully under plugin pressure. Don't even get me started on RankMath's database bloat.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://wordpress.org/plugins/index-wp-mysql-for-speed/" rel="noopener noreferrer"&gt;Index WP MySQL For Speed&lt;/a&gt; plugin by Ollie Jones is a solid first move for standard installs. Heavily customized sites need a manual EXPLAIN review.&lt;/p&gt;

&lt;h2&gt;
  
  
  Real Result
&lt;/h2&gt;

&lt;p&gt;A dashboard recently: 3+ seconds to load, 120,000 records, correlated subqueries hitting unindexed join columns. Thousands of row reads per load, every single request.&lt;/p&gt;

&lt;p&gt;Three index statements. Instant. Same server, same code, same database.&lt;/p&gt;




&lt;p&gt;Check the DB structure before you blame the host or code. The answer is usually closer than you think.&lt;/p&gt;

&lt;p&gt;Full breakdown on the Zadro Web blog: &lt;a href="https://zadroweb.com/blog/database-indexes-slow-website/" rel="noopener noreferrer"&gt;https://zadroweb.com/blog/database-indexes-slow-website/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>mysql</category>
      <category>webdev</category>
      <category>performance</category>
      <category>wordpress</category>
    </item>
  </channel>
</rss>
