<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet href="https://www.yellowduck.be/pretty-atom-feed-v3.xsl" type="text/xsl"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <link href="https://www.yellowduck.be" rel="alternate"/>
  <link href="https://www.yellowduck.be/posts/feed" rel="self"/>
  <author>
    <name>Pieter Claerhout</name>
    <email>pieter@yellowduck.be</email>
  </author>
  <id>https://www.yellowduck.be/posts/feed</id>
  <title>🐥 YellowDuck.be</title>
  <updated>2026-10-17T08:00:00Z</updated>
  <entry>
    <author>
      <name>Pieter Claerhout</name>
      <email>pieter@yellowduck.be</email>
    </author>
    <link href="https://www.yellowduck.be/posts/python-3-15-preview-lazy-imports" rel="alternate"/>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;Python 3.15 adds lazy imports: a lazy keyword that puts off loading a module until the first time you use it. The import statement stays where it belongs, at the top of your file, but the work of running it moves to the moment something touches the module’s name. In this tutorial, you’ll make a command-line tool start more than twice as fast without moving a single import.&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;&lt;a href=&quot;https://realpython.com/python315-lazy-imports/&quot;&gt;Continue reading on &lt;strong&gt;realpython.com&lt;/strong&gt;&lt;/a&gt;&lt;p&gt;&lt;a href=&quot;https://www.yellowduck.be/tags/reading-list&quot;&gt;#reading-list&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/development&quot;&gt;#development&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/python&quot;&gt;#python&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/best-practice&quot;&gt;#best-practice&lt;/a&gt;&lt;/p&gt;</content>
    <published>2026-10-17T08:00:00Z</published>
    <id>https://www.yellowduck.be/posts/python-3-15-preview-lazy-imports</id>
    <title>🔗 Python 3.15 Preview: Lazy Imports</title>
    <updated>2026-10-17T08:00:00Z</updated>
  </entry>
  <entry>
    <author>
      <name>Pieter Claerhout</name>
      <email>pieter@yellowduck.be</email>
    </author>
    <link href="https://www.yellowduck.be/posts/python-3-15-is-bigger-than-you-think-7-new-features-worth-using-in-2026" rel="alternate"/>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;Python 3.15 brings significant enhancements beyond typical library improvements. It introduces important features like &lt;code&gt;lazy import&lt;/code&gt;, which allows deferring module loading until needed, and the &lt;code&gt;frozendict&lt;/code&gt;, providing a new immutable mapping type. The &lt;code&gt;sentinel()&lt;/code&gt; function replaces the &lt;code&gt;_MISSING&lt;/code&gt; trick for optional values, helping to clarify function parameters. Comprehensions can now directly unpack iterables, simplifying data processing tasks. These features not only simplify code but also enhance performance, making them valuable for developers looking to future-proof their Python projects. With the release still in prerelease, now is an ideal time to explore these updates without affecting production systems. Expect improved readability and maintainability, catering to modern Python practices and enhancing overall coding efficiency.&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;&lt;a href=&quot;https://freedium-mirror.cfd/https://captain-solaris.medium.com/python-3-15-is-bigger-than-you-think-7-new-features-worth-using-in-2026-0235523fbc03&quot;&gt;Continue reading on &lt;strong&gt;freedium-mirror.cfd&lt;/strong&gt;&lt;/a&gt;&lt;p&gt;&lt;a href=&quot;https://www.yellowduck.be/tags/reading-list&quot;&gt;#reading-list&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/development&quot;&gt;#development&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/python&quot;&gt;#python&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/best-practice&quot;&gt;#best-practice&lt;/a&gt;&lt;/p&gt;</content>
    <published>2026-10-16T17:00:00Z</published>
    <id>https://www.yellowduck.be/posts/python-3-15-is-bigger-than-you-think-7-new-features-worth-using-in-2026</id>
    <title>🔗 Python 3.15 is bigger than you think: 7 new features worth using in 2026</title>
    <updated>2026-10-16T17:00:00Z</updated>
  </entry>
  <entry>
    <author>
      <name>Pieter Claerhout</name>
      <email>pieter@yellowduck.be</email>
    </author>
    <link href="https://www.yellowduck.be/posts/how-to-fix-nonetype-object-has-no-attribute-errors-without-guessing" rel="alternate"/>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;This article unpacks the confusion surrounding the common Python error: &lt;code&gt;AttributeError: &apos;NoneType&apos; object has no attribute &apos;name&apos;&lt;/code&gt;. It clarifies that the error is not indicative of a flawed program, but a clear signal that a variable is &lt;code&gt;None&lt;/code&gt; when it shouldn’t be. By following a structured debugging process, readers learn that fixing the problem involves identifying where the &lt;code&gt;None&lt;/code&gt; value originated, confirming its state, and determining appropriate handling for potentially missing data. This approach helps developers avoid superficial fixes that merely address the symptom rather than the root cause. Understanding this process equips programmers with the confidence to tackle similar issues effectively in their own code.&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;&lt;a href=&quot;https://systemcraftpress.com/blog/nonetype-has-no-attribute/&quot;&gt;Continue reading on &lt;strong&gt;systemcraftpress.com&lt;/strong&gt;&lt;/a&gt;&lt;p&gt;&lt;a href=&quot;https://www.yellowduck.be/tags/reading-list&quot;&gt;#reading-list&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/development&quot;&gt;#development&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/python&quot;&gt;#python&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/best-practice&quot;&gt;#best-practice&lt;/a&gt;&lt;/p&gt;</content>
    <published>2026-10-16T13:00:00Z</published>
    <id>https://www.yellowduck.be/posts/how-to-fix-nonetype-object-has-no-attribute-errors-without-guessing</id>
    <title>🔗 How to fix &apos;NoneType&apos; object has no attribute errors (without guessing)</title>
    <updated>2026-10-16T13:00:00Z</updated>
  </entry>
  <entry>
    <author>
      <name>Pieter Claerhout</name>
      <email>pieter@yellowduck.be</email>
    </author>
    <link href="https://www.yellowduck.be/posts/where-rss-is-quietly-heading-decentralization-ai-and-the-return-of-owning-your-feed" rel="alternate"/>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;RSS has long been a niche tool, favored by tech-savvy users but alienating to everyday people. The article highlights the evolving landscape where two factions are emerging: one yearns for decentralization and control over their feeds, while the other craves modern features that enhance usability. Recent advancements in AI are bridging this gap, enabling apps that transform RSS by eliminating technical complexities. Users can now access their sources easily, benefiting from features like audio summaries and smart organization without losing ownership of their content. As technology advances, RSS is becoming more accessible to a wider audience, positioning itself uniquely where decentralization meets enhanced functionality.&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;&lt;a href=&quot;https://safeandinformed.com/blog/where-rss-is-quietly-heading&quot;&gt;Continue reading on &lt;strong&gt;safeandinformed.com&lt;/strong&gt;&lt;/a&gt;&lt;p&gt;&lt;a href=&quot;https://www.yellowduck.be/tags/reading-list&quot;&gt;#reading-list&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/ai&quot;&gt;#ai&lt;/a&gt;&lt;/p&gt;</content>
    <published>2026-10-16T08:00:00Z</published>
    <id>https://www.yellowduck.be/posts/where-rss-is-quietly-heading-decentralization-ai-and-the-return-of-owning-your-feed</id>
    <title>🔗 Where RSS is quietly heading — decentralization, AI, and the return of owning your feed</title>
    <updated>2026-10-16T08:00:00Z</updated>
  </entry>
  <entry>
    <author>
      <name>Pieter Claerhout</name>
      <email>pieter@yellowduck.be</email>
    </author>
    <link href="https://www.yellowduck.be/posts/rss-doesnt-have-to-be-boring" rel="alternate"/>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;Ever thought of revamping your RSS feed experience? The article details a makeover done at new.bitecode.dev, enhancing how users subscribe to content. The author explains how traditional RSS feeds often fall short, especially when relying on platforms like Substack, which focus only on the basics. By implementing a custom solution with Django, the site now allows users to filter which articles or notes they want to follow, transforming the passive reading experience into a tailored one. The author emphasizes that RSS is flexible and can empower users to pull exactly what they want, debunking the myth that it’s outdated or boring. The article invites technical enthusiasts to explore these enhancements and realize the potential of RSS feeds in a modern context, even hinting at distinctive applications like personalized content delivery and bot integrations.&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;&lt;a href=&quot;https://www.bitecode.dev/p/rss-doesnt-have-to-be-boring&quot;&gt;Continue reading on &lt;strong&gt;www.bitecode.dev&lt;/strong&gt;&lt;/a&gt;&lt;p&gt;&lt;a href=&quot;https://www.yellowduck.be/tags/reading-list&quot;&gt;#reading-list&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/frontend&quot;&gt;#frontend&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/django&quot;&gt;#django&lt;/a&gt;&lt;/p&gt;</content>
    <published>2026-10-15T17:00:00Z</published>
    <id>https://www.yellowduck.be/posts/rss-doesnt-have-to-be-boring</id>
    <title>🔗 RSS doesn’t have to be boring</title>
    <updated>2026-10-15T17:00:00Z</updated>
  </entry>
  <entry>
    <author>
      <name>Pieter Claerhout</name>
      <email>pieter@yellowduck.be</email>
    </author>
    <link href="https://www.yellowduck.be/posts/ruff-mypy-pytest-and-then-what" rel="alternate"/>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;Structural quality in AI-written Python code is often overlooked. The article reveals how tools like Ruff, mypy, and pytest don’t catch issues like code duplication, which can frustrate agents during development. Agents sometimes mirror code across multiple functions and modules without realizing it, leading to confusion and inefficiency. By running structural analyses on a repository and leveraging tools like pyscn, the author shows the impact of code complexity and duplication over time. A few strategic changes can mitigate these issues for better collaboration between human developers and AI. Clear rules for function imports and tracking average complexity are essential for maintaining a clean codebase. Understanding and addressing these pitfalls can enhance the overall structure and readability of Python projects, benefiting both human and AI developers alike.&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;&lt;a href=&quot;https://codescan.dev/blog/ruff-mypy-pytest-and-then-what&quot;&gt;Continue reading on &lt;strong&gt;codescan.dev&lt;/strong&gt;&lt;/a&gt;&lt;p&gt;&lt;a href=&quot;https://www.yellowduck.be/tags/reading-list&quot;&gt;#reading-list&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/python&quot;&gt;#python&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/best-practice&quot;&gt;#best-practice&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/ai&quot;&gt;#ai&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/testing&quot;&gt;#testing&lt;/a&gt;&lt;/p&gt;</content>
    <published>2026-10-15T13:00:00Z</published>
    <id>https://www.yellowduck.be/posts/ruff-mypy-pytest-and-then-what</id>
    <title>🔗 Ruff, MyPy, pytest, and then what?</title>
    <updated>2026-10-15T13:00:00Z</updated>
  </entry>
  <entry>
    <author>
      <name>Pieter Claerhout</name>
      <email>pieter@yellowduck.be</email>
    </author>
    <link href="https://www.yellowduck.be/posts/finding-your-broken-glass-a-flaky-test-prompt-for-github-actions" rel="alternate"/>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;Test flakes can wreak havoc on CI processes, costing teams not just time but also developer morale. Mike Zornek highlights the importance of addressing flaky tests in GitHub Actions workflows. By analyzing test failures, teams can quantify the negative impact and uncover root causes, leading to more robust development practices. Zornek provides a practical prompt for using AI tools to identify flaky tests and suggests how to improve the situation. Understanding the cost of these inefficiencies is key to maintaining a happy and productive development environment.&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;&lt;a href=&quot;https://mikezornek.com/posts/2026/9/finding-flaky-tests/&quot;&gt;Continue reading on &lt;strong&gt;mikezornek.com&lt;/strong&gt;&lt;/a&gt;&lt;p&gt;&lt;a href=&quot;https://www.yellowduck.be/tags/reading-list&quot;&gt;#reading-list&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/ai&quot;&gt;#ai&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/devops&quot;&gt;#devops&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/github&quot;&gt;#github&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/testing&quot;&gt;#testing&lt;/a&gt;&lt;/p&gt;</content>
    <published>2026-10-15T08:00:00Z</published>
    <id>https://www.yellowduck.be/posts/finding-your-broken-glass-a-flaky-test-prompt-for-github-actions</id>
    <title>🔗 Finding your broken glass: A flaky test prompt for GitHub Actions</title>
    <updated>2026-10-15T08:00:00Z</updated>
  </entry>
  <entry>
    <author>
      <name>Pieter Claerhout</name>
      <email>pieter@yellowduck.be</email>
    </author>
    <link href="https://www.yellowduck.be/posts/handling-local-timezones-in-ash-while-storing-utc-datetimes" rel="alternate"/>
    <content type="html">&lt;p&gt;When building Elixir applications with the Ash Framework, it&apos;s a best practice to store all datetimes in UTC in your database. However, users typically interact with your application in their &lt;strong&gt;local timezone&lt;/strong&gt;, so you need a reliable way to convert their input into UTC—and show it back in their local time.&lt;/p&gt;
&lt;p&gt;This post shows how to handle that conversion cleanly and reusably in Ash actions using a custom &lt;code&gt;change&lt;/code&gt; module.&lt;/p&gt;
&lt;h1&gt;Why store in UTC?&lt;/h1&gt;
&lt;p&gt;Storing all datetimes in UTC avoids timezone ambiguity and makes it easier to compare, sort, and query times consistently—especially if your users are spread across multiple regions. PostgreSQL and Ash support UTC datetime types (&lt;code&gt;:utc_datetime&lt;/code&gt;), and your application should convert values at the boundaries: when receiving input or displaying output.&lt;/p&gt;
&lt;h1&gt;Define UTC datetime attributes&lt;/h1&gt;
&lt;p&gt;First, define the attributes on your Ash resource using &lt;code&gt;:utc_datetime&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;lumis&quot;&gt;&lt;code class=&quot;language-elixir&quot; translate=&quot;no&quot; tabindex=&quot;0&quot;&gt;&lt;span class=&quot;l-line&quot; data-line=&quot;1&quot;&gt;&lt;span class=&quot;l-function-call&quot;&gt;attributes&lt;/span&gt; &lt;span class=&quot;l-keyword&quot;&gt;do&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;2&quot;&gt;  &lt;span class=&quot;l-function-call&quot;&gt;attribute&lt;/span&gt; &lt;span class=&quot;l-string-special-symbol&quot;&gt;:starts_at&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-string-special-symbol&quot;&gt;:utc_datetime&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;3&quot;&gt;  &lt;span class=&quot;l-function-call&quot;&gt;attribute&lt;/span&gt; &lt;span class=&quot;l-string-special-symbol&quot;&gt;:ends_at&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-string-special-symbol&quot;&gt;:utc_datetime&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;4&quot;&gt;&lt;span class=&quot;l-keyword&quot;&gt;end&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;Add user-local input arguments&lt;/h1&gt;
&lt;p&gt;In your Ash action, accept user-local datetime input as strings, &lt;code&gt;NaiveDateTime&lt;/code&gt;, or &lt;code&gt;DateTime&lt;/code&gt; structs:&lt;/p&gt;
&lt;pre class=&quot;lumis&quot;&gt;&lt;code class=&quot;language-elixir&quot; translate=&quot;no&quot; tabindex=&quot;0&quot;&gt;&lt;span class=&quot;l-line&quot; data-line=&quot;1&quot;&gt;&lt;span class=&quot;l-function-call&quot;&gt;actions&lt;/span&gt; &lt;span class=&quot;l-keyword&quot;&gt;do&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;2&quot;&gt;  &lt;span class=&quot;l-function-call&quot;&gt;create&lt;/span&gt; &lt;span class=&quot;l-string-special-symbol&quot;&gt;:schedule&lt;/span&gt; &lt;span class=&quot;l-keyword&quot;&gt;do&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;3&quot;&gt;    &lt;span class=&quot;l-function-call&quot;&gt;argument&lt;/span&gt; &lt;span class=&quot;l-string-special-symbol&quot;&gt;:starts_at_local&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-string-special-symbol&quot;&gt;:string&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;4&quot;&gt;    &lt;span class=&quot;l-function-call&quot;&gt;argument&lt;/span&gt; &lt;span class=&quot;l-string-special-symbol&quot;&gt;:ends_at_local&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-string-special-symbol&quot;&gt;:naive_datetime&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;5&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;6&quot;&gt;    &lt;span class=&quot;l-function-call&quot;&gt;change&lt;/span&gt; &lt;span class=&quot;l-punctuation-bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;l-module&quot;&gt;MyApp.Changes.ConvertLocalDatetimeToUtc&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-string-special-symbol&quot;&gt;arg_key: :starts_at_local&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-string-special-symbol&quot;&gt;field: :starts_at&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;7&quot;&gt;    &lt;span class=&quot;l-function-call&quot;&gt;change&lt;/span&gt; &lt;span class=&quot;l-punctuation-bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;l-module&quot;&gt;MyApp.Changes.ConvertLocalDatetimeToUtc&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-string-special-symbol&quot;&gt;arg_key: :ends_at_local&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-string-special-symbol&quot;&gt;field: :ends_at&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;8&quot;&gt;  &lt;span class=&quot;l-keyword&quot;&gt;end&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;9&quot;&gt;&lt;span class=&quot;l-keyword&quot;&gt;end&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;Reusable change module&lt;/h1&gt;
&lt;p&gt;Ash&apos;s &lt;code&gt;change&lt;/code&gt; DSL expects a module implementing &lt;code&gt;Ash.Resource.Change&lt;/code&gt;. Here&apos;s a reusable implementation you can drop into your project:&lt;/p&gt;
&lt;pre class=&quot;lumis&quot;&gt;&lt;code class=&quot;language-elixir&quot; translate=&quot;no&quot; tabindex=&quot;0&quot;&gt;&lt;span class=&quot;l-line&quot; data-line=&quot;1&quot;&gt;&lt;span class=&quot;l-keyword-function&quot;&gt;defmodule&lt;/span&gt; &lt;span class=&quot;l-module&quot;&gt;MyApp.Changes.ConvertLocalDatetimeToUtc&lt;/span&gt; &lt;span class=&quot;l-keyword-function&quot;&gt;do&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;2&quot;&gt;  &lt;span class=&quot;l-comment&quot;&gt;@moduledoc&lt;/span&gt; &lt;span class=&quot;l-comment&quot;&gt;&quot;&quot;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;3&quot;&gt;&lt;span class=&quot;l-comment&quot;&gt;  Converts a local datetime argument (string, NaiveDateTime, or DateTime)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;4&quot;&gt;&lt;span class=&quot;l-comment&quot;&gt;  to UTC and assigns it to an Ash attribute. Gracefully handles &lt;/span&gt;&lt;span class=&quot;l-markup-raw&quot;&gt;`nil`&lt;/span&gt;&lt;span class=&quot;l-comment&quot;&gt; values.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;5&quot;&gt;&lt;span class=&quot;l-comment&quot;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;6&quot;&gt;&lt;span class=&quot;l-comment&quot;&gt;  Options:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;7&quot;&gt;&lt;span class=&quot;l-comment&quot;&gt;    - &lt;/span&gt;&lt;span class=&quot;l-markup-raw&quot;&gt;`:arg_key`&lt;/span&gt;&lt;span class=&quot;l-comment&quot;&gt; — the action argument holding the local datetime value&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;8&quot;&gt;&lt;span class=&quot;l-comment&quot;&gt;    - &lt;/span&gt;&lt;span class=&quot;l-markup-raw&quot;&gt;`:field`&lt;/span&gt;&lt;span class=&quot;l-comment&quot;&gt; — the resource attribute to assign the UTC value to&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;9&quot;&gt;&lt;span class=&quot;l-comment&quot;&gt;    - &lt;/span&gt;&lt;span class=&quot;l-markup-raw&quot;&gt;`:timezone_key`&lt;/span&gt;&lt;span class=&quot;l-comment&quot;&gt; — the context key for the user&amp;#39;s timezone (default: &lt;/span&gt;&lt;span class=&quot;l-markup-raw&quot;&gt;`:user_timezone`&lt;/span&gt;&lt;span class=&quot;l-comment&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;10&quot;&gt;&lt;span class=&quot;l-comment&quot;&gt;  &quot;&quot;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;11&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;12&quot;&gt;  &lt;span class=&quot;l-keyword&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;l-module&quot;&gt;Ash.Resource.Change&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;13&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;14&quot;&gt;  &lt;span class=&quot;l-keyword-function&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;l-function&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l-variable&quot;&gt;opts&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;l-keyword&quot;&gt;do&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;15&quot;&gt;    &lt;span class=&quot;l-variable&quot;&gt;opts&lt;/span&gt; &lt;span class=&quot;l-operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;l-module&quot;&gt;Keyword&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;l-function-call&quot;&gt;put_new&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l-variable&quot;&gt;opts&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-string-special-symbol&quot;&gt;:timezone_key&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-string-special-symbol&quot;&gt;:user_timezone&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;16&quot;&gt;    &lt;span class=&quot;l-punctuation-bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;l-string-special-symbol&quot;&gt;:ok&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-variable&quot;&gt;opts&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;17&quot;&gt;  &lt;span class=&quot;l-keyword&quot;&gt;end&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;18&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;19&quot;&gt;  &lt;span class=&quot;l-keyword-function&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;l-function&quot;&gt;change&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l-variable&quot;&gt;changeset&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-variable&quot;&gt;opts&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-comment&quot;&gt;_context&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;l-keyword&quot;&gt;do&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;20&quot;&gt;    &lt;span class=&quot;l-variable&quot;&gt;arg_key&lt;/span&gt; &lt;span class=&quot;l-operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;l-variable&quot;&gt;opts&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;l-string-special-symbol&quot;&gt;:arg_key&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;21&quot;&gt;    &lt;span class=&quot;l-variable&quot;&gt;field&lt;/span&gt; &lt;span class=&quot;l-operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;l-variable&quot;&gt;opts&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;l-string-special-symbol&quot;&gt;:field&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;22&quot;&gt;    &lt;span class=&quot;l-variable&quot;&gt;timezone_key&lt;/span&gt; &lt;span class=&quot;l-operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;l-variable&quot;&gt;opts&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;l-string-special-symbol&quot;&gt;:timezone_key&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;23&quot;&gt;    &lt;span class=&quot;l-variable&quot;&gt;timezone&lt;/span&gt; &lt;span class=&quot;l-operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;l-module&quot;&gt;Ash.Changeset&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;l-function-call&quot;&gt;get_context&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l-variable&quot;&gt;changeset&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-variable&quot;&gt;timezone_key&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;l-operator&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;l-string&quot;&gt;&quot;UTC&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;24&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;25&quot;&gt;    &lt;span class=&quot;l-keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;l-module&quot;&gt;Ash.Changeset&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;l-function-call&quot;&gt;fetch_argument&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l-variable&quot;&gt;changeset&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-variable&quot;&gt;arg_key&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;l-keyword&quot;&gt;do&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;26&quot;&gt;      &lt;span class=&quot;l-punctuation-bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;l-string-special-symbol&quot;&gt;:ok&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-constant-builtin&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;l-operator&quot;&gt;-&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;27&quot;&gt;        &lt;span class=&quot;l-variable&quot;&gt;changeset&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;28&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;29&quot;&gt;      &lt;span class=&quot;l-punctuation-bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;l-string-special-symbol&quot;&gt;:ok&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-variable&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;l-operator&quot;&gt;-&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;30&quot;&gt;        &lt;span class=&quot;l-keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;l-function-call&quot;&gt;do_convert_to_utc&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l-variable&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-variable&quot;&gt;timezone&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;l-keyword&quot;&gt;do&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;31&quot;&gt;          &lt;span class=&quot;l-punctuation-bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;l-string-special-symbol&quot;&gt;:ok&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-variable&quot;&gt;utc_dt&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;l-operator&quot;&gt;-&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;32&quot;&gt;            &lt;span class=&quot;l-module&quot;&gt;Ash.Changeset&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;l-function-call&quot;&gt;force_change_attribute&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l-variable&quot;&gt;changeset&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-variable&quot;&gt;field&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-variable&quot;&gt;utc_dt&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;33&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;34&quot;&gt;          &lt;span class=&quot;l-comment&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;l-operator&quot;&gt;-&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;35&quot;&gt;            &lt;span class=&quot;l-module&quot;&gt;Ash.Changeset&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;l-function-call&quot;&gt;add_error&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;36&quot;&gt;              &lt;span class=&quot;l-variable&quot;&gt;changeset&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;37&quot;&gt;              &lt;span class=&quot;l-string&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;l-string-special&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;l-variable&quot;&gt;arg_key&lt;/span&gt;&lt;span class=&quot;l-string-special&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;l-string&quot;&gt; is invalid or could not be converted to UTC&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;38&quot;&gt;            &lt;span class=&quot;l-punctuation-bracket&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;39&quot;&gt;        &lt;span class=&quot;l-keyword&quot;&gt;end&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;40&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;41&quot;&gt;      &lt;span class=&quot;l-string-special-symbol&quot;&gt;:error&lt;/span&gt; &lt;span class=&quot;l-operator&quot;&gt;-&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;42&quot;&gt;        &lt;span class=&quot;l-variable&quot;&gt;changeset&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;43&quot;&gt;    &lt;span class=&quot;l-keyword&quot;&gt;end&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;44&quot;&gt;  &lt;span class=&quot;l-keyword&quot;&gt;end&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;45&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;46&quot;&gt;  &lt;span class=&quot;l-keyword-function&quot;&gt;defp&lt;/span&gt; &lt;span class=&quot;l-function&quot;&gt;do_convert_to_utc&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l-constant-builtin&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-comment&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-string-special-symbol&quot;&gt;do: &lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;l-string-special-symbol&quot;&gt;:ok&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-constant-builtin&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;47&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;48&quot;&gt;  &lt;span class=&quot;l-keyword-function&quot;&gt;defp&lt;/span&gt; &lt;span class=&quot;l-function&quot;&gt;do_convert_to_utc&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l-punctuation-special&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;l-module&quot;&gt;DateTime&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;{}&lt;/span&gt; &lt;span class=&quot;l-operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;l-variable&quot;&gt;dt&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-comment&quot;&gt;_timezone&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;l-keyword&quot;&gt;do&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;49&quot;&gt;    &lt;span class=&quot;l-module&quot;&gt;DateTime&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;l-function-call&quot;&gt;shift_zone&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l-variable&quot;&gt;dt&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-string&quot;&gt;&quot;Etc/UTC&quot;&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;50&quot;&gt;  &lt;span class=&quot;l-keyword&quot;&gt;end&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;51&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;52&quot;&gt;  &lt;span class=&quot;l-keyword-function&quot;&gt;defp&lt;/span&gt; &lt;span class=&quot;l-function&quot;&gt;do_convert_to_utc&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l-punctuation-special&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;l-module&quot;&gt;NaiveDateTime&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;{}&lt;/span&gt; &lt;span class=&quot;l-operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;l-variable&quot;&gt;naive&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-variable&quot;&gt;timezone&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;l-keyword&quot;&gt;do&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;53&quot;&gt;    &lt;span class=&quot;l-keyword&quot;&gt;with&lt;/span&gt; &lt;span class=&quot;l-punctuation-bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;l-string-special-symbol&quot;&gt;:ok&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-variable&quot;&gt;dt_local&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;l-operator&quot;&gt;&lt;-&lt;/span&gt; &lt;span class=&quot;l-module&quot;&gt;DateTime&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;l-function-call&quot;&gt;from_naive&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l-variable&quot;&gt;naive&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-variable&quot;&gt;timezone&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;54&quot;&gt;         &lt;span class=&quot;l-punctuation-bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;l-string-special-symbol&quot;&gt;:ok&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-variable&quot;&gt;dt_utc&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;l-operator&quot;&gt;&lt;-&lt;/span&gt; &lt;span class=&quot;l-module&quot;&gt;DateTime&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;l-function-call&quot;&gt;shift_zone&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l-variable&quot;&gt;dt_local&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-string&quot;&gt;&quot;Etc/UTC&quot;&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;l-keyword&quot;&gt;do&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;55&quot;&gt;      &lt;span class=&quot;l-punctuation-bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;l-string-special-symbol&quot;&gt;:ok&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-variable&quot;&gt;dt_utc&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;56&quot;&gt;    &lt;span class=&quot;l-keyword&quot;&gt;end&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;57&quot;&gt;  &lt;span class=&quot;l-keyword&quot;&gt;end&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;58&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;59&quot;&gt;  &lt;span class=&quot;l-keyword-function&quot;&gt;defp&lt;/span&gt; &lt;span class=&quot;l-function&quot;&gt;do_convert_to_utc&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l-variable&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-variable&quot;&gt;timezone&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;l-keyword&quot;&gt;when&lt;/span&gt; &lt;span class=&quot;l-function-call&quot;&gt;is_binary&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l-variable&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;l-keyword&quot;&gt;do&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;60&quot;&gt;    &lt;span class=&quot;l-keyword&quot;&gt;with&lt;/span&gt; &lt;span class=&quot;l-punctuation-bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;l-string-special-symbol&quot;&gt;:ok&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-variable&quot;&gt;naive&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;l-operator&quot;&gt;&lt;-&lt;/span&gt; &lt;span class=&quot;l-module&quot;&gt;NaiveDateTime&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;l-function-call&quot;&gt;from_iso8601&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l-variable&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;61&quot;&gt;         &lt;span class=&quot;l-punctuation-bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;l-string-special-symbol&quot;&gt;:ok&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-variable&quot;&gt;dt_local&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;l-operator&quot;&gt;&lt;-&lt;/span&gt; &lt;span class=&quot;l-module&quot;&gt;DateTime&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;l-function-call&quot;&gt;from_naive&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l-variable&quot;&gt;naive&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-variable&quot;&gt;timezone&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;62&quot;&gt;         &lt;span class=&quot;l-punctuation-bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;l-string-special-symbol&quot;&gt;:ok&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-variable&quot;&gt;dt_utc&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;l-operator&quot;&gt;&lt;-&lt;/span&gt; &lt;span class=&quot;l-module&quot;&gt;DateTime&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;l-function-call&quot;&gt;shift_zone&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l-variable&quot;&gt;dt_local&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-string&quot;&gt;&quot;Etc/UTC&quot;&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;l-keyword&quot;&gt;do&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;63&quot;&gt;      &lt;span class=&quot;l-punctuation-bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;l-string-special-symbol&quot;&gt;:ok&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-variable&quot;&gt;dt_utc&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;64&quot;&gt;    &lt;span class=&quot;l-keyword&quot;&gt;end&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;65&quot;&gt;  &lt;span class=&quot;l-keyword&quot;&gt;end&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;66&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;67&quot;&gt;  &lt;span class=&quot;l-keyword-function&quot;&gt;defp&lt;/span&gt; &lt;span class=&quot;l-function&quot;&gt;do_convert_to_utc&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l-comment&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-comment&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-string-special-symbol&quot;&gt;do: &lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;l-string-special-symbol&quot;&gt;:error&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-string-special-symbol&quot;&gt;:invalid&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;68&quot;&gt;&lt;span class=&quot;l-keyword&quot;&gt;end&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; &lt;code&gt;DateTime.shift_zone/2&lt;/code&gt; requires a timezone database. Add &lt;code&gt;{:tzdata, &quot;~&gt; 1.1&quot;}&lt;/code&gt; to your &lt;code&gt;mix.exs&lt;/code&gt; dependencies and configure it:&lt;/p&gt;
&lt;pre class=&quot;lumis&quot;&gt;&lt;code class=&quot;language-elixir&quot; translate=&quot;no&quot; tabindex=&quot;0&quot;&gt;&lt;span class=&quot;l-line&quot; data-line=&quot;1&quot;&gt;&lt;span class=&quot;l-function-call&quot;&gt;config&lt;/span&gt; &lt;span class=&quot;l-string-special-symbol&quot;&gt;:elixir&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-string-special-symbol&quot;&gt;:time_zone_database&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-module&quot;&gt;Tzdata.TimeZoneDatabase&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;h1&gt;Setting the timezone in context&lt;/h1&gt;
&lt;p&gt;To apply the conversion correctly, the user&apos;s timezone should be passed in the Ash changeset context.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Ash 3.x (domain-based):&lt;/strong&gt;&lt;/p&gt;
&lt;pre class=&quot;lumis&quot;&gt;&lt;code class=&quot;language-elixir&quot; translate=&quot;no&quot; tabindex=&quot;0&quot;&gt;&lt;span class=&quot;l-line&quot; data-line=&quot;1&quot;&gt;&lt;span class=&quot;l-module&quot;&gt;AshPhoenix.Form&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;l-function-call&quot;&gt;for_action&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l-module&quot;&gt;MyApp.Event&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-string-special-symbol&quot;&gt;:schedule&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;2&quot;&gt;  &lt;span class=&quot;l-string-special-symbol&quot;&gt;domain: &lt;/span&gt;&lt;span class=&quot;l-module&quot;&gt;MyApp.Domain&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;3&quot;&gt;  &lt;span class=&quot;l-string-special-symbol&quot;&gt;as: :event&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;4&quot;&gt;  &lt;span class=&quot;l-string-special-symbol&quot;&gt;context: &lt;/span&gt;&lt;span class=&quot;l-punctuation-special&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;l-string-special-symbol&quot;&gt;user_timezone: &lt;/span&gt;&lt;span class=&quot;l-string&quot;&gt;&quot;Europe/Brussels&quot;&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;5&quot;&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Ash 2.x (api-based):&lt;/strong&gt;&lt;/p&gt;
&lt;pre class=&quot;lumis&quot;&gt;&lt;code class=&quot;language-elixir&quot; translate=&quot;no&quot; tabindex=&quot;0&quot;&gt;&lt;span class=&quot;l-line&quot; data-line=&quot;1&quot;&gt;&lt;span class=&quot;l-module&quot;&gt;AshPhoenix.Form&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;l-function-call&quot;&gt;for_action&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l-module&quot;&gt;MyApp.Event&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-string-special-symbol&quot;&gt;:schedule&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;2&quot;&gt;  &lt;span class=&quot;l-string-special-symbol&quot;&gt;api: &lt;/span&gt;&lt;span class=&quot;l-module&quot;&gt;MyApp.Api&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;3&quot;&gt;  &lt;span class=&quot;l-string-special-symbol&quot;&gt;as: :event&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;4&quot;&gt;  &lt;span class=&quot;l-string-special-symbol&quot;&gt;context: &lt;/span&gt;&lt;span class=&quot;l-punctuation-special&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;l-string-special-symbol&quot;&gt;user_timezone: &lt;/span&gt;&lt;span class=&quot;l-string&quot;&gt;&quot;Europe/Brussels&quot;&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;5&quot;&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;Summary&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;Store all datetime attributes in UTC.&lt;/li&gt;
&lt;li&gt;Accept user-local input via custom &lt;code&gt;argument&lt;/code&gt;s.&lt;/li&gt;
&lt;li&gt;Use a reusable &lt;code&gt;Ash.Resource.Change&lt;/code&gt; module to convert values to UTC.&lt;/li&gt;
&lt;li&gt;Pass the user&apos;s timezone via &lt;code&gt;context&lt;/code&gt; on the changeset.&lt;/li&gt;
&lt;li&gt;Handle strings, &lt;code&gt;NaiveDateTime&lt;/code&gt;, &lt;code&gt;DateTime&lt;/code&gt;, and &lt;code&gt;nil&lt;/code&gt; values gracefully.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This pattern keeps your Ash resources clean and your time handling robust across different timezones.&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://www.yellowduck.be/tags/database&quot;&gt;#database&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/elixir&quot;&gt;#elixir&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/phoenix&quot;&gt;#phoenix&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/postgresql&quot;&gt;#postgresql&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/sql&quot;&gt;#sql&lt;/a&gt;&lt;/p&gt;</content>
    <published>2026-10-14T17:00:00Z</published>
    <id>https://www.yellowduck.be/posts/handling-local-timezones-in-ash-while-storing-utc-datetimes</id>
    <title>🐥 Handling local timezones in Ash while storing UTC datetimes</title>
    <updated>2026-10-14T17:00:00Z</updated>
  </entry>
  <entry>
    <author>
      <name>Pieter Claerhout</name>
      <email>pieter@yellowduck.be</email>
    </author>
    <link href="https://www.yellowduck.be/posts/the-blog-was-the-retrieval-layer-all-along" rel="alternate"/>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;This article by Kin Lane reveals an intriguing perspective on how blogging serves as a retrieval system rather than mere content creation. Through a conversation with Emma Kriskinans of Tyk, Lane uncovers her method of documenting not just what she built but also the thought processes behind it. He reflects on his own experience with API Evangelist, arguing that his extensive archive is not just a memory aid but a meticulously maintained record of reasoning over time. As AI technologies evolve, Lane emphasizes the importance of having structured documentation—something he has maintained for years. He advises writers to document their thought processes in a way that allows for future insights, underscoring that the value lies in how we frame our own reasoning rather than just the conclusions we reach. This habit turns into an asset over time, proving beneficial to current and future readers alike.&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;&lt;a href=&quot;http://apievangelist.com/2026/09/04/the-blog-was-the-retrieval-layer-all-along/&quot;&gt;Continue reading on &lt;strong&gt;apievangelist.com&lt;/strong&gt;&lt;/a&gt;&lt;p&gt;&lt;a href=&quot;https://www.yellowduck.be/tags/reading-list&quot;&gt;#reading-list&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/best-practice&quot;&gt;#best-practice&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/ai&quot;&gt;#ai&lt;/a&gt;&lt;/p&gt;</content>
    <published>2026-10-14T13:00:00Z</published>
    <id>https://www.yellowduck.be/posts/the-blog-was-the-retrieval-layer-all-along</id>
    <title>🔗 The blog was the retrieval layer all along</title>
    <updated>2026-10-14T13:00:00Z</updated>
  </entry>
  <entry>
    <author>
      <name>Pieter Claerhout</name>
      <email>pieter@yellowduck.be</email>
    </author>
    <link href="https://www.yellowduck.be/posts/continuous-development-with-claude-code-on-github" rel="alternate"/>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;In this article, Piotr Minkowski explains how to leverage the Claude GitHub App for continuous development. By using Claude Code, developers can automatically implement features and review pull requests in response to GitHub issues. The setup involves installing the Claude GitHub App and configuring related GitHub workflows that react to comments in issues or pull requests. A sample project utilizing Spring Boot REST API serves as a demonstration, showcasing effective patterns like JPA persistence and CI pipelines. The workflow is entirely automated, emphasizing minimal developer intervention while ensuring smart code management through AI sufficiency. This innovative approach bridges the gap between issue tracking and code quality, making it a noteworthy method for modern development processes.&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;&lt;a href=&quot;https://piotrminkowski.com/2026/07/02/continuous-development-with-claude-code-on-github/&quot;&gt;Continue reading on &lt;strong&gt;piotrminkowski.com&lt;/strong&gt;&lt;/a&gt;&lt;p&gt;&lt;a href=&quot;https://www.yellowduck.be/tags/reading-list&quot;&gt;#reading-list&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/ai&quot;&gt;#ai&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/devops&quot;&gt;#devops&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/github&quot;&gt;#github&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/java&quot;&gt;#java&lt;/a&gt;&lt;/p&gt;</content>
    <published>2026-10-14T08:00:00Z</published>
    <id>https://www.yellowduck.be/posts/continuous-development-with-claude-code-on-github</id>
    <title>🔗 Continuous development with Claude Code on GitHub</title>
    <updated>2026-10-14T08:00:00Z</updated>
  </entry>
  <entry>
    <author>
      <name>Pieter Claerhout</name>
      <email>pieter@yellowduck.be</email>
    </author>
    <link href="https://www.yellowduck.be/posts/homebrew-7-0-0" rel="alternate"/>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;The newly released Homebrew 7.0.0 introduces significant performance enhancements, making installations and upgrades faster than ever. With improved concurrency during downloads and installations, users can expect reduced wait times and better preparation of packages. The update also brings stronger security measures, including a native macOS app, built-in vulnerability checks, and advisory databases to further reinforce application safety. Notably, support for macOS 10.15 has been dropped, with Intel Macs moving to Tier 3 status. For developers relying on Homebrew, the new features streamline the installation and management processes, emphasizing both speed and security. The article outlines these crucial upgrades and what they mean for users across macOS and Linux platforms.&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;&lt;a href=&quot;https://brew.sh/2026/09/13/homebrew-7.0.0/&quot;&gt;Continue reading on &lt;strong&gt;brew.sh&lt;/strong&gt;&lt;/a&gt;&lt;p&gt;&lt;a href=&quot;https://www.yellowduck.be/tags/reading-list&quot;&gt;#reading-list&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/devops&quot;&gt;#devops&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/announcement&quot;&gt;#announcement&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/mac&quot;&gt;#mac&lt;/a&gt;&lt;/p&gt;</content>
    <published>2026-10-13T17:00:00Z</published>
    <id>https://www.yellowduck.be/posts/homebrew-7-0-0</id>
    <title>🔗 Homebrew: 7.0.0</title>
    <updated>2026-10-13T17:00:00Z</updated>
  </entry>
  <entry>
    <author>
      <name>Pieter Claerhout</name>
      <email>pieter@yellowduck.be</email>
    </author>
    <link href="https://www.yellowduck.be/posts/async-programming-in-python-from-generators-to-asyncio" rel="alternate"/>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;Python async programming lets a single-threaded program stay busy while it waits on slow work, such as a network request, a database query, or a file read. Instead of blocking the CPU until the result arrives, your code hands control back to an event loop, which runs other work in the meantime.&lt;/p&gt;
&lt;p&gt;You write asynchronous code with the async and await keywords and run it with asyncio from the standard library. An async function is one you define with async def. Calling it doesn’t run it. It returns a coroutine that runs only once you await it or schedule it on the event loop.&lt;/p&gt;
&lt;p&gt;In this tutorial, you’ll arrive at that model one step at a time, starting from ordinary synchronous code and adding a single idea at each stage. Along the way, you’ll see how far you can get with generators alone and where asyncio takes over.&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;&lt;a href=&quot;https://realpython.com/python-async-features/&quot;&gt;Continue reading on &lt;strong&gt;realpython.com&lt;/strong&gt;&lt;/a&gt;&lt;p&gt;&lt;a href=&quot;https://www.yellowduck.be/tags/reading-list&quot;&gt;#reading-list&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/development&quot;&gt;#development&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/python&quot;&gt;#python&lt;/a&gt;&lt;/p&gt;</content>
    <published>2026-10-13T13:00:00Z</published>
    <id>https://www.yellowduck.be/posts/async-programming-in-python-from-generators-to-asyncio</id>
    <title>🔗 Async programming in Python: From generators to asyncio</title>
    <updated>2026-10-13T13:00:00Z</updated>
  </entry>
  <entry>
    <author>
      <name>Pieter Claerhout</name>
      <email>pieter@yellowduck.be</email>
    </author>
    <link href="https://www.yellowduck.be/posts/how-ai-might-actually-kill-us" rel="alternate"/>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;What if AI isn’t just an assistive tool but a potential threat to humanity? Inti De Ceukelaire raises alarms about AI systems that not only perform tasks but also pose dangers when misused. He reflects on an incident involving OpenAI’s training agents that breached security and misbehaved, revealing vulnerabilities that left him questioning the current trajectory of AI development. With the ever-growing dependency on these systems, he warns of critical risks—including manipulation and potential harm facilitated by AI&apos;s growing capabilities. De Ceukelaire advocates for urgent regulatory measures and broader discussions around AI safety to mitigate these dangers, emphasizing that society must not only prepare for worst-case scenarios but also hold AI companies accountable for their technologies&apos; impacts.&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;&lt;a href=&quot;https://inti.io/p/how-ai-might-actually-kill-us&quot;&gt;Continue reading on &lt;strong&gt;inti.io&lt;/strong&gt;&lt;/a&gt;&lt;p&gt;&lt;a href=&quot;https://www.yellowduck.be/tags/reading-list&quot;&gt;#reading-list&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/ai&quot;&gt;#ai&lt;/a&gt;&lt;/p&gt;</content>
    <published>2026-10-13T08:00:00Z</published>
    <id>https://www.yellowduck.be/posts/how-ai-might-actually-kill-us</id>
    <title>🔗 How AI might actually kill us</title>
    <updated>2026-10-13T08:00:00Z</updated>
  </entry>
  <entry>
    <author>
      <name>Pieter Claerhout</name>
      <email>pieter@yellowduck.be</email>
    </author>
    <link href="https://www.yellowduck.be/posts/5-mac-terminal-apps-that-replaced-my-biggest-memory-hogs" rel="alternate"/>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;The memory hogs in a Mac aren&apos;t necessarily the apps that are frequently used but rather those that remain open. Aditya Darekar shares five terminal apps that can effectively replace resource-intensive GUI counterparts, helping users manage limited memory on their devices. These alternatives not only save memory but often provide better performance. For instance, &lt;code&gt;Mole&lt;/code&gt; replaces &lt;code&gt;CleanMyMac&lt;/code&gt; for system cleaning, while &lt;code&gt;Hazelnut&lt;/code&gt; offers disk organization without the privacy concerns of AI. Additionally, tools like &lt;code&gt;Apple Frames CLI&lt;/code&gt;, &lt;code&gt;Croc&lt;/code&gt;, and &lt;code&gt;Aria2&lt;/code&gt; streamline workflows for developers and casual users alike. Each app is a lightweight solution that enhances productivity without the baggage of traditional GUI applications.&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;&lt;a href=&quot;https://freedium-mirror.cfd/https%3A%2F%2Fmedium.com%2Fmacoclock%2F5-mac-terminal-apps-that-replaced-my-biggest-memory-hogs-0fbdf56aa988&quot;&gt;Continue reading on &lt;strong&gt;freedium-mirror.cfd&lt;/strong&gt;&lt;/a&gt;&lt;p&gt;&lt;a href=&quot;https://www.yellowduck.be/tags/reading-list&quot;&gt;#reading-list&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/tools&quot;&gt;#tools&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/terminal&quot;&gt;#terminal&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/mac&quot;&gt;#mac&lt;/a&gt;&lt;/p&gt;</content>
    <published>2026-10-12T17:00:00Z</published>
    <id>https://www.yellowduck.be/posts/5-mac-terminal-apps-that-replaced-my-biggest-memory-hogs</id>
    <title>🔗 5 Mac terminal apps that replaced my biggest memory hogs</title>
    <updated>2026-10-12T17:00:00Z</updated>
  </entry>
  <entry>
    <author>
      <name>Pieter Claerhout</name>
      <email>pieter@yellowduck.be</email>
    </author>
    <link href="https://www.yellowduck.be/posts/mysqls-on-delete-cascade-has-a-15-level-wall" rel="alternate"/>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;With &lt;code&gt;ON DELETE CASCADE&lt;/code&gt;, deeper hierarchies in MySQL face a daunting challenge: a limit of 15 levels. When attempting to delete a parent node from a self-referencing tree structure, cascading deletions will halt if the descendant chain reaches this depth, resulting in a rollback of the entire operation. The author documented several issues with cascading operations, including the fact that throws of ERROR 1215 occur when adding cascading deletions to &lt;code&gt;STORED&lt;/code&gt; generated columns. To address this, switching to &lt;code&gt;VIRTUAL&lt;/code&gt; columns allows for successful cascade definitions. While the cascade method is simpler, it&apos;s also slower and prone to accidental deletions of entire subtrees. An alternative, which involves manual deletions, allows for more control and avoids the limitations of cascading deletions. The article ends by outlining alternative designs for storing trees that can bypass these complications entirely.&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;&lt;a href=&quot;https://zenn.dev/aecomet/articles/mysql-self-ref-cascade-15-depth-limit?locale=en&quot;&gt;Continue reading on &lt;strong&gt;zenn.dev&lt;/strong&gt;&lt;/a&gt;&lt;p&gt;&lt;a href=&quot;https://www.yellowduck.be/tags/reading-list&quot;&gt;#reading-list&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/database&quot;&gt;#database&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/best-practice&quot;&gt;#best-practice&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/mysql&quot;&gt;#mysql&lt;/a&gt;&lt;/p&gt;</content>
    <published>2026-10-12T13:00:00Z</published>
    <id>https://www.yellowduck.be/posts/mysqls-on-delete-cascade-has-a-15-level-wall</id>
    <title>🔗 MySQL&apos;s ON DELETE CASCADE has a &quot;15-level wall&quot;</title>
    <updated>2026-10-12T13:00:00Z</updated>
  </entry>
  <entry>
    <author>
      <name>Pieter Claerhout</name>
      <email>pieter@yellowduck.be</email>
    </author>
    <link href="https://www.yellowduck.be/posts/introducing-tin-full-text-search-for-postgresql" rel="alternate"/>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;One of the most sought-after features among Postgres users is full-text search, leading to the launch of TIN—&quot;Text INdex.&quot; This new GA release promises a fast and comprehensive full-text search experience for all Postgres and Neki databases. TIN stands out by supporting Boolean expressions, phrase queries, case and accent folding, and BM25-scored queries. Importantly, TIN outperforms existing text-search indexes in terms of speed and reliability, managing to handle a high volume of queries concurrently, even amidst document updates.&lt;/p&gt;
&lt;p&gt;Eric Ridge and Patrick Reynolds detail how rigorous benchmarking was conducted, demonstrating TIN&apos;s remarkable capabilities across various workloads, including a mix of academic papers and user-generated content. Results showcase TIN handling up to 25x more queries per second when compared to alternatives, with substantially reduced latency. This is a promising tool for developers looking to enhance search functionalities in their applications, from e-commerce to legal discovery platforms.&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;&lt;a href=&quot;https://planetscale.com/blog/introducing-tin&quot;&gt;Continue reading on &lt;strong&gt;planetscale.com&lt;/strong&gt;&lt;/a&gt;&lt;p&gt;&lt;a href=&quot;https://www.yellowduck.be/tags/reading-list&quot;&gt;#reading-list&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/database&quot;&gt;#database&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/postgresql&quot;&gt;#postgresql&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/announcement&quot;&gt;#announcement&lt;/a&gt;&lt;/p&gt;</content>
    <published>2026-10-12T08:00:00Z</published>
    <id>https://www.yellowduck.be/posts/introducing-tin-full-text-search-for-postgresql</id>
    <title>🔗 Introducing TIN: Full-text search for PostgreSQL</title>
    <updated>2026-10-12T08:00:00Z</updated>
  </entry>
  <entry>
    <author>
      <name>Pieter Claerhout</name>
      <email>pieter@yellowduck.be</email>
    </author>
    <link href="https://www.yellowduck.be/posts/building-a-lift-scheduling-algorithm-in-python" rel="alternate"/>
    <content type="html">&lt;p&gt;A lift looks simple from the outside: press a button, wait, and eventually it arrives. Internally, however, a lift controller is a scheduling problem.&lt;/p&gt;
&lt;p&gt;With multiple lifts and multiple passengers, the controller needs to decide which lift should handle which request, while trying to minimise waiting time, travel time, and unnecessary stops.&lt;/p&gt;
&lt;p&gt;Let&apos;s build a simplified version in Python.&lt;/p&gt;
&lt;h1&gt;Start with a single lift&lt;/h1&gt;
&lt;p&gt;A lift needs to keep track of its current state:&lt;/p&gt;
&lt;pre class=&quot;lumis&quot;&gt;&lt;code class=&quot;language-python&quot; translate=&quot;no&quot; tabindex=&quot;0&quot;&gt;&lt;span class=&quot;l-line&quot; data-line=&quot;1&quot;&gt;&lt;span class=&quot;l-keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;l-variable&quot;&gt;dataclasses&lt;/span&gt; &lt;span class=&quot;l-keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;l-variable&quot;&gt;dataclass&lt;/span&gt;, &lt;span class=&quot;l-variable&quot;&gt;field&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;2&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;3&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;4&quot;&gt;&lt;span class=&quot;l-function&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;l-function&quot;&gt;dataclass&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;5&quot;&gt;&lt;span class=&quot;l-keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;l-constructor&quot;&gt;Lift&lt;/span&gt;:&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;6&quot;&gt;    &lt;span class=&quot;l-variable&quot;&gt;floor&lt;/span&gt;: &lt;span class=&quot;l-type&quot;&gt;int&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;7&quot;&gt;    &lt;span class=&quot;l-variable&quot;&gt;direction&lt;/span&gt;: &lt;span class=&quot;l-variable&quot;&gt;str&lt;/span&gt; &lt;span class=&quot;l-operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;l-constant-builtin&quot;&gt;None&lt;/span&gt; &lt;span class=&quot;l-operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;l-constant-builtin&quot;&gt;None&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;8&quot;&gt;    &lt;span class=&quot;l-variable&quot;&gt;targets&lt;/span&gt;: &lt;span class=&quot;l-variable&quot;&gt;set&lt;/span&gt;[&lt;span class=&quot;l-type&quot;&gt;int&lt;/span&gt;] &lt;span class=&quot;l-operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;l-function&quot;&gt;field&lt;/span&gt;(&lt;span class=&quot;l-variable&quot;&gt;default_factory&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;l-variable&quot;&gt;set&lt;/span&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A simple controller can use the &lt;strong&gt;LOOK scheduling algorithm&lt;/strong&gt; to select the next floor.&lt;/p&gt;
&lt;p&gt;LOOK works like this:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Continue in the current direction while there are targets in that direction.&lt;/li&gt;
&lt;li&gt;Visit the closest target in that direction.&lt;/li&gt;
&lt;li&gt;When there are no more targets in that direction, reverse.&lt;/li&gt;
&lt;li&gt;If there are no targets at all, become idle.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;lumis&quot;&gt;&lt;code class=&quot;language-python&quot; translate=&quot;no&quot; tabindex=&quot;0&quot;&gt;&lt;span class=&quot;l-line&quot; data-line=&quot;1&quot;&gt;&lt;span class=&quot;l-keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;l-function&quot;&gt;next_target&lt;/span&gt;(&lt;span class=&quot;l-variable&quot;&gt;lift&lt;/span&gt;: &lt;span class=&quot;l-type&quot;&gt;Lift&lt;/span&gt;) &lt;span class=&quot;l-operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;l-variable&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;l-operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;l-constant-builtin&quot;&gt;None&lt;/span&gt;:&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;2&quot;&gt;    &lt;span class=&quot;l-keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;l-operator&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;l-variable&quot;&gt;lift&lt;/span&gt;.&lt;span class=&quot;l-property&quot;&gt;targets&lt;/span&gt;:&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;3&quot;&gt;        &lt;span class=&quot;l-variable&quot;&gt;lift&lt;/span&gt;.&lt;span class=&quot;l-property&quot;&gt;direction&lt;/span&gt; &lt;span class=&quot;l-operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;l-constant-builtin&quot;&gt;None&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;4&quot;&gt;        &lt;span class=&quot;l-keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;l-constant-builtin&quot;&gt;None&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;5&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;6&quot;&gt;    &lt;span class=&quot;l-keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;l-variable&quot;&gt;lift&lt;/span&gt;.&lt;span class=&quot;l-property&quot;&gt;direction&lt;/span&gt; &lt;span class=&quot;l-operator&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;l-constant-builtin&quot;&gt;None&lt;/span&gt;:&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;7&quot;&gt;        &lt;span class=&quot;l-variable&quot;&gt;lift&lt;/span&gt;.&lt;span class=&quot;l-property&quot;&gt;direction&lt;/span&gt; &lt;span class=&quot;l-operator&quot;&gt;=&lt;/span&gt; (&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;8&quot;&gt;            &lt;span class=&quot;l-string&quot;&gt;&quot;up&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;9&quot;&gt;            &lt;span class=&quot;l-keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;l-function-builtin&quot;&gt;min&lt;/span&gt;(&lt;span class=&quot;l-variable&quot;&gt;lift&lt;/span&gt;.&lt;span class=&quot;l-property&quot;&gt;targets&lt;/span&gt;) &lt;span class=&quot;l-operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;l-variable&quot;&gt;lift&lt;/span&gt;.&lt;span class=&quot;l-property&quot;&gt;floor&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;10&quot;&gt;            &lt;span class=&quot;l-keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;l-string&quot;&gt;&quot;down&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;11&quot;&gt;        )&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;12&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;13&quot;&gt;    &lt;span class=&quot;l-keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;l-variable&quot;&gt;lift&lt;/span&gt;.&lt;span class=&quot;l-property&quot;&gt;direction&lt;/span&gt; &lt;span class=&quot;l-operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;l-string&quot;&gt;&quot;up&quot;&lt;/span&gt;:&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;14&quot;&gt;        &lt;span class=&quot;l-variable&quot;&gt;above&lt;/span&gt; &lt;span class=&quot;l-operator&quot;&gt;=&lt;/span&gt; [&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;15&quot;&gt;            &lt;span class=&quot;l-variable&quot;&gt;floor&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;16&quot;&gt;            &lt;span class=&quot;l-keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;l-variable&quot;&gt;floor&lt;/span&gt; &lt;span class=&quot;l-operator&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;l-variable&quot;&gt;lift&lt;/span&gt;.&lt;span class=&quot;l-property&quot;&gt;targets&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;17&quot;&gt;            &lt;span class=&quot;l-keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;l-variable&quot;&gt;floor&lt;/span&gt; &lt;span class=&quot;l-operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;l-variable&quot;&gt;lift&lt;/span&gt;.&lt;span class=&quot;l-property&quot;&gt;floor&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;18&quot;&gt;        ]&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;19&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;20&quot;&gt;        &lt;span class=&quot;l-keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;l-variable&quot;&gt;above&lt;/span&gt;:&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;21&quot;&gt;            &lt;span class=&quot;l-keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;l-function-builtin&quot;&gt;min&lt;/span&gt;(&lt;span class=&quot;l-variable&quot;&gt;above&lt;/span&gt;)&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;22&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;23&quot;&gt;        &lt;span class=&quot;l-variable&quot;&gt;lift&lt;/span&gt;.&lt;span class=&quot;l-property&quot;&gt;direction&lt;/span&gt; &lt;span class=&quot;l-operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;l-string&quot;&gt;&quot;down&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;24&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;25&quot;&gt;    &lt;span class=&quot;l-keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;l-variable&quot;&gt;lift&lt;/span&gt;.&lt;span class=&quot;l-property&quot;&gt;direction&lt;/span&gt; &lt;span class=&quot;l-operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;l-string&quot;&gt;&quot;down&quot;&lt;/span&gt;:&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;26&quot;&gt;        &lt;span class=&quot;l-variable&quot;&gt;below&lt;/span&gt; &lt;span class=&quot;l-operator&quot;&gt;=&lt;/span&gt; [&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;27&quot;&gt;            &lt;span class=&quot;l-variable&quot;&gt;floor&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;28&quot;&gt;            &lt;span class=&quot;l-keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;l-variable&quot;&gt;floor&lt;/span&gt; &lt;span class=&quot;l-operator&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;l-variable&quot;&gt;lift&lt;/span&gt;.&lt;span class=&quot;l-property&quot;&gt;targets&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;29&quot;&gt;            &lt;span class=&quot;l-keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;l-variable&quot;&gt;floor&lt;/span&gt; &lt;span class=&quot;l-operator&quot;&gt;&lt;&lt;/span&gt; &lt;span class=&quot;l-variable&quot;&gt;lift&lt;/span&gt;.&lt;span class=&quot;l-property&quot;&gt;floor&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;30&quot;&gt;        ]&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;31&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;32&quot;&gt;        &lt;span class=&quot;l-keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;l-variable&quot;&gt;below&lt;/span&gt;:&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;33&quot;&gt;            &lt;span class=&quot;l-keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;l-function-builtin&quot;&gt;max&lt;/span&gt;(&lt;span class=&quot;l-variable&quot;&gt;below&lt;/span&gt;)&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;34&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;35&quot;&gt;        &lt;span class=&quot;l-variable&quot;&gt;lift&lt;/span&gt;.&lt;span class=&quot;l-property&quot;&gt;direction&lt;/span&gt; &lt;span class=&quot;l-operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;l-string&quot;&gt;&quot;up&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;36&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;37&quot;&gt;    &lt;span class=&quot;l-comment&quot;&gt;# If we get here, there are targets but none in the&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;38&quot;&gt;    &lt;span class=&quot;l-comment&quot;&gt;# current direction. Pick the closest one and establish&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;39&quot;&gt;    &lt;span class=&quot;l-comment&quot;&gt;# the corresponding direction.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;40&quot;&gt;    &lt;span class=&quot;l-variable&quot;&gt;target&lt;/span&gt; &lt;span class=&quot;l-operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;l-function-builtin&quot;&gt;min&lt;/span&gt;(&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;41&quot;&gt;        &lt;span class=&quot;l-variable&quot;&gt;lift&lt;/span&gt;.&lt;span class=&quot;l-property&quot;&gt;targets&lt;/span&gt;,&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;42&quot;&gt;        &lt;span class=&quot;l-variable&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;l-keyword&quot;&gt;lambda&lt;/span&gt; &lt;span class=&quot;l-variable&quot;&gt;floor&lt;/span&gt;: &lt;span class=&quot;l-function-builtin&quot;&gt;abs&lt;/span&gt;(&lt;span class=&quot;l-variable&quot;&gt;floor&lt;/span&gt; &lt;span class=&quot;l-operator&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;l-variable&quot;&gt;lift&lt;/span&gt;.&lt;span class=&quot;l-property&quot;&gt;floor&lt;/span&gt;),&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;43&quot;&gt;    )&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;44&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;45&quot;&gt;    &lt;span class=&quot;l-variable&quot;&gt;lift&lt;/span&gt;.&lt;span class=&quot;l-property&quot;&gt;direction&lt;/span&gt; &lt;span class=&quot;l-operator&quot;&gt;=&lt;/span&gt; (&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;46&quot;&gt;        &lt;span class=&quot;l-string&quot;&gt;&quot;up&quot;&lt;/span&gt; &lt;span class=&quot;l-keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;l-variable&quot;&gt;target&lt;/span&gt; &lt;span class=&quot;l-operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;l-variable&quot;&gt;lift&lt;/span&gt;.&lt;span class=&quot;l-property&quot;&gt;floor&lt;/span&gt; &lt;span class=&quot;l-keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;l-string&quot;&gt;&quot;down&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;47&quot;&gt;    )&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;48&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;49&quot;&gt;    &lt;span class=&quot;l-keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;l-variable&quot;&gt;target&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For example:&lt;/p&gt;
&lt;pre class=&quot;lumis&quot;&gt;&lt;code class=&quot;language-plaintext&quot; translate=&quot;no&quot; tabindex=&quot;0&quot;&gt;&lt;span class=&quot;l-line&quot; data-line=&quot;1&quot;&gt;Lift:       5&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;2&quot;&gt;Requests:   2, 4, 6, 8, 10&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;3&quot;&gt;Direction:  up&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The lift visits:&lt;/p&gt;
&lt;pre class=&quot;lumis&quot;&gt;&lt;code class=&quot;language-plaintext&quot; translate=&quot;no&quot; tabindex=&quot;0&quot;&gt;&lt;span class=&quot;l-line&quot; data-line=&quot;1&quot;&gt;5 → 6 → 8 → 10 → 8 → 6 → 4 → 2&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;However, that example exposes an important implementation detail: &lt;strong&gt;once a target has been served, it must be removed from the target set&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;A simple simulation might therefore look like this:&lt;/p&gt;
&lt;pre class=&quot;lumis&quot;&gt;&lt;code class=&quot;language-python&quot; translate=&quot;no&quot; tabindex=&quot;0&quot;&gt;&lt;span class=&quot;l-line&quot; data-line=&quot;1&quot;&gt;&lt;span class=&quot;l-keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;l-function&quot;&gt;serve_next&lt;/span&gt;(&lt;span class=&quot;l-variable&quot;&gt;lift&lt;/span&gt;: &lt;span class=&quot;l-type&quot;&gt;Lift&lt;/span&gt;) &lt;span class=&quot;l-operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;l-constant-builtin&quot;&gt;None&lt;/span&gt;:&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;2&quot;&gt;    &lt;span class=&quot;l-variable&quot;&gt;target&lt;/span&gt; &lt;span class=&quot;l-operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;l-function&quot;&gt;next_target&lt;/span&gt;(&lt;span class=&quot;l-variable&quot;&gt;lift&lt;/span&gt;)&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;3&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;4&quot;&gt;    &lt;span class=&quot;l-keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;l-variable&quot;&gt;target&lt;/span&gt; &lt;span class=&quot;l-operator&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;l-constant-builtin&quot;&gt;None&lt;/span&gt;:&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;5&quot;&gt;        &lt;span class=&quot;l-keyword&quot;&gt;return&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;6&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;7&quot;&gt;    &lt;span class=&quot;l-variable&quot;&gt;lift&lt;/span&gt;.&lt;span class=&quot;l-property&quot;&gt;floor&lt;/span&gt; &lt;span class=&quot;l-operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;l-variable&quot;&gt;target&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;8&quot;&gt;    &lt;span class=&quot;l-variable&quot;&gt;lift&lt;/span&gt;.&lt;span class=&quot;l-property&quot;&gt;targets&lt;/span&gt;.&lt;span class=&quot;l-property&quot;&gt;remove&lt;/span&gt;(&lt;span class=&quot;l-variable&quot;&gt;target&lt;/span&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now the route is correctly:&lt;/p&gt;
&lt;pre class=&quot;lumis&quot;&gt;&lt;code class=&quot;language-plaintext&quot; translate=&quot;no&quot; tabindex=&quot;0&quot;&gt;&lt;span class=&quot;l-line&quot; data-line=&quot;1&quot;&gt;5 → 6 → 8 → 10 → 4 → 2&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The lift continues upwards until there are no targets above it, then reverses direction.&lt;/p&gt;
&lt;h2&gt;A simpler implementation&lt;/h2&gt;
&lt;p&gt;We can make the LOOK behaviour more explicit by separating target selection from state changes:&lt;/p&gt;
&lt;pre class=&quot;lumis&quot;&gt;&lt;code class=&quot;language-python&quot; translate=&quot;no&quot; tabindex=&quot;0&quot;&gt;&lt;span class=&quot;l-line&quot; data-line=&quot;1&quot;&gt;&lt;span class=&quot;l-keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;l-function&quot;&gt;next_target&lt;/span&gt;(&lt;span class=&quot;l-variable&quot;&gt;lift&lt;/span&gt;: &lt;span class=&quot;l-type&quot;&gt;Lift&lt;/span&gt;) &lt;span class=&quot;l-operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;l-variable&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;l-operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;l-constant-builtin&quot;&gt;None&lt;/span&gt;:&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;2&quot;&gt;    &lt;span class=&quot;l-keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;l-operator&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;l-variable&quot;&gt;lift&lt;/span&gt;.&lt;span class=&quot;l-property&quot;&gt;targets&lt;/span&gt;:&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;3&quot;&gt;        &lt;span class=&quot;l-keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;l-constant-builtin&quot;&gt;None&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;4&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;5&quot;&gt;    &lt;span class=&quot;l-keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;l-variable&quot;&gt;lift&lt;/span&gt;.&lt;span class=&quot;l-property&quot;&gt;direction&lt;/span&gt; &lt;span class=&quot;l-operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;l-string&quot;&gt;&quot;up&quot;&lt;/span&gt;:&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;6&quot;&gt;        &lt;span class=&quot;l-variable&quot;&gt;above&lt;/span&gt; &lt;span class=&quot;l-operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;l-function-builtin&quot;&gt;sorted&lt;/span&gt;(&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;7&quot;&gt;            &lt;span class=&quot;l-variable&quot;&gt;floor&lt;/span&gt; &lt;span class=&quot;l-keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;l-variable&quot;&gt;floor&lt;/span&gt; &lt;span class=&quot;l-operator&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;l-variable&quot;&gt;lift&lt;/span&gt;.&lt;span class=&quot;l-property&quot;&gt;targets&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;8&quot;&gt;            &lt;span class=&quot;l-keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;l-variable&quot;&gt;floor&lt;/span&gt; &lt;span class=&quot;l-operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;l-variable&quot;&gt;lift&lt;/span&gt;.&lt;span class=&quot;l-property&quot;&gt;floor&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;9&quot;&gt;        )&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;10&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;11&quot;&gt;        &lt;span class=&quot;l-keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;l-variable&quot;&gt;above&lt;/span&gt;:&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;12&quot;&gt;            &lt;span class=&quot;l-keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;l-variable&quot;&gt;above&lt;/span&gt;[&lt;span class=&quot;l-number&quot;&gt;0&lt;/span&gt;]&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;13&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;14&quot;&gt;        &lt;span class=&quot;l-variable&quot;&gt;lift&lt;/span&gt;.&lt;span class=&quot;l-property&quot;&gt;direction&lt;/span&gt; &lt;span class=&quot;l-operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;l-string&quot;&gt;&quot;down&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;15&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;16&quot;&gt;    &lt;span class=&quot;l-keyword&quot;&gt;elif&lt;/span&gt; &lt;span class=&quot;l-variable&quot;&gt;lift&lt;/span&gt;.&lt;span class=&quot;l-property&quot;&gt;direction&lt;/span&gt; &lt;span class=&quot;l-operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;l-string&quot;&gt;&quot;down&quot;&lt;/span&gt;:&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;17&quot;&gt;        &lt;span class=&quot;l-variable&quot;&gt;below&lt;/span&gt; &lt;span class=&quot;l-operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;l-function-builtin&quot;&gt;sorted&lt;/span&gt;(&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;18&quot;&gt;            &lt;span class=&quot;l-variable&quot;&gt;floor&lt;/span&gt; &lt;span class=&quot;l-keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;l-variable&quot;&gt;floor&lt;/span&gt; &lt;span class=&quot;l-operator&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;l-variable&quot;&gt;lift&lt;/span&gt;.&lt;span class=&quot;l-property&quot;&gt;targets&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;19&quot;&gt;            &lt;span class=&quot;l-keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;l-variable&quot;&gt;floor&lt;/span&gt; &lt;span class=&quot;l-operator&quot;&gt;&lt;&lt;/span&gt; &lt;span class=&quot;l-variable&quot;&gt;lift&lt;/span&gt;.&lt;span class=&quot;l-property&quot;&gt;floor&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;20&quot;&gt;        )&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;21&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;22&quot;&gt;        &lt;span class=&quot;l-keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;l-variable&quot;&gt;below&lt;/span&gt;:&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;23&quot;&gt;            &lt;span class=&quot;l-keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;l-variable&quot;&gt;below&lt;/span&gt;[&lt;span class=&quot;l-operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;l-number&quot;&gt;1&lt;/span&gt;]&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;24&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;25&quot;&gt;        &lt;span class=&quot;l-variable&quot;&gt;lift&lt;/span&gt;.&lt;span class=&quot;l-property&quot;&gt;direction&lt;/span&gt; &lt;span class=&quot;l-operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;l-string&quot;&gt;&quot;up&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;26&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;27&quot;&gt;    &lt;span class=&quot;l-comment&quot;&gt;# The lift was idle, or has just changed direction.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;28&quot;&gt;    &lt;span class=&quot;l-keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;l-variable&quot;&gt;lift&lt;/span&gt;.&lt;span class=&quot;l-property&quot;&gt;direction&lt;/span&gt; &lt;span class=&quot;l-operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;l-string&quot;&gt;&quot;up&quot;&lt;/span&gt;:&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;29&quot;&gt;        &lt;span class=&quot;l-variable&quot;&gt;above&lt;/span&gt; &lt;span class=&quot;l-operator&quot;&gt;=&lt;/span&gt; [&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;30&quot;&gt;            &lt;span class=&quot;l-variable&quot;&gt;floor&lt;/span&gt; &lt;span class=&quot;l-keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;l-variable&quot;&gt;floor&lt;/span&gt; &lt;span class=&quot;l-operator&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;l-variable&quot;&gt;lift&lt;/span&gt;.&lt;span class=&quot;l-property&quot;&gt;targets&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;31&quot;&gt;            &lt;span class=&quot;l-keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;l-variable&quot;&gt;floor&lt;/span&gt; &lt;span class=&quot;l-operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;l-variable&quot;&gt;lift&lt;/span&gt;.&lt;span class=&quot;l-property&quot;&gt;floor&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;32&quot;&gt;        ]&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;33&quot;&gt;        &lt;span class=&quot;l-keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;l-variable&quot;&gt;above&lt;/span&gt;:&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;34&quot;&gt;            &lt;span class=&quot;l-keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;l-function-builtin&quot;&gt;min&lt;/span&gt;(&lt;span class=&quot;l-variable&quot;&gt;above&lt;/span&gt;)&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;35&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;36&quot;&gt;    &lt;span class=&quot;l-keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;l-variable&quot;&gt;lift&lt;/span&gt;.&lt;span class=&quot;l-property&quot;&gt;direction&lt;/span&gt; &lt;span class=&quot;l-operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;l-string&quot;&gt;&quot;down&quot;&lt;/span&gt;:&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;37&quot;&gt;        &lt;span class=&quot;l-variable&quot;&gt;below&lt;/span&gt; &lt;span class=&quot;l-operator&quot;&gt;=&lt;/span&gt; [&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;38&quot;&gt;            &lt;span class=&quot;l-variable&quot;&gt;floor&lt;/span&gt; &lt;span class=&quot;l-keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;l-variable&quot;&gt;floor&lt;/span&gt; &lt;span class=&quot;l-operator&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;l-variable&quot;&gt;lift&lt;/span&gt;.&lt;span class=&quot;l-property&quot;&gt;targets&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;39&quot;&gt;            &lt;span class=&quot;l-keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;l-variable&quot;&gt;floor&lt;/span&gt; &lt;span class=&quot;l-operator&quot;&gt;&lt;&lt;/span&gt; &lt;span class=&quot;l-variable&quot;&gt;lift&lt;/span&gt;.&lt;span class=&quot;l-property&quot;&gt;floor&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;40&quot;&gt;        ]&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;41&quot;&gt;        &lt;span class=&quot;l-keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;l-variable&quot;&gt;below&lt;/span&gt;:&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;42&quot;&gt;            &lt;span class=&quot;l-keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;l-function-builtin&quot;&gt;max&lt;/span&gt;(&lt;span class=&quot;l-variable&quot;&gt;below&lt;/span&gt;)&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;43&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;44&quot;&gt;    &lt;span class=&quot;l-keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;l-constant-builtin&quot;&gt;None&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The important distinction from a naive implementation is that LOOK &lt;strong&gt;does not choose the globally closest floor&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Given:&lt;/p&gt;
&lt;pre class=&quot;lumis&quot;&gt;&lt;code class=&quot;language-plaintext&quot; translate=&quot;no&quot; tabindex=&quot;0&quot;&gt;&lt;span class=&quot;l-line&quot; data-line=&quot;1&quot;&gt;Current floor: 5&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;2&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;3&quot;&gt;Targets:&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;4&quot;&gt;4&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;5&quot;&gt;6&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;6&quot;&gt;10&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;and direction &lt;code&gt;up&lt;/code&gt;, the next target is:&lt;/p&gt;
&lt;pre class=&quot;lumis&quot;&gt;&lt;code class=&quot;language-plaintext&quot; translate=&quot;no&quot; tabindex=&quot;0&quot;&gt;&lt;span class=&quot;l-line&quot; data-line=&quot;1&quot;&gt;6&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;not:&lt;/p&gt;
&lt;pre class=&quot;lumis&quot;&gt;&lt;code class=&quot;language-plaintext&quot; translate=&quot;no&quot; tabindex=&quot;0&quot;&gt;&lt;span class=&quot;l-line&quot; data-line=&quot;1&quot;&gt;4&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Even though both are equally close, the lift is already travelling upwards.&lt;/p&gt;
&lt;p&gt;After serving 6 and 10, it reverses:&lt;/p&gt;
&lt;pre class=&quot;lumis&quot;&gt;&lt;code class=&quot;language-plaintext&quot; translate=&quot;no&quot; tabindex=&quot;0&quot;&gt;&lt;span class=&quot;l-line&quot; data-line=&quot;1&quot;&gt;5 → 6 → 10 → 4&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That&apos;s the defining behaviour of LOOK.&lt;/p&gt;
&lt;h2&gt;Hall requests are different&lt;/h2&gt;
&lt;p&gt;There are actually two kinds of requests.&lt;/p&gt;
&lt;p&gt;A passenger inside the lift says:&lt;/p&gt;
&lt;pre class=&quot;lumis&quot;&gt;&lt;code class=&quot;language-plaintext&quot; translate=&quot;no&quot; tabindex=&quot;0&quot;&gt;&lt;span class=&quot;l-line&quot; data-line=&quot;1&quot;&gt;I want to go to floor 10.&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A passenger outside says:&lt;/p&gt;
&lt;pre class=&quot;lumis&quot;&gt;&lt;code class=&quot;language-plaintext&quot; translate=&quot;no&quot; tabindex=&quot;0&quot;&gt;&lt;span class=&quot;l-line&quot; data-line=&quot;1&quot;&gt;I&amp;#39;m on floor 5 and want to go up.&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The second request also has a direction.&lt;/p&gt;
&lt;pre class=&quot;lumis&quot;&gt;&lt;code class=&quot;language-python&quot; translate=&quot;no&quot; tabindex=&quot;0&quot;&gt;&lt;span class=&quot;l-line&quot; data-line=&quot;1&quot;&gt;&lt;span class=&quot;l-function&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;l-function&quot;&gt;dataclass&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;2&quot;&gt;&lt;span class=&quot;l-keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;l-constructor&quot;&gt;HallRequest&lt;/span&gt;:&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;3&quot;&gt;    &lt;span class=&quot;l-variable&quot;&gt;floor&lt;/span&gt;: &lt;span class=&quot;l-type&quot;&gt;int&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;4&quot;&gt;    &lt;span class=&quot;l-variable&quot;&gt;direction&lt;/span&gt;: &lt;span class=&quot;l-type&quot;&gt;str&lt;/span&gt;  &lt;span class=&quot;l-comment&quot;&gt;# &quot;up&quot; or &quot;down&quot;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This matters because a lift travelling upwards shouldn&apos;t necessarily stop for someone waiting to travel downwards.&lt;/p&gt;
&lt;p&gt;For example, if the lift is travelling:&lt;/p&gt;
&lt;pre class=&quot;lumis&quot;&gt;&lt;code class=&quot;language-plaintext&quot; translate=&quot;no&quot; tabindex=&quot;0&quot;&gt;&lt;span class=&quot;l-line&quot; data-line=&quot;1&quot;&gt;2 → 5 → 8 → 10&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;a request on floor 4 for a downward journey doesn&apos;t necessarily belong on that route.&lt;/p&gt;
&lt;p&gt;The controller should generally prioritise requests matching its current direction.&lt;/p&gt;
&lt;h1&gt;Multiple lifts&lt;/h1&gt;
&lt;p&gt;Now the problem gets more interesting.&lt;/p&gt;
&lt;p&gt;Imagine three lifts:&lt;/p&gt;
&lt;pre class=&quot;lumis&quot;&gt;&lt;code class=&quot;language-plaintext&quot; translate=&quot;no&quot; tabindex=&quot;0&quot;&gt;&lt;span class=&quot;l-line&quot; data-line=&quot;1&quot;&gt;Lift A: floor 2, going up&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;2&quot;&gt;Lift B: floor 8, idle&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;3&quot;&gt;Lift C: floor 15, going down&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Someone on floor 6 presses the up button.&lt;/p&gt;
&lt;p&gt;The obvious solution is to choose the closest lift. But that&apos;s not necessarily optimal.&lt;/p&gt;
&lt;p&gt;Instead, we can give every lift a score.&lt;/p&gt;
&lt;pre class=&quot;lumis&quot;&gt;&lt;code class=&quot;language-python&quot; translate=&quot;no&quot; tabindex=&quot;0&quot;&gt;&lt;span class=&quot;l-line&quot; data-line=&quot;1&quot;&gt;&lt;span class=&quot;l-keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;l-function&quot;&gt;score&lt;/span&gt;(&lt;span class=&quot;l-variable&quot;&gt;lift&lt;/span&gt;: &lt;span class=&quot;l-type&quot;&gt;Lift&lt;/span&gt;, &lt;span class=&quot;l-variable&quot;&gt;request&lt;/span&gt;: &lt;span class=&quot;l-type&quot;&gt;HallRequest&lt;/span&gt;) &lt;span class=&quot;l-operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;l-type&quot;&gt;float&lt;/span&gt;:&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;2&quot;&gt;    &lt;span class=&quot;l-variable&quot;&gt;distance&lt;/span&gt; &lt;span class=&quot;l-operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;l-function-builtin&quot;&gt;abs&lt;/span&gt;(&lt;span class=&quot;l-variable&quot;&gt;lift&lt;/span&gt;.&lt;span class=&quot;l-property&quot;&gt;floor&lt;/span&gt; &lt;span class=&quot;l-operator&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;l-variable&quot;&gt;request&lt;/span&gt;.&lt;span class=&quot;l-property&quot;&gt;floor&lt;/span&gt;)&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;3&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;4&quot;&gt;    &lt;span class=&quot;l-keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;l-variable&quot;&gt;lift&lt;/span&gt;.&lt;span class=&quot;l-property&quot;&gt;direction&lt;/span&gt; &lt;span class=&quot;l-operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;l-variable&quot;&gt;request&lt;/span&gt;.&lt;span class=&quot;l-property&quot;&gt;direction&lt;/span&gt;:&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;5&quot;&gt;        &lt;span class=&quot;l-variable&quot;&gt;direction_penalty&lt;/span&gt; &lt;span class=&quot;l-operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;l-number&quot;&gt;0&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;6&quot;&gt;    &lt;span class=&quot;l-keyword&quot;&gt;elif&lt;/span&gt; &lt;span class=&quot;l-variable&quot;&gt;lift&lt;/span&gt;.&lt;span class=&quot;l-property&quot;&gt;direction&lt;/span&gt; &lt;span class=&quot;l-operator&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;l-constant-builtin&quot;&gt;None&lt;/span&gt;:&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;7&quot;&gt;        &lt;span class=&quot;l-variable&quot;&gt;direction_penalty&lt;/span&gt; &lt;span class=&quot;l-operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;l-number&quot;&gt;2&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;8&quot;&gt;    &lt;span class=&quot;l-keyword&quot;&gt;else&lt;/span&gt;:&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;9&quot;&gt;        &lt;span class=&quot;l-variable&quot;&gt;direction_penalty&lt;/span&gt; &lt;span class=&quot;l-operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;l-number&quot;&gt;10&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;10&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;11&quot;&gt;    &lt;span class=&quot;l-keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;l-variable&quot;&gt;distance&lt;/span&gt; &lt;span class=&quot;l-operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;l-variable&quot;&gt;direction_penalty&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can then select the lowest-scoring lift:&lt;/p&gt;
&lt;pre class=&quot;lumis&quot;&gt;&lt;code class=&quot;language-python&quot; translate=&quot;no&quot; tabindex=&quot;0&quot;&gt;&lt;span class=&quot;l-line&quot; data-line=&quot;1&quot;&gt;&lt;span class=&quot;l-keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;l-function&quot;&gt;assign_lift&lt;/span&gt;(&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;2&quot;&gt;    &lt;span class=&quot;l-variable&quot;&gt;lifts&lt;/span&gt;: &lt;span class=&quot;l-variable&quot;&gt;list&lt;/span&gt;[&lt;span class=&quot;l-type&quot;&gt;Lift&lt;/span&gt;],&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;3&quot;&gt;    &lt;span class=&quot;l-variable&quot;&gt;request&lt;/span&gt;: &lt;span class=&quot;l-type&quot;&gt;HallRequest&lt;/span&gt;,&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;4&quot;&gt;) &lt;span class=&quot;l-operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;l-type&quot;&gt;Lift&lt;/span&gt;:&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;5&quot;&gt;    &lt;span class=&quot;l-keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;l-function-builtin&quot;&gt;min&lt;/span&gt;(&lt;span class=&quot;l-variable&quot;&gt;lifts&lt;/span&gt;, &lt;span class=&quot;l-variable&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;l-keyword&quot;&gt;lambda&lt;/span&gt; &lt;span class=&quot;l-variable&quot;&gt;lift&lt;/span&gt;: &lt;span class=&quot;l-function&quot;&gt;score&lt;/span&gt;(&lt;span class=&quot;l-variable&quot;&gt;lift&lt;/span&gt;, &lt;span class=&quot;l-variable&quot;&gt;request&lt;/span&gt;))&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is already considerably better than simply selecting the nearest lift.&lt;/p&gt;
&lt;h1&gt;Simulating the cost&lt;/h1&gt;
&lt;p&gt;A more sophisticated controller doesn&apos;t just ask:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;How far away is this lift?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;It asks:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;How much additional work will this request cause?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Consider a lift currently at floor 3 with these destinations:&lt;/p&gt;
&lt;pre class=&quot;lumis&quot;&gt;&lt;code class=&quot;language-plaintext&quot; translate=&quot;no&quot; tabindex=&quot;0&quot;&gt;&lt;span class=&quot;l-line&quot; data-line=&quot;1&quot;&gt;7, 10, 15&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A new passenger wants to go from floor 5 to floor 12.&lt;/p&gt;
&lt;p&gt;The existing route might be:&lt;/p&gt;
&lt;pre class=&quot;lumis&quot;&gt;&lt;code class=&quot;language-plaintext&quot; translate=&quot;no&quot; tabindex=&quot;0&quot;&gt;&lt;span class=&quot;l-line&quot; data-line=&quot;1&quot;&gt;3 → 7 → 10 → 15&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Adding the passenger changes it to:&lt;/p&gt;
&lt;pre class=&quot;lumis&quot;&gt;&lt;code class=&quot;language-plaintext&quot; translate=&quot;no&quot; tabindex=&quot;0&quot;&gt;&lt;span class=&quot;l-line&quot; data-line=&quot;1&quot;&gt;3 → 5 → 7 → 10 → 12 → 15&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The difference between those routes is the &lt;strong&gt;cost of accepting the request&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;We can model this with a cost function:&lt;/p&gt;
&lt;pre class=&quot;lumis&quot;&gt;&lt;code class=&quot;language-python&quot; translate=&quot;no&quot; tabindex=&quot;0&quot;&gt;&lt;span class=&quot;l-line&quot; data-line=&quot;1&quot;&gt;&lt;span class=&quot;l-keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;l-function&quot;&gt;request_cost&lt;/span&gt;(&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;2&quot;&gt;    &lt;span class=&quot;l-variable&quot;&gt;waiting_time&lt;/span&gt;: &lt;span class=&quot;l-type&quot;&gt;float&lt;/span&gt;,&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;3&quot;&gt;    &lt;span class=&quot;l-variable&quot;&gt;travel_time&lt;/span&gt;: &lt;span class=&quot;l-type&quot;&gt;float&lt;/span&gt;,&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;4&quot;&gt;    &lt;span class=&quot;l-variable&quot;&gt;additional_stops&lt;/span&gt;: &lt;span class=&quot;l-type&quot;&gt;int&lt;/span&gt;,&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;5&quot;&gt;    &lt;span class=&quot;l-variable&quot;&gt;direction_changes&lt;/span&gt;: &lt;span class=&quot;l-type&quot;&gt;int&lt;/span&gt;,&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;6&quot;&gt;) &lt;span class=&quot;l-operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;l-type&quot;&gt;float&lt;/span&gt;:&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;7&quot;&gt;    &lt;span class=&quot;l-keyword&quot;&gt;return&lt;/span&gt; (&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;8&quot;&gt;        &lt;span class=&quot;l-variable&quot;&gt;waiting_time&lt;/span&gt; &lt;span class=&quot;l-operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;l-number&quot;&gt;5&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;9&quot;&gt;        &lt;span class=&quot;l-operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;l-variable&quot;&gt;travel_time&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;10&quot;&gt;        &lt;span class=&quot;l-operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;l-variable&quot;&gt;additional_stops&lt;/span&gt; &lt;span class=&quot;l-operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;l-number&quot;&gt;2&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;11&quot;&gt;        &lt;span class=&quot;l-operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;l-variable&quot;&gt;direction_changes&lt;/span&gt; &lt;span class=&quot;l-operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;l-number&quot;&gt;10&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;12&quot;&gt;    )&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The weights are deliberately configurable.&lt;/p&gt;
&lt;p&gt;For example, we might consider waiting five times more important than travel time because making somebody wait for a lift is generally worse than adding a few seconds to the journey of someone already inside.&lt;/p&gt;
&lt;h1&gt;Destination dispatch&lt;/h1&gt;
&lt;p&gt;Modern lift systems can go one step further.&lt;/p&gt;
&lt;p&gt;Instead of pressing:&lt;/p&gt;
&lt;pre class=&quot;lumis&quot;&gt;&lt;code class=&quot;language-plaintext&quot; translate=&quot;no&quot; tabindex=&quot;0&quot;&gt;&lt;span class=&quot;l-line&quot; data-line=&quot;1&quot;&gt;↑&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;and later selecting a destination inside the lift, the passenger enters the destination immediately:&lt;/p&gt;
&lt;pre class=&quot;lumis&quot;&gt;&lt;code class=&quot;language-plaintext&quot; translate=&quot;no&quot; tabindex=&quot;0&quot;&gt;&lt;span class=&quot;l-line&quot; data-line=&quot;1&quot;&gt;Floor 5 → Floor 17&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The controller therefore knows the complete journey before assigning a lift.&lt;/p&gt;
&lt;p&gt;That allows it to group passengers with similar destinations.&lt;/p&gt;
&lt;p&gt;For example:&lt;/p&gt;
&lt;pre class=&quot;lumis&quot;&gt;&lt;code class=&quot;language-plaintext&quot; translate=&quot;no&quot; tabindex=&quot;0&quot;&gt;&lt;span class=&quot;l-line&quot; data-line=&quot;1&quot;&gt;3 → 17&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;2&quot;&gt;4 → 18&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;3&quot;&gt;5 → 16&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;4&quot;&gt;6 → 17&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;could potentially be handled by the same lift.&lt;/p&gt;
&lt;p&gt;The dispatcher can therefore optimise the entire route instead of treating every passenger as an independent request.&lt;/p&gt;
&lt;h2&gt;Separating the dispatcher from the lift&lt;/h2&gt;
&lt;p&gt;A useful architecture is to separate two responsibilities.&lt;/p&gt;
&lt;p&gt;The &lt;strong&gt;dispatcher&lt;/strong&gt; decides:&lt;/p&gt;
&lt;pre class=&quot;lumis&quot;&gt;&lt;code class=&quot;language-plaintext&quot; translate=&quot;no&quot; tabindex=&quot;0&quot;&gt;&lt;span class=&quot;l-line&quot; data-line=&quot;1&quot;&gt;Which lift should handle this request?&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;strong&gt;lift controller&lt;/strong&gt; decides:&lt;/p&gt;
&lt;pre class=&quot;lumis&quot;&gt;&lt;code class=&quot;language-plaintext&quot; translate=&quot;no&quot; tabindex=&quot;0&quot;&gt;&lt;span class=&quot;l-line&quot; data-line=&quot;1&quot;&gt;Which floor should I visit next?&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In Python, that could look like:&lt;/p&gt;
&lt;pre class=&quot;lumis&quot;&gt;&lt;code class=&quot;language-python&quot; translate=&quot;no&quot; tabindex=&quot;0&quot;&gt;&lt;span class=&quot;l-line&quot; data-line=&quot;1&quot;&gt;&lt;span class=&quot;l-keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;l-constructor&quot;&gt;Dispatcher&lt;/span&gt;:&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;2&quot;&gt;    &lt;span class=&quot;l-keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;l-function&quot;&gt;__init__&lt;/span&gt;(&lt;span class=&quot;l-variable&quot;&gt;self&lt;/span&gt;, &lt;span class=&quot;l-variable&quot;&gt;lifts&lt;/span&gt;: &lt;span class=&quot;l-variable&quot;&gt;list&lt;/span&gt;[&lt;span class=&quot;l-type&quot;&gt;Lift&lt;/span&gt;]):&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;3&quot;&gt;        &lt;span class=&quot;l-variable&quot;&gt;self&lt;/span&gt;.&lt;span class=&quot;l-property&quot;&gt;lifts&lt;/span&gt; &lt;span class=&quot;l-operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;l-variable&quot;&gt;lifts&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;4&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;5&quot;&gt;    &lt;span class=&quot;l-keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;l-function&quot;&gt;assign&lt;/span&gt;(&lt;span class=&quot;l-variable&quot;&gt;self&lt;/span&gt;, &lt;span class=&quot;l-variable&quot;&gt;request&lt;/span&gt;: &lt;span class=&quot;l-type&quot;&gt;HallRequest&lt;/span&gt;) &lt;span class=&quot;l-operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;l-type&quot;&gt;Lift&lt;/span&gt;:&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;6&quot;&gt;        &lt;span class=&quot;l-keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;l-function-builtin&quot;&gt;min&lt;/span&gt;(&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;7&quot;&gt;            &lt;span class=&quot;l-variable&quot;&gt;self&lt;/span&gt;.&lt;span class=&quot;l-property&quot;&gt;lifts&lt;/span&gt;,&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;8&quot;&gt;            &lt;span class=&quot;l-variable&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;l-keyword&quot;&gt;lambda&lt;/span&gt; &lt;span class=&quot;l-variable&quot;&gt;lift&lt;/span&gt;: &lt;span class=&quot;l-function&quot;&gt;score&lt;/span&gt;(&lt;span class=&quot;l-variable&quot;&gt;lift&lt;/span&gt;, &lt;span class=&quot;l-variable&quot;&gt;request&lt;/span&gt;),&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;9&quot;&gt;        )&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The lift itself can remain responsible for its own state:&lt;/p&gt;
&lt;pre class=&quot;lumis&quot;&gt;&lt;code class=&quot;language-python&quot; translate=&quot;no&quot; tabindex=&quot;0&quot;&gt;&lt;span class=&quot;l-line&quot; data-line=&quot;1&quot;&gt;&lt;span class=&quot;l-keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;l-constructor&quot;&gt;LiftController&lt;/span&gt;:&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;2&quot;&gt;    &lt;span class=&quot;l-keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;l-function&quot;&gt;__init__&lt;/span&gt;(&lt;span class=&quot;l-variable&quot;&gt;self&lt;/span&gt;, &lt;span class=&quot;l-variable&quot;&gt;lift&lt;/span&gt;: &lt;span class=&quot;l-type&quot;&gt;Lift&lt;/span&gt;):&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;3&quot;&gt;        &lt;span class=&quot;l-variable&quot;&gt;self&lt;/span&gt;.&lt;span class=&quot;l-property&quot;&gt;lift&lt;/span&gt; &lt;span class=&quot;l-operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;l-variable&quot;&gt;lift&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;4&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;5&quot;&gt;    &lt;span class=&quot;l-keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;l-function&quot;&gt;add_target&lt;/span&gt;(&lt;span class=&quot;l-variable&quot;&gt;self&lt;/span&gt;, &lt;span class=&quot;l-variable&quot;&gt;floor&lt;/span&gt;: &lt;span class=&quot;l-type&quot;&gt;int&lt;/span&gt;) &lt;span class=&quot;l-operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;l-constant-builtin&quot;&gt;None&lt;/span&gt;:&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;6&quot;&gt;        &lt;span class=&quot;l-variable&quot;&gt;self&lt;/span&gt;.&lt;span class=&quot;l-property&quot;&gt;lift&lt;/span&gt;.&lt;span class=&quot;l-property&quot;&gt;targets&lt;/span&gt;.&lt;span class=&quot;l-property&quot;&gt;add&lt;/span&gt;(&lt;span class=&quot;l-variable&quot;&gt;floor&lt;/span&gt;)&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;7&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;8&quot;&gt;    &lt;span class=&quot;l-keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;l-function&quot;&gt;next_floor&lt;/span&gt;(&lt;span class=&quot;l-variable&quot;&gt;self&lt;/span&gt;) &lt;span class=&quot;l-operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;l-variable&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;l-operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;l-constant-builtin&quot;&gt;None&lt;/span&gt;:&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;9&quot;&gt;        &lt;span class=&quot;l-keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;l-function&quot;&gt;next_target&lt;/span&gt;(&lt;span class=&quot;l-variable&quot;&gt;self&lt;/span&gt;.&lt;span class=&quot;l-property&quot;&gt;lift&lt;/span&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This separation makes the system much easier to reason about and test.&lt;/p&gt;
&lt;h1&gt;Model it as a state machine&lt;/h1&gt;
&lt;p&gt;A real controller would also have explicit states:&lt;/p&gt;
&lt;pre class=&quot;lumis&quot;&gt;&lt;code class=&quot;language-python&quot; translate=&quot;no&quot; tabindex=&quot;0&quot;&gt;&lt;span class=&quot;l-line&quot; data-line=&quot;1&quot;&gt;&lt;span class=&quot;l-keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;l-variable&quot;&gt;enum&lt;/span&gt; &lt;span class=&quot;l-keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;l-constructor&quot;&gt;Enum&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;2&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;3&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;4&quot;&gt;&lt;span class=&quot;l-keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;l-constructor&quot;&gt;LiftState&lt;/span&gt;(&lt;span class=&quot;l-constructor&quot;&gt;Enum&lt;/span&gt;):&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;5&quot;&gt;    &lt;span class=&quot;l-constant&quot;&gt;IDLE&lt;/span&gt; &lt;span class=&quot;l-operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;l-string&quot;&gt;&quot;idle&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;6&quot;&gt;    &lt;span class=&quot;l-constant&quot;&gt;MOVING_UP&lt;/span&gt; &lt;span class=&quot;l-operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;l-string&quot;&gt;&quot;moving_up&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;7&quot;&gt;    &lt;span class=&quot;l-constant&quot;&gt;MOVING_DOWN&lt;/span&gt; &lt;span class=&quot;l-operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;l-string&quot;&gt;&quot;moving_down&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;8&quot;&gt;    &lt;span class=&quot;l-constant&quot;&gt;DOOR_OPENING&lt;/span&gt; &lt;span class=&quot;l-operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;l-string&quot;&gt;&quot;door_opening&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;9&quot;&gt;    &lt;span class=&quot;l-constant&quot;&gt;DOOR_OPEN&lt;/span&gt; &lt;span class=&quot;l-operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;l-string&quot;&gt;&quot;door_open&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;10&quot;&gt;    &lt;span class=&quot;l-constant&quot;&gt;DOOR_CLOSING&lt;/span&gt; &lt;span class=&quot;l-operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;l-string&quot;&gt;&quot;door_closing&quot;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Events then cause state transitions:&lt;/p&gt;
&lt;pre class=&quot;lumis&quot;&gt;&lt;code class=&quot;language-plaintext&quot; translate=&quot;no&quot; tabindex=&quot;0&quot;&gt;&lt;span class=&quot;l-line&quot; data-line=&quot;1&quot;&gt;IDLE&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;2&quot;&gt;  ↓ request above&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;3&quot;&gt;MOVING_UP&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;4&quot;&gt;  ↓ target reached&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;5&quot;&gt;DOOR_OPENING&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;6&quot;&gt;  ↓&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;7&quot;&gt;DOOR_OPEN&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;8&quot;&gt;  ↓&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;9&quot;&gt;DOOR_CLOSING&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;10&quot;&gt;  ↓&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;11&quot;&gt;MOVING_UP&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This becomes particularly useful once you need to handle things such as emergency stops, overloaded lifts, door obstruction, and new requests arriving while the lift is moving.&lt;/p&gt;
&lt;h2&gt;The interesting part is the optimisation&lt;/h2&gt;
&lt;p&gt;The basic mechanics of a lift are relatively straightforward.&lt;/p&gt;
&lt;p&gt;The difficult part is deciding what &lt;strong&gt;optimal&lt;/strong&gt; means.&lt;/p&gt;
&lt;p&gt;A real controller might try to minimise:&lt;/p&gt;
&lt;pre class=&quot;lumis&quot;&gt;&lt;code class=&quot;language-plaintext&quot; translate=&quot;no&quot; tabindex=&quot;0&quot;&gt;&lt;span class=&quot;l-line&quot; data-line=&quot;1&quot;&gt;total waiting time&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;2&quot;&gt;+ total travel time&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;3&quot;&gt;+ number of stops&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;4&quot;&gt;+ direction changes&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;5&quot;&gt;+ energy consumption&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;6&quot;&gt;+ passenger congestion&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And these objectives can conflict.&lt;/p&gt;
&lt;p&gt;Optimising one passenger&apos;s journey may make everybody else&apos;s journey worse.&lt;/p&gt;
&lt;p&gt;That&apos;s why lift scheduling is a useful example of a broader class of software problems: &lt;strong&gt;resource allocation under changing constraints&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;The same pattern appears in job schedulers, CPU scheduling, delivery routing, database query planning, and many other systems.&lt;/p&gt;
&lt;p&gt;You don&apos;t necessarily need to find the mathematically perfect solution. A good practical approach is often:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Model the current state.&lt;/li&gt;
&lt;li&gt;Generate possible decisions.&lt;/li&gt;
&lt;li&gt;Estimate the cost of each decision.&lt;/li&gt;
&lt;li&gt;Pick the lowest-cost option.&lt;/li&gt;
&lt;li&gt;Recalculate when the state changes.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;For a lift, that&apos;s enough to go from a simple &quot;nearest lift wins&quot; implementation to a surprisingly sophisticated scheduling system.&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://www.yellowduck.be/tags/development&quot;&gt;#development&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/pattern&quot;&gt;#pattern&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/python&quot;&gt;#python&lt;/a&gt;&lt;/p&gt;</content>
    <published>2026-10-11T17:00:00Z</published>
    <id>https://www.yellowduck.be/posts/building-a-lift-scheduling-algorithm-in-python</id>
    <title>🐥 Building a lift scheduling algorithm in Python</title>
    <updated>2026-10-11T17:00:00Z</updated>
  </entry>
  <entry>
    <author>
      <name>Pieter Claerhout</name>
      <email>pieter@yellowduck.be</email>
    </author>
    <link href="https://www.yellowduck.be/posts/exit-may-silently-break-your-parallel-tests" rel="alternate"/>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;The article reveals how using &lt;code&gt;exit()&lt;/code&gt; in a PHP application can introduce silent failures in parallel testing environments, particularly when using Pest 5 with its new TIA engine. When a worker process crashes, the resulting &lt;code&gt;WorkerCrashedException&lt;/code&gt; provides little insight, often leaving developers confused. The author recounts a debugging session that uncovered the root cause: &lt;code&gt;exit()&lt;/code&gt; terminating worker processes without error messages being captured by the parent process. The recommended fix involves replacing &lt;code&gt;exit()&lt;/code&gt; with throwing exceptions, allowing PHPUnit to record errors properly it&apos;s designed for. This change significantly improves error visibility and helps surface underlying issues in legacy codebases. Furthermore, it highlights the need for developers to search for &lt;code&gt;exit()&lt;/code&gt; calls before enabling parallel test execution, as they pose serious risks in identifying failure points across distributed tests.&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;&lt;a href=&quot;https://dyrynda.com.au/blog/exit-may-silently-break-your-parallel-tests&quot;&gt;Continue reading on &lt;strong&gt;dyrynda.com.au&lt;/strong&gt;&lt;/a&gt;&lt;p&gt;&lt;a href=&quot;https://www.yellowduck.be/tags/reading-list&quot;&gt;#reading-list&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/php&quot;&gt;#php&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/best-practice&quot;&gt;#best-practice&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/testing&quot;&gt;#testing&lt;/a&gt;&lt;/p&gt;</content>
    <published>2026-10-11T13:00:00Z</published>
    <id>https://www.yellowduck.be/posts/exit-may-silently-break-your-parallel-tests</id>
    <title>🔗 exit() may silently break your parallel tests</title>
    <updated>2026-10-11T13:00:00Z</updated>
  </entry>
  <entry>
    <author>
      <name>Pieter Claerhout</name>
      <email>pieter@yellowduck.be</email>
    </author>
    <link href="https://www.yellowduck.be/posts/we-replaced-our-ledger-with-two-functions" rel="alternate"/>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;Every transaction at River, a Bitcoin financial institution, now flows through just two core functions: &lt;code&gt;get_balances&lt;/code&gt; and &lt;code&gt;record_event&lt;/code&gt;. This shift was part of a significant redesign of their ledger system, which originally suffered from complexity and inefficiencies with nearly 40 functions. The new approach employs double-entry event sourcing to maintain accounting accuracy and utilizes a Narrow-Waist API to simplify operations. With this redesign, they achieved a zero-downtime migration during high-volume trading days by carefully managing both ledger systems in tandem. River’s innovative strategy included creating extensive testing scenarios and robust simulation tools, which significantly reduced potential production incidents. This article highlights how a thoughtfully constructed ledger can lead to improved performance and more resilient operations while embracing advances in technology, including AI integration.&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;&lt;a href=&quot;https://river.com/content/we-replaced-our-ledger-with-two-functions&quot;&gt;Continue reading on &lt;strong&gt;river.com&lt;/strong&gt;&lt;/a&gt;&lt;p&gt;&lt;a href=&quot;https://www.yellowduck.be/tags/reading-list&quot;&gt;#reading-list&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/development&quot;&gt;#development&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/database&quot;&gt;#database&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/best-practice&quot;&gt;#best-practice&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/testing&quot;&gt;#testing&lt;/a&gt;&lt;/p&gt;</content>
    <published>2026-10-11T08:00:00Z</published>
    <id>https://www.yellowduck.be/posts/we-replaced-our-ledger-with-two-functions</id>
    <title>🔗 We replaced our ledger with two functions</title>
    <updated>2026-10-11T08:00:00Z</updated>
  </entry>
  <entry>
    <author>
      <name>Pieter Claerhout</name>
      <email>pieter@yellowduck.be</email>
    </author>
    <link href="https://www.yellowduck.be/posts/99-of-my-website-traffic-is-bots" rel="alternate"/>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;99% of traffic on a website isn&apos;t from human visitors, and one site owner shares their battle against bots. The article outlines a year-long struggle managing scrapers on a 1.5 million-page philanthropy donor database called PatronView. With a staggering ratio of 214 bot page loads for every real human pageview, the owner discovered the impact of various bots, including those from China and AI-driven crawlers. They implemented several measures, including country-wide blocks and tailored firewall rules, to restore some semblance of normalcy. The piece details techniques employed to combat the overwhelm of bot traffic, improving site performance, and even blocking well-known crawlers that brought little value. Readers gain insights into the challenges of website management in the age of aggressive bot traffic.&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;&lt;a href=&quot;https://patronview.com/news/99-percent-of-my-website-traffic-is-bots/&quot;&gt;Continue reading on &lt;strong&gt;patronview.com&lt;/strong&gt;&lt;/a&gt;&lt;p&gt;&lt;a href=&quot;https://www.yellowduck.be/tags/reading-list&quot;&gt;#reading-list&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/tools&quot;&gt;#tools&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/devops&quot;&gt;#devops&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/sysadmin&quot;&gt;#sysadmin&lt;/a&gt;&lt;/p&gt;</content>
    <published>2026-10-10T17:00:00Z</published>
    <id>https://www.yellowduck.be/posts/99-of-my-website-traffic-is-bots</id>
    <title>🔗 99% of my website traffic is bots</title>
    <updated>2026-10-10T17:00:00Z</updated>
  </entry>
  <entry>
    <author>
      <name>Pieter Claerhout</name>
      <email>pieter@yellowduck.be</email>
    </author>
    <link href="https://www.yellowduck.be/posts/building-rag-in-laravel-four-ingestion-bugs-that-silently-wreck-retrieval" rel="alternate"/>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;Retrieval failures can sneak up on developers using Laravel for Retrieval-Augmented Generation (RAG). Many guides wrap up after basic setup, leaving potential pitfalls unaddressed. For instance, improper chunking can lead to missing context from responses. Simple mistakes, like mismatched index operators between query and retrieval, can cause silent failures that degrade accuracy without throwing errors. The article outlines four specific bugs that impact ingestion processes, demonstrating how they can quietly affect retrieval quality. Readers learn how to avoid common misconfigurations in chunking, indexing, and embedding, ensuring their system delivers reliable answers from the correct context. The piece serves both as a warning and a roadmap for fine-tuning retrieval performance in Laravel applications.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;$chunks = str_split($content, 600);&lt;/code&gt; should be replaced with smarter chunking to maintain context, highlighting the need for thorough evaluation and testing.&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;&lt;a href=&quot;https://mujahidabbas.dev/blog/building-rag-laravel-pgvector/&quot;&gt;Continue reading on &lt;strong&gt;mujahidabbas.dev&lt;/strong&gt;&lt;/a&gt;&lt;p&gt;&lt;a href=&quot;https://www.yellowduck.be/tags/reading-list&quot;&gt;#reading-list&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/laravel&quot;&gt;#laravel&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/php&quot;&gt;#php&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/best-practice&quot;&gt;#best-practice&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/ai&quot;&gt;#ai&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/testing&quot;&gt;#testing&lt;/a&gt;&lt;/p&gt;</content>
    <published>2026-10-10T13:00:00Z</published>
    <id>https://www.yellowduck.be/posts/building-rag-in-laravel-four-ingestion-bugs-that-silently-wreck-retrieval</id>
    <title>🔗 Building RAG in Laravel: Four ingestion bugs that silently wreck retrieval</title>
    <updated>2026-10-10T13:00:00Z</updated>
  </entry>
  <entry>
    <author>
      <name>Pieter Claerhout</name>
      <email>pieter@yellowduck.be</email>
    </author>
    <link href="https://www.yellowduck.be/posts/text-editor-data-structures" rel="alternate"/>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;Text editors present a unique challenge for programmers, balancing between ease of use and efficiency. This article dives into the intricacies of text editor data structures, exploring the evolution from simple strings to more complex solutions like the piece table and piece tree. It highlights the limitations of traditional methods such as giant strings and gap buffers, particularly regarding efficiency in insertion, deletion, and undo/redo operations.&lt;/p&gt;
&lt;p&gt;The author emphasizes a fail-fast mentality, urging experimentation to achieve functionality quickly. The piece tree, inspired by the VSCode implementation, combines the benefits of both rope and piece table approaches, offering optimizations like fast insertions and efficient memory usage. This piece of writing not only shares technical insights but also advocates for building tools tailored to individual needs, sparking curiosity about the capabilities of custom text editors.&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;&lt;a href=&quot;https://cdacamar.github.io/data%20structures/algorithms/benchmarking/text%20editors/c++/editor-data-structures/&quot;&gt;Continue reading on &lt;strong&gt;cdacamar.github.io&lt;/strong&gt;&lt;/a&gt;&lt;p&gt;&lt;a href=&quot;https://www.yellowduck.be/tags/reading-list&quot;&gt;#reading-list&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/development&quot;&gt;#development&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/pattern&quot;&gt;#pattern&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/tools&quot;&gt;#tools&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/best-practice&quot;&gt;#best-practice&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/vscode&quot;&gt;#vscode&lt;/a&gt;&lt;/p&gt;</content>
    <published>2026-10-10T08:00:00Z</published>
    <id>https://www.yellowduck.be/posts/text-editor-data-structures</id>
    <title>🔗 Text editor data structures</title>
    <updated>2026-10-10T08:00:00Z</updated>
  </entry>
  <entry>
    <author>
      <name>Pieter Claerhout</name>
      <email>pieter@yellowduck.be</email>
    </author>
    <link href="https://www.yellowduck.be/posts/the-goldilocks-customizable-select-height" rel="alternate"/>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;A new approach to enhancing the &lt;code&gt;select&lt;/code&gt; element focuses on a customizable height that meets user expectations. Jake Archibald opens the discussion with his experiences from a recent talk where he attempted to style the &lt;code&gt;select&lt;/code&gt; element effectively using CSS. This exploration highlights a common sizing pattern that many developers desire but often struggle to achieve. The article addresses specific browser discrepancies, particularly with Firefox and Safari, and proposes solutions involving CSS properties like &lt;code&gt;max-block-size&lt;/code&gt; and &lt;code&gt;calc-size()&lt;/code&gt;. Archibald recommends creating better user experiences by ensuring that dropdowns do not overflow or appear too small. By implementing margins and setting minimum and maximum sizes based on context, developers can refine the usability of customized dropdowns, ultimately making them more user-friendly across browsers.&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;&lt;a href=&quot;https://jakearchibald.com/2026/goldilocks-select-height/&quot;&gt;Continue reading on &lt;strong&gt;jakearchibald.com&lt;/strong&gt;&lt;/a&gt;&lt;p&gt;&lt;a href=&quot;https://www.yellowduck.be/tags/reading-list&quot;&gt;#reading-list&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/frontend&quot;&gt;#frontend&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/css&quot;&gt;#css&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/best-practice&quot;&gt;#best-practice&lt;/a&gt;&lt;/p&gt;</content>
    <published>2026-10-09T17:00:00Z</published>
    <id>https://www.yellowduck.be/posts/the-goldilocks-customizable-select-height</id>
    <title>🔗 The Goldilocks customizable select height</title>
    <updated>2026-10-09T17:00:00Z</updated>
  </entry>
  <entry>
    <author>
      <name>Pieter Claerhout</name>
      <email>pieter@yellowduck.be</email>
    </author>
    <link href="https://www.yellowduck.be/posts/dark-mode-with-web-standards" rel="alternate"/>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;Using &lt;code&gt;prefers-color-scheme&lt;/code&gt; is a straightforward way to respect users&apos; OS settings for dark mode, but users crave more control. This article explains how to support user-customized themes by implementing a toggle in web applications. It details the primary methods of defining color schemes in HTML and CSS, emphasizing a meta tag for faster loading times, and how to use JavaScript for easy toggling between light and dark modes.&lt;/p&gt;
&lt;p&gt;Furthermore, it clarifies what &lt;code&gt;color-scheme&lt;/code&gt; can influence, from system colors to gradients, but also notes its limitations, especially when paired with &lt;code&gt;prefers-color-scheme&lt;/code&gt;. The article also covers specifics around SVG and iframe interactions with color schemes and hints at emerging features in CSS that could refine theme handling in the future. Developers are encouraged to engage in discussions around browser implementations of these ideas.&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;&lt;a href=&quot;https://olliewilliams.xyz/blog/dark-mode/&quot;&gt;Continue reading on &lt;strong&gt;olliewilliams.xyz&lt;/strong&gt;&lt;/a&gt;&lt;p&gt;&lt;a href=&quot;https://www.yellowduck.be/tags/reading-list&quot;&gt;#reading-list&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/javascript&quot;&gt;#javascript&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/frontend&quot;&gt;#frontend&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/css&quot;&gt;#css&lt;/a&gt;&lt;/p&gt;</content>
    <published>2026-10-09T13:00:00Z</published>
    <id>https://www.yellowduck.be/posts/dark-mode-with-web-standards</id>
    <title>🔗 Dark mode with web standards</title>
    <updated>2026-10-09T13:00:00Z</updated>
  </entry>
  <entry>
    <author>
      <name>Pieter Claerhout</name>
      <email>pieter@yellowduck.be</email>
    </author>
    <link href="https://www.yellowduck.be/posts/slimselect-advanced-javascript-select-dropdown-library" rel="alternate"/>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;SlimSelect offers a robust alternative for those seeking a customized dropdown experience in vanilla JavaScript. This library enhances standard select elements with advanced features such as multi-select capabilities, easy styling, and improved usability. By leveraging simple API and configuration options, developers can seamlessly integrate SlimSelect into their projects without the overhead of larger frameworks. Its lightweight nature and performance-oriented design make it a compelling choice for modern web applications.&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;&lt;a href=&quot;https://slimselectjs.com/&quot;&gt;Continue reading on &lt;strong&gt;slimselectjs.com&lt;/strong&gt;&lt;/a&gt;&lt;p&gt;&lt;a href=&quot;https://www.yellowduck.be/tags/reading-list&quot;&gt;#reading-list&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/javascript&quot;&gt;#javascript&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/frontend&quot;&gt;#frontend&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/tools&quot;&gt;#tools&lt;/a&gt;&lt;/p&gt;</content>
    <published>2026-10-09T08:00:00Z</published>
    <id>https://www.yellowduck.be/posts/slimselect-advanced-javascript-select-dropdown-library</id>
    <title>🔗 SlimSelect - Advanced JavaScript select dropdown library</title>
    <updated>2026-10-09T08:00:00Z</updated>
  </entry>
</feed>