<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
  xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>tkulesza.eu — notes from production</title>
    <link>https://tkulesza.eu</link>
    <description>Long-form notes on PHP architecture, design patterns that survive contact with reality, and AI agents built into systems that can't afford a rewrite.</description>
    <language>en</language>
    <managingEditor>info@tkulesza.eu (Tomasz Kulesza)</managingEditor>
    <atom:link href="https://tkulesza.eu/feed.xml" rel="self" type="application/rss+xml"/>
  <item>
    <title><![CDATA[Your RAG bot is confidently wrong and you will not notice until a customer calls]]></title>
    <link>https://tkulesza.eu/articles/n8n-rag-data-quality</link>
    <guid isPermaLink="true">https://tkulesza.eu/articles/n8n-rag-data-quality</guid>
    <description><![CDATA[The chatbot had been running in production for six weeks before anyone noticed it was quoting a price list from nine months ago. Not occasionally. Every time someone asked about the premium tier, it would cheerfully produce a number that was wrong by forty percent and cite the source document with full confidence. The source document was real. It was just from before the November repricing. Somewhere in the vector store it sat next to the updated sheet, and the retriever kept picking it because the query "how much does the premium plan cost" matched the old document's phrasing better than the new one. Cosine similarity does not care about recency. The model did not know it was being lied to. Neither did we, for six weeks.]]></description>
    <pubDate>Fri, 22 May 2026 00:00:00 GMT</pubDate>
    <category>n8n</category>
      <category>rag</category>
      <category>vector-db</category>
      <category>chatbot</category>
      <category>data-quality</category>
  </item>
  <item>
    <title><![CDATA[The hidden cost of microservice boundaries: a five-year retrospective]]></title>
    <link>https://tkulesza.eu/articles/microservice-cost</link>
    <guid isPermaLink="true">https://tkulesza.eu/articles/microservice-cost</guid>
    <description><![CDATA[In 2021 we drew 47 boxes on a whiteboard. The number was generated by the Institute of Ass-Pulled Data: nobody measured anything, we just felt that 47 was about right for the size of the domain. By 2024, 22 of those boxes were back inside other boxes. This is not a story about microservices being wrong. It is a story about the boundary between two services being the most expensive thing in your system to change, and us not knowing that yet.]]></description>
    <pubDate>Thu, 14 May 2026 00:00:00 GMT</pubDate>
    <category>architecture</category>
      <category>organisation</category>
      <category>platform</category>
      <category>devex</category>
  </item>
  <item>
    <title><![CDATA[Building production AI agents with stateful graph orchestration]]></title>
    <link>https://tkulesza.eu/articles/agent-graphs</link>
    <guid isPermaLink="true">https://tkulesza.eu/articles/agent-graphs</guid>
    <description><![CDATA[Once an agent has more than two tools, the "ReAct loop" stops being an architecture and starts being a liability. Latency stacks, error modes multiply, and there is nowhere honest to draw a boundary for retries.]]></description>
    <pubDate>Sat, 09 May 2026 00:00:00 GMT</pubDate>
    <category>ai-agents</category>
      <category>langgraph</category>
      <category>state-machines</category>
      <category>observability</category>
  </item>
  <item>
    <title><![CDATA[Postgres at the edge: rethinking primary keys for global writes]]></title>
    <link>https://tkulesza.eu/articles/postgres-edge</link>
    <guid isPermaLink="true">https://tkulesza.eu/articles/postgres-edge</guid>
    <description><![CDATA[A serial primary key is not a key. It is a coordinated agreement between every writer that they will take turns. Honour that agreement across two regions and you have, by definition, given up either availability or freshness.]]></description>
    <pubDate>Mon, 30 Mar 2026 00:00:00 GMT</pubDate>
    <category>postgres</category>
      <category>distributed-systems</category>
      <category>ulid</category>
      <category>replication</category>
  </item>
  <item>
    <title><![CDATA[LLMs in PHP: integrating language models into production systems without rewriting everything]]></title>
    <link>https://tkulesza.eu/articles/llm-in-php</link>
    <guid isPermaLink="true">https://tkulesza.eu/articles/llm-in-php</guid>
    <description><![CDATA[Every team I have spoken to over the last two years has had the same conversation. Engineers want to add LLM features, the CTO says Python, and the platform team, which owns the PHP monolith with ten years of business logic, goes quiet. The argument is that ML tooling is Python-first, LLM SDKs are better in Python, and that is where the talent pool is.]]></description>
    <pubDate>Mon, 28 Oct 2024 00:00:00 GMT</pubDate>
    <category>php</category>
      <category>llm</category>
      <category>ai-agents</category>
      <category>llphant</category>
      <category>openai</category>
      <category>production</category>
  </item>
  <item>
    <title><![CDATA[The Singleton trap: global state, PHP-FPM workers, and the pattern that aged poorly]]></title>
    <link>https://tkulesza.eu/articles/singleton-pattern</link>
    <guid isPermaLink="true">https://tkulesza.eu/articles/singleton-pattern</guid>
    <description><![CDATA[I have been in exactly two code reviews where a developer proposed a Singleton and was right to do so. I have been in perhaps forty where they were not. The pattern is not the problem. The problem is that "I only want one of these" sounds like the right motivation almost every time, and it almost never is.]]></description>
    <pubDate>Tue, 15 Oct 2024 00:00:00 GMT</pubDate>
    <category>php</category>
      <category>design-patterns</category>
      <category>dependency-injection</category>
      <category>architecture</category>
      <category>testing</category>
      <category>php-fpm</category>
  </item>
  <item>
    <title><![CDATA[Factory Method: the pattern nobody needs until they need it for everything]]></title>
    <link>https://tkulesza.eu/articles/factory-method</link>
    <guid isPermaLink="true">https://tkulesza.eu/articles/factory-method</guid>
    <description><![CDATA[The textbook examples for Factory Method involve shapes and animals. ShapeFactory returning a Circle or Square based on a string. AnimalFactory constructing a Dog or a Cat. These examples are correct. They are also useless as design guidance, because nobody's business domain involves shapes.]]></description>
    <pubDate>Mon, 15 Jul 2024 00:00:00 GMT</pubDate>
    <category>php</category>
      <category>design-patterns</category>
      <category>factory</category>
      <category>dependency-injection</category>
      <category>architecture</category>
  </item>
  <item>
    <title><![CDATA[Ansible beyond the tutorial: idempotency, drift detection, and the playbook that saved a 3am incident]]></title>
    <link>https://tkulesza.eu/articles/ansible-production</link>
    <guid isPermaLink="true">https://tkulesza.eu/articles/ansible-production</guid>
    <description><![CDATA[The demo playbook installs nginx and starts it. It works once on a clean VM and everyone nods in the meeting. What nobody demonstrates is running the same playbook six months later on a server where an engineer manually edited /etc/nginx/nginx.conf to temporarily fix a production problem and then forgot to document it. Or after the nginx package got updated by an unnoticed apt cron job. Or on a server that was never properly converged because someone cancelled the playbook halfway through.]]></description>
    <pubDate>Mon, 06 May 2024 00:00:00 GMT</pubDate>
    <category>ansible</category>
      <category>devops</category>
      <category>infrastructure</category>
      <category>automation</category>
      <category>idempotency</category>
      <category>configuration-management</category>
  </item>
  <item>
    <title><![CDATA[Finite state machines in PHP: modelling order lifecycle without the spaghetti]]></title>
    <link>https://tkulesza.eu/articles/state-machine</link>
    <guid isPermaLink="true">https://tkulesza.eu/articles/state-machine</guid>
    <description><![CDATA[The bug report said: "Customer was charged twice for the same order." The order was in status payment_pending. A frontend timeout caused the customer to click "Pay" again. The second click triggered a new payment intent. Both intents completed within 200 milliseconds of each other. Neither the frontend nor the backend had a mechanism to prevent a second payment on an order that was already being charged.]]></description>
    <pubDate>Sat, 10 Feb 2024 00:00:00 GMT</pubDate>
    <category>php</category>
      <category>design-patterns</category>
      <category>state-machine</category>
      <category>fsm</category>
      <category>ddd</category>
      <category>order-management</category>
  </item>
  <item>
    <title><![CDATA[The Bridge pattern: separating what you send from how you send it]]></title>
    <link>https://tkulesza.eu/articles/bridge-pattern</link>
    <guid isPermaLink="true">https://tkulesza.eu/articles/bridge-pattern</guid>
    <description><![CDATA[The Bridge pattern is explained in most textbooks with shapes and rendering APIs. A Shape hierarchy and a Renderer hierarchy, bridged together. The examples are correct and entirely useless as design guidance because nobody's production system is drawing shapes.]]></description>
    <pubDate>Thu, 19 Oct 2023 00:00:00 GMT</pubDate>
    <category>php</category>
      <category>design-patterns</category>
      <category>bridge</category>
      <category>architecture</category>
      <category>notifications</category>
      <category>abstraction</category>
  </item>
  <item>
    <title><![CDATA[PHP references: the footgun that ships faster than you think]]></title>
    <link>https://tkulesza.eu/articles/php-references</link>
    <guid isPermaLink="true">https://tkulesza.eu/articles/php-references</guid>
    <description><![CDATA[PHP references are one of the few language features the PHP manual explicitly warns against using unnecessarily. The warning is warranted. I have debugged three separate production incidents caused by references, and in two of them the original developer was not aware they had introduced a reference at all.]]></description>
    <pubDate>Sat, 30 Sep 2023 00:00:00 GMT</pubDate>
    <category>php</category>
      <category>memory</category>
      <category>debugging</category>
      <category>references</category>
      <category>performance</category>
      <category>footguns</category>
  </item>
  <item>
    <title><![CDATA[Design patterns in production: what they solve, what they cost, and when to skip them]]></title>
    <link>https://tkulesza.eu/articles/design-patterns-production</link>
    <guid isPermaLink="true">https://tkulesza.eu/articles/design-patterns-production</guid>
    <description><![CDATA[The GoF book was published in 1994. In the thirty years since, design patterns have gone through at least three complete cycles: introduction, over-application, backlash, and cautious re-adoption. We are somewhere in the fourth or fifth cycle now, depending on which part of the industry you are in.]]></description>
    <pubDate>Fri, 10 Feb 2023 00:00:00 GMT</pubDate>
    <category>design-patterns</category>
      <category>architecture</category>
      <category>php</category>
      <category>refactoring</category>
      <category>code-review</category>
  </item>
  </channel>
</rss>