<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0">
<channel>
<title>Coder Legion - Recent posts tagged software-engineering</title>
<link>https://coderlegion.com/tag/software-engineering</link>
<description>Powered by Question2Answer</description>
<item>
<title>Day 2: An Overview of Definition-Driven Architecture</title>
<link>https://coderlegion.com/33157/day-2-an-overview-of-definition-driven-architecture</link>
<description>&lt;p&gt;The previous post, &lt;a rel=&quot;nofollow&quot; href=&quot;https://coderlegion.com/33030/day-1-why-erp-needs-a-definition-driven-framework&quot;&gt;Why ERP Needs a Definition-Driven Framework&lt;/a&gt;, ended by promising this one would be about which decisions a framework should make on its users' behalf.&lt;/p&gt;
&lt;p&gt;The difference between a framework and a library is usually explained as &quot;you call a library, a framework calls you.&quot; That describes the mechanism, not the price. The real price is that once control is inverted, the framework's defaults become your defaults. It decides what data looks like, how a request flows, and where the extension points are. You are free to disagree, and the cost of disagreeing is working around it, which is usually more expensive than not using it at all.&lt;/p&gt;
&lt;p&gt;So the question to ask about a framework is not what it can do. The more things it can do, the fewer decisions it has made for you, and the same decision has to be made again for every single form.&lt;/p&gt;
&lt;p&gt;This post answers the four questions a definition-driven framework has to settle first. Every other design choice is a consequence of these four answers:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Which layer holds the single source of truth&lt;/li&gt;
&lt;li&gt;Which layers are derived from it&lt;/li&gt;
&lt;li&gt;What is left to the application&lt;/li&gt;
&lt;li&gt;Where this whole approach does not hold&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;None of the four can be left blank. If the framework does not answer them, the people using it will answer them individually, and their answers will not agree. This module puts the truth in an entity, that one puts it in the table, a third puts it in a form configuration file on the front end. A good part of the three long-standing problems from the previous post grows out of exactly that.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;1-which-layer-holds-the-single-source-of-truth&quot;&gt;1. Which layer holds the single source of truth&lt;/h3&gt;
&lt;p&gt;First, a distinction that is easy to blur: a Code-First stack is not short of definitions. The API contract is a definition, the migration script is a definition, so is the database's own metadata. The difference is not whether definitions exist. It is which way the arrow points. Those definitions are dumped out as products. Editing them is meaningless, because the next generation run overwrites them.&lt;/p&gt;
&lt;p&gt;So the question is not whether to have definition files. It is where the arrow should point, and there are only three candidates:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;Candidate &lt;/th&gt;&lt;th&gt;Paradigm &lt;/th&gt;&lt;th&gt;The catch&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;Code &lt;/td&gt;&lt;td&gt;Code-First &lt;/td&gt;&lt;td&gt;A type system is wide enough to hold any semantics you can think of. The price is that the only reader of the truth is the compiler, and changing the truth means going through compilation, packaging and deployment&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;The database &lt;/td&gt;&lt;td&gt;Database-First &lt;/td&gt;&lt;td&gt;Data is the final fact, but the type system is too narrow&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Definitions &lt;/td&gt;&lt;td&gt;Definition-Driven &lt;/td&gt;&lt;td&gt;Has to satisfy two conditions at once&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Where the database is narrow: it knows that &lt;code&gt;total_amount&lt;/code&gt; is a &lt;code&gt;decimal(18,4)&lt;/code&gt;, but not whether that number is an amount, a quantity or an exchange rate. It does not know whether the value should be rounded, how many digits to show, whether it is required, whether it can be edited, or whether it belongs in the query list. Those semantics have nowhere to live, so they end up somewhere else, and the truth has scattered again.&lt;/p&gt;
&lt;h4 id=&quot;for-definitions-to-be-the-truth-two-conditions-neither-optional&quot;&gt;For definitions to be the truth, two conditions, neither optional&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Wide enough to carry semantics beyond structure&lt;/li&gt;
&lt;li&gt;They are data, not code, which was the whole subject of the previous post&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In this framework, what a single field can declare falls into six categories:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;Category &lt;/th&gt;&lt;th&gt;What is declared &lt;/th&gt;&lt;th&gt;Can the database hold it?&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;Structure &lt;/td&gt;&lt;td&gt;Field name, data type, length, default value &lt;/td&gt;&lt;td&gt;✅&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Behavior &lt;/td&gt;&lt;td&gt;Required, read only, visible &lt;/td&gt;&lt;td&gt;Partly&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Presentation &lt;/td&gt;&lt;td&gt;Caption, control type, display format, width &lt;/td&gt;&lt;td&gt;❌&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Semantics &lt;/td&gt;&lt;td&gt;What kind of number this is, which field carries the currency, which field carries the unit of measure &lt;/td&gt;&lt;td&gt;❌&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Relationships &lt;/td&gt;&lt;td&gt;The related form, the field mapping, the display field &lt;/td&gt;&lt;td&gt;❌&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Security &lt;/td&gt;&lt;td&gt;Data scope roles, sensitive data classification &lt;/td&gt;&lt;td&gt;❌&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Code can hold all six, but only as a stack of attributes, and once they are stacked someone still has to parse them back into data before anything can use them.&lt;/p&gt;
&lt;p&gt;The width of that declaration is deliberate. It has to cover the UI, the database and validation in one pass.&lt;/p&gt;
&lt;h4 id=&quot;a-single-source-of-truth-is-not-a-claim-it-is-a-dependency-direction&quot;&gt;A single source of truth is not a claim, it is a dependency direction&lt;/h4&gt;
&lt;p&gt;Any framework can write &quot;this framework treats X as the single source of truth&quot; in its documentation. To verify it, the dependency direction is enough: the layer holding the truth should depend on nobody, and everybody should depend on it.&lt;/p&gt;
&lt;p&gt;The definition layer package has an extremely short reference list. It does not know a database exists, or HTTP, or a UI. In the other direction, data access, business logic, the API contract, the client, and every front end all reference it.&lt;/p&gt;
&lt;p&gt;That direction buys two things:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The definition layer can be processed by tools on its own. A definition editor, a generator, a batch checking script: each needs to reference only this one package, without dragging in the entire database and API stack. The claim in the previous post that a thousand definitions can be swept by one script rests on this.&lt;/li&gt;
&lt;li&gt;AI benefits too. To make it understand a system, feeding it the definitions is more efficient than having it scan the whole code base, and the conclusions are more reliable. The difference is not file size. It is whether relationships are declared or inferred. Ask which forms an order touches, and reading the definitions means reading relationships that were already declared; reading the code means chasing types, chasing queries, chasing configuration, and then inferring intent, and inference gets things wrong.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The tokens saved are a side effect. As for the business judgments that definitions do not cover, the code still has to be read.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;2-which-layers-are-derived-from-definitions&quot;&gt;2. Which layers are derived from definitions&lt;/h3&gt;
&lt;p&gt;The previous post used plain language: the UI, the database, CRUD SQL, validation rules. The precise list comes in two kinds. Most of it is generation:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;What is generated &lt;/th&gt;&lt;th&gt;From&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;The single record form layout &lt;/td&gt;&lt;td&gt;The form structure (master block plus detail block)&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;The list layout &lt;/td&gt;&lt;td&gt;The list fields the form structure declares&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;The lookup window layout &lt;/td&gt;&lt;td&gt;The queryable fields the form structure declares&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;The physical table structure &lt;/td&gt;&lt;td&gt;Every table in the form structure&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;INSERT / UPDATE / DELETE &lt;/td&gt;&lt;td&gt;The form structure's field set&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;SELECT, including cross table joins &lt;/td&gt;&lt;td&gt;The form structure's fields and declared relationships&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;The structure of the data container that crosses layers &lt;/td&gt;&lt;td&gt;The form structure's field set&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The other kind is evaluation. Here the framework does not build anything; it takes the rules and expressions declared in the definition and evaluates them. Validation rules are evaluated at save and delete. Computed fields are recalculated the moment the user changes a field they depend on.&lt;/p&gt;
&lt;p&gt;When a requirement converges into a new shared behavior, a matching mechanism grows. The query criteria screen for a report, for example, is essentially a set of fields plus editors, and a layout definition can describe it.&lt;/p&gt;
&lt;p&gt;Grouped by who consumes it:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Form structure definition
│
├─ Generates
│   ├─ Three layouts: single record / list / lookup window  -&amp;gt;  screens on every front end
│   ├─ Physical table structure  -&amp;gt;  the database
│   ├─ Two kinds of SQL: write / query, including cross table joins  -&amp;gt;  data access
│   └─ The cross layer data container structure  -&amp;gt;  transport
│
└─ Evaluates
    ├─ Validation rules  -&amp;gt;  at save and delete
    └─ Computed fields  -&amp;gt;  the moment a dependent field changes
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;generating-code-or-generating-objects&quot;&gt;Generating code, or generating objects&lt;/h4&gt;
&lt;p&gt;Growing something out of a definition at build time and at run time produces completely different results:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A build time generator produces code. When the definition changes, you regenerate, recompile and redeploy. Not one step of the delivery path from the previous post has been removed, and everything saved earlier is handed back at the final gate.&lt;/li&gt;
&lt;li&gt;Run time derivation produces objects in memory. Swap the definition, and the next read is the new one. This is exactly where &quot;no compilation, packaging or deployment between changing a definition and having it take effect&quot; holds.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Two things are paid for it:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Compile time type checking is gone. A misspelled field name is not discovered until run time, and how that debt gets repaid is the subject of &lt;a rel=&quot;nofollow&quot; href=&quot;https://medium.com/@jeff377/day-9-data-and-definitions-split-apart-so-forms-need-no-types-of-their-own-5e76d8564d72&quot;&gt;Data and Definitions Split Apart, So Forms Need No Types of Their Own&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Cache invalidation goes from an optimization to a problem that must be solved. Definitions will be cached for performance, so the framework needs a notification mechanism that carries a definition changed on one node out to every other node.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id=&quot;3-what-is-left-to-the-application&quot;&gt;3. What is left to the application&lt;/h3&gt;
&lt;p&gt;Where the framework should stop has two rules, one positive and one negative.&lt;/p&gt;
&lt;p&gt;The positive one: any behavior that is standardized and repetitive should converge into a framework mechanism.&lt;/p&gt;
&lt;p&gt;This is not merely about writing fewer lines:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;There is one way to do each thing, and a fix goes in one place, which saves maintenance&lt;/li&gt;
&lt;li&gt;New joiners, consultants and whoever inherits the system learn it once, and understanding one form means understanding all of them, which saves training and is the most commonly underestimated of the two&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The symptoms of losing control listed in the previous post are all invoices issued by the absence of a general mechanism, and they are reissued per person, per year.&lt;/p&gt;
&lt;p&gt;The negative rule is a single test: would every ERP in the world do this the same way? What is the same belongs to the framework. What differs belongs to the application.&lt;/p&gt;
&lt;p&gt;By that line the framework stays out of three areas:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Business judgment: how a status transitions, when shipping must be blocked. Every company differs.&lt;/li&gt;
&lt;li&gt;SQL for reports and batch jobs: multi table joins, aggregation, dynamic criteria, performance tuning. Forcing these into definitions only makes them more complicated.&lt;/li&gt;
&lt;li&gt;The application shell: the login screen, connection settings, navigation menu, windows and theme.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Staying out is not the same as ignoring. Having handed those three over, the framework picks up two obligations it must get right:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A registry that maps a form identifier to the class you wrote&lt;/li&gt;
&lt;li&gt;A few extension points at unambiguous positions, so your logic can hook into the save and delete flows&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;How well those two are done decides directly whether the application is pleasant or awkward to write, and a later chapter takes them apart.&lt;/p&gt;
&lt;p&gt;Handing work over does not reduce complexity either. What definitions can absorb is repetition, not the inherent difficulty of the business. The hard parts have not disappeared. They have been concentrated in one place and given to whoever knows why they are hard.&lt;/p&gt;
&lt;p&gt;The third area is clearest in the case study. The Northwind front end has one UI project shared by every front end, and only four hand written screens: the main window, login, connection settings, and the layer that hosts forms. All four are shell. Not one of the eight business forms needs screen code.&lt;/p&gt;
&lt;p&gt;You can see where the line is drawn at a glance: the hand written part is precisely the part definitions did not take over.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;4-architectural-patterns-three-sources-one-piece-from-each&quot;&gt;4. Architectural patterns: three sources, one piece from each&lt;/h3&gt;
&lt;p&gt;With the first three questions answered, the shape of the framework is fixed. Before the fourth, one description that gets applied to it deserves handling: a hybrid of N-Tier, Clean Architecture and MVVM. &quot;Hybrid&quot; turns into a decorative word very easily, so here is what was taken and what was not.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;Source &lt;/th&gt;&lt;th&gt;What was taken &lt;/th&gt;&lt;th&gt;What was not&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;N-Tier &lt;/td&gt;&lt;td&gt;The layer boundaries and the cross layer data container. One request travels front end, API, business object, repository, data access infrastructure, database, and each layer knows only its neighbor. The most completely taken of the three &lt;/td&gt;&lt;td&gt;Nothing&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Clean Architecture &lt;/td&gt;&lt;td&gt;The inward dependency direction from the first section, verifiable by opening the project reference lists &lt;/td&gt;&lt;td&gt;Its requirement on the domain. The pure approach gives every business concept its own strongly typed domain entity, and modeling a thousand tables one at a time is the scale cost from the previous post itself&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;MVVM &lt;/td&gt;&lt;td&gt;All three roles are present. The model is the cross layer data container, the view is rendered from the layout definition, and the view model is a framework provided &lt;code&gt;DataObject&lt;/code&gt; &lt;/td&gt;&lt;td&gt;Nothing; the only difference is who writes the view model&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;That &lt;code&gt;DataObject&lt;/code&gt; carries the full job: wrapping the data container, exposing field reads and writes for screen binding, raising notifications for value changes and unsaved state, managing the start and cancellation of detail row editing, and loading, saving and deleting against the back end. The difference is that it is not hand written per form. Its shape comes from the definition. The price is that the screen binds not to a property like &lt;code&gt;order.CustomerId&lt;/code&gt; but to a field name.&lt;/p&gt;
&lt;p&gt;The cross layer data container is not a custom type either, and a later post is devoted to what that choice costs.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;5-systems-this-should-not-be-applied-to&quot;&gt;5. Systems this should not be applied to&lt;/h3&gt;
&lt;p&gt;Three questions before you commit. If any one of them does not survive, seriously consider not applying this.&lt;/p&gt;
&lt;p&gt;Q1: Are your screens standardized forms?&lt;br&gt;
Definitions can drive layout only because the layout patterns converged, and what converged here is a block of master fields plus a detail block. Dashboards, charts, drag and drop scheduling and maps are not in the pattern. The framework's answer is not &quot;we support those too.&quot; It is that this road does not go through definitions, and you write it yourself.&lt;/p&gt;
&lt;p&gt;Q2: Is your request a single document?&lt;br&gt;
The cross layer data container is self describing, and the price is that every response carries the field structure with it. For one order that is worth it. For an event stream at tens of thousands per second it is not.&lt;/p&gt;
&lt;p&gt;Q3: How long does your system have to live?&lt;br&gt;
The cost is concentrated up front and the return does not show for three to five years. A system that ships, closes and gets rewritten wholesale three years later pays the cost and never collects.&lt;/p&gt;
&lt;p&gt;Not many scenarios satisfy all three at once, and internal enterprise management systems happen to hit every one. Finance, purchasing, inventory, sales, HR, customer relations: the screens are almost all standardized forms, the requests are almost all documents, and once it goes live it lives a long time.&lt;/p&gt;
&lt;p&gt;These boundaries are not &quot;the framework has not got there yet.&quot; They are places where getting there would make things worse. A framework that can do anything has made no decisions for you, and the decisions are exactly where a framework's value is.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;the-northwind-case-study-how-much-one-definition-does&quot;&gt;The Northwind case study: how much one definition does&lt;/h3&gt;
&lt;p&gt;The order in the case study has a single form structure definition, and it states:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;One master with one detail&lt;/li&gt;
&lt;li&gt;Three cross table relationships on the master, to customer, employee and shipper, each a lookup window that brings one record back&lt;/li&gt;
&lt;li&gt;Each detail row picks a product&lt;/li&gt;
&lt;li&gt;Amounts are not typed by anyone. They are calculated from quantity, unit price and discount&lt;/li&gt;
&lt;li&gt;Three validation rules have to pass before saving&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Once that one definition is written, everything in the table from the second section is in place. The tables are created from it, the list and lookup window layouts grow directly out of it, the write and query SQL is assembled by the framework, the data carries its structure with it across layers, and the validation rules are checked at the moment of saving. Not one of those requires additional code.&lt;/p&gt;
&lt;p&gt;The same definition is read by four front ends: desktop, browser, iOS and Android, all sharing one UI project.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;The value of a framework is not how much it can do. It is whether it can tell which decisions belong to it: the thing that is identical a thousand times over is its, and the thing that differs every time is yours. Draw that line crooked and both sides carry the weight. Draw it too conservatively and you redo the same work a thousand times. Draw it too greedily and you spend more effort working around it.&lt;/p&gt;
&lt;p&gt;The next post, &lt;a rel=&quot;nofollow&quot; href=&quot;https://dev.to/jeff377/day-3-the-northwind-case-study-eight-forms-and-a-custom-business-object-3i33&quot;&gt;The Northwind Case Study: Eight Forms and a Custom Business Object&lt;/a&gt;, brings the case study on stage.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;📚 Part of the thirty-post series &lt;a rel=&quot;nofollow&quot; href=&quot;https://coderlegion.com/33029/erp-architects-notes-designing-a-definition-driven-framework-series-index&quot;&gt;ERP Architect's Notes: Designing a Definition-Driven Framework&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;📘 Original article on HackMD, in Traditional Chinese:&lt;br&gt;
👉 &lt;a rel=&quot;nofollow&quot; href=&quot;https://hackmd.io/@jeff377/day-02-framework-decisions&quot;&gt;https://hackmd.io/@jeff377/day-02-framework-decisions&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;📢 You are welcome to republish this, please credit the source.&lt;br&gt;
📬 Follow for more engineering notes:&lt;br&gt;
&lt;a rel=&quot;nofollow&quot; href=&quot;https://www.facebook.com/profile.php?id=61574839666569&quot;&gt;Facebook&lt;/a&gt; ｜ &lt;a rel=&quot;nofollow&quot; href=&quot;https://hackmd.io/@jeff377&quot;&gt;HackMD&lt;/a&gt; ｜ &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/jeff377&quot;&gt;GitHub&lt;/a&gt; ｜ &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.nuget.org/profiles/jeff377&quot;&gt;NuGet&lt;/a&gt;&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/33157/day-2-an-overview-of-definition-driven-architecture</guid>
<pubDate>Sat, 31 Oct 2026 01:00:08 +0000</pubDate>
</item>
<item>
<title>How to Handle Duplicate Files Without Losing the Record That Matters</title>
<link>https://coderlegion.com/33057/how-to-handle-duplicate-files-without-losing-the-record-that-matters</link>
<description>&lt;p&gt;Every office has the same quiet problem. A client sends a contract on WhatsApp, someone downloads it, and the same file arrives again by email the next morning. A colleague then saves a &quot;final&quot; copy to their desktop. Within a week, one document has become five, and nobody is sure which one is real.&lt;/p&gt;
&lt;p&gt;Cleaning this up sounds simple. Delete the extras and move on. But if you delete the wrong copy, you may lose a signed version, a date stamp, or the proof of who sent what. This guide explains how to deal with duplicate files safely, and how good &lt;strong&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://www.intellodocs.com/&quot;&gt;Document Management Software in India&lt;/a&gt;&lt;/strong&gt; can make the whole job easier.&lt;/p&gt;
&lt;h2 id=&quot;why-duplicate-files-pile-up-so-quickly&quot;&gt;Why Duplicate Files Pile Up So Quickly&lt;/h2&gt;
&lt;p&gt;Duplicates rarely come from carelessness. They come from the way work actually happens.&lt;/p&gt;
&lt;p&gt;Documents reach a team from many directions, such as chat groups, inboxes, scanners and shared drives. Each channel creates its own copy. People also save files &quot;just in case,&quot; rename them to suit themselves, and forward attachments instead of sharing links. Nobody means to create clutter, yet the clutter grows anyway.&lt;/p&gt;
&lt;p&gt;The cost is real. Staff waste time hunting for the right file, and they sometimes act on an outdated one. In a law, accounting or compliance setting, working from the wrong version can lead to a missed clause, a wrong figure or an awkward conversation with a client.&lt;/p&gt;
&lt;h2 id=&quot;not-all-duplicates-are-the-same&quot;&gt;Not All Duplicates Are the Same&lt;/h2&gt;
&lt;p&gt;Before you delete anything, understand what kind of duplicate you are looking at. They fall into three broad groups.&lt;/p&gt;
&lt;h3 id=&quot;exact-copies&quot;&gt;Exact copies&lt;/h3&gt;
&lt;p&gt;These are identical files. The content, size and date are the same, and only the location or name differs. They are the safest to clean up, but even here you should confirm that the file is really identical, not just similarly named.&lt;/p&gt;
&lt;h3 id=&quot;near-duplicates-and-versions&quot;&gt;Near duplicates and versions&lt;/h3&gt;
&lt;p&gt;This group causes most of the trouble. Two files look alike, but one has an extra paragraph, a corrected amount or a new signature page. Neither is a true copy of the other. Deleting the older one may erase the history of how the document changed, which matters a great deal in contracts, board resolutions and tax records.&lt;/p&gt;
&lt;h3 id=&quot;the-same-document-from-different-channels&quot;&gt;The same document from different channels&lt;/h3&gt;
&lt;p&gt;A client may send one agreement through WhatsApp and again by email. The content may match, but the two arrivals tell you different things. One shows when the client first sent it, and the other shows when it reached the formal inbox. That origin and timing can become important evidence later.&lt;/p&gt;
&lt;h2 id=&quot;how-to-spot-the-record-that-matters&quot;&gt;How to Spot the Record That Matters&lt;/h2&gt;
&lt;p&gt;The &quot;record that matters&quot; is not always the newest file, and it is not always the biggest one. It is the version you could rely on if somebody asked you to prove it tomorrow.&lt;/p&gt;
&lt;p&gt;Ask yourself these questions:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Is this the signed, stamped or approved version?&lt;/li&gt;
&lt;li&gt;Does it have a clear date, sender and source?&lt;/li&gt;
&lt;li&gt;Is it the last version that went to the client, the auditor or the regulator?&lt;/li&gt;
&lt;li&gt;Can I show how it differs from earlier drafts?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you can answer yes to most of these, you have likely found your master record. Everything else is either a duplicate to retire or a version to preserve as history.&lt;/p&gt;
&lt;h2 id=&quot;a-simple-way-to-clean-up-duplicate-files&quot;&gt;A Simple Way to Clean Up Duplicate Files&lt;/h2&gt;
&lt;p&gt;You do not need a huge project to start. Work through these steps in order, and you will avoid most of the common mistakes.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Pick one place to begin.&lt;/strong&gt; Choose a single client folder or a single month. Trying to fix the whole archive at once usually ends in frustration.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Group files that look related.&lt;/strong&gt; Put together anything with the same document type, party and date range.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Compare, don't assume.&lt;/strong&gt; Open the files side by side. Check dates, amounts, names and signatures, not just filenames.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Mark the master record.&lt;/strong&gt; Decide which file is the reliable one, and note why you chose it.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Keep the history, retire the noise.&lt;/strong&gt; Exact copies can be removed. Real versions should be kept with their dates and authors.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Record what you did.&lt;/strong&gt; A short note on who cleaned up what, and when, saves arguments later.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Set a rule for next time.&lt;/strong&gt; Decide where new files go, so the same mess does not return.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Doing this by hand works for a small folder. For a larger archive, however, it becomes slow, tiring and easy to get wrong.&lt;/p&gt;
&lt;h2 id=&quot;why-deleting-is-the-riskiest-move&quot;&gt;Why Deleting Is the Riskiest Move&lt;/h2&gt;
&lt;p&gt;Deleting feels tidy, but it is the one step you cannot take back. Neither a recycle bin nor a backup is a safe plan if you did not know what you were removing in the first place.&lt;/p&gt;
&lt;p&gt;There are three common ways deletion goes wrong:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;You remove the only signed copy&lt;/strong&gt; and keep an unsigned draft.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;You lose the provenance,&lt;/strong&gt; meaning the proof of who sent the file and when.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;You break a legal or audit requirement,&lt;/strong&gt; because some records must be kept for a set period, or kept untouched if they are under review.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;A safer habit is to retire duplicates, not destroy them. Move them out of daily view, keep a record of the action, and delete only when your retention rules clearly allow it.&lt;/p&gt;
&lt;h2 id=&quot;what-to-look-for-in-document-management-software-in-india&quot;&gt;What to Look For in Document Management Software in India&lt;/h2&gt;
&lt;p&gt;Good Document Management Software in India does more than store files. It helps you stay in control when documents arrive from many places and must be produced again years later. When you compare tools, look for these features.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Version history with restore.&lt;/strong&gt; Every revision should be kept with its author and timestamp, so you can go back to any earlier state.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Visual comparison.&lt;/strong&gt; You should be able to see exactly what changed between two versions, without reading both line by line.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;An access and audit log.&lt;/strong&gt; Every view, edit, share and deletion should be recorded, ideally in a way that cannot be altered.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Check-in and check-out.&lt;/strong&gt; This stops two people from quietly overwriting each other's work.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Provenance tracking.&lt;/strong&gt; The tool should remember which channel a file came from, who sent it and when.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Retention rules and legal hold.&lt;/strong&gt; You should be able to set how long records stay, and freeze anything under scrutiny.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Per-file permissions.&lt;/strong&gt; Not everyone needs to see everything, and clients should only see what you share with them.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Data control.&lt;/strong&gt; Since many Indian firms care about where their data lives, check whether the tool supports your region, your policy and India's data protection rules.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Whether you run a law practice, a CA firm or a compliance team, these basics matter more than a long list of flashy extras.&lt;/p&gt;
&lt;h2 id=&quot;how-intellodocs-approaches-the-problem&quot;&gt;How IntelloDocs Approaches the Problem&lt;/h2&gt;
&lt;p&gt;IntelloDocs is an AI-native document vault built by Imbibe Technologies in Karnal. It takes a slightly different route from older systems. Instead of asking people to sort files after the fact, it deals with the mess at the point of arrival.&lt;/p&gt;
&lt;p&gt;Files can be captured from WhatsApp, email attachments, office scanners and direct uploads. As each file comes in, the system keeps its origin, sender and timestamp attached to it. That means you are not only storing a document but also keeping the story of how it reached you.&lt;/p&gt;
&lt;p&gt;The AI then reads the content of the document instead of relying on the filename. It picks out the kind of record, the parties, dates and amounts, and suggests where the file should go. A person confirms it with one tap, so nothing is filed silently.&lt;/p&gt;
&lt;p&gt;Several other features help when duplicates and versions pile up:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Versioning and visual diffs&lt;/strong&gt; show how a document changed over time.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Check-in and check-out&lt;/strong&gt; prevent accidental overwrites.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;An immutable access log&lt;/strong&gt; records every open, edit, share and deletion.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Retention rules and legal hold&lt;/strong&gt; protect records that must not be touched.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cited answers&lt;/strong&gt; let you ask a question in plain language and see the exact sentence the answer came from.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;IntelloDocs is currently in Phase 1 early access, so it is worth speaking with the team about your own setup and archive before you decide.&lt;/p&gt;
&lt;h2 id=&quot;habits-that-keep-duplicates-from-coming-back&quot;&gt;Habits That Keep Duplicates From Coming Back&lt;/h2&gt;
&lt;p&gt;Even the best software works better with a few simple team habits.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Share links, not attachments,&lt;/strong&gt; whenever you can.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Name files clearly,&lt;/strong&gt; or let the system name them for you.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Agree on one &quot;home&quot; for each client or matter,&lt;/strong&gt; so nobody wonders where a file belongs.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Review your archive on a schedule,&lt;/strong&gt; not only when it becomes a crisis.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Treat provenance as part of the record.&lt;/strong&gt; A file without a source is a weaker file.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These small changes cost very little, but they save hours over a year.&lt;/p&gt;
&lt;h2 id=&quot;frequently-asked-questions&quot;&gt;Frequently Asked Questions&lt;/h2&gt;
&lt;h3 id=&quot;1-is-it-safe-to-delete-duplicate-files-&quot;&gt;1. Is it safe to delete duplicate files?&lt;/h3&gt;
&lt;p&gt;It is safe only when you are sure the files are exact copies and no retention or legal rule applies. Near duplicates and different versions should be kept, because they show how a document changed. The safest approach is to retire duplicates first and delete them later, with a clear record of what was removed.&lt;/p&gt;
&lt;h3 id=&quot;2-how-does-document-management-software-in-india-help-with-duplicate-files-&quot;&gt;2. How does Document Management Software in India help with duplicate files?&lt;/h3&gt;
&lt;p&gt;Document Management Software in India gives you version history, audit logs, permissions and retention rules in one place. Instead of guessing which copy is right, you can see who changed what and when, restore earlier versions, and keep every action on record. Tools that capture the source and timestamp of each file make it even easier to find the record that matters.&lt;/p&gt;
&lt;h3 id=&quot;3-what-is-the-difference-between-a-duplicate-file-and-a-document-version-&quot;&gt;3. What is the difference between a duplicate file and a document version?&lt;/h3&gt;
&lt;p&gt;A duplicate is an identical copy of a file. A version is a changed copy, such as a draft with edits or a final with a signature added. Duplicates can usually be removed, but versions should be kept and labelled, since they form the history of the document.&lt;/p&gt;
&lt;h2 id=&quot;final-thoughts&quot;&gt;Final Thoughts&lt;/h2&gt;
&lt;p&gt;Duplicate files are not just a storage problem. They are a trust problem, because a team that cannot tell which file is real cannot fully rely on its own records. The answer is neither to hoard everything nor to delete in a hurry. Instead, identify the record that matters, keep its history and source, and retire the rest with care.&lt;/p&gt;
&lt;p&gt;If your team handles contracts, tax papers or client records, a well-built document management system will protect you far better than a folder full of renamed copies. Take a look at IntelloDocs at &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.intellodocs.com&quot;&gt;intellodocs&lt;/a&gt; to see how it handles capture, versioning and cited search in one place.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/33057/how-to-handle-duplicate-files-without-losing-the-record-that-matters</guid>
<pubDate>Fri, 30 Oct 2026 07:14:35 +0000</pubDate>
</item>
<item>
<title>The Ultimate Guide to Online Education Platforms with Analytics for Coaching Institutes</title>
<link>https://coderlegion.com/33045/the-ultimate-guide-to-online-education-platforms-with-analytics-for-coaching-institutes</link>
<description>&lt;p&gt;The way in which coaching institutes teach manage students. Measure academic progress is changing fast. Today’s learners expect flexibility, timely feedback and access to learning resources that go beyond the classroom. At the time coaching institutes have to manage batches, attendance, performance and show meaningful learning outcomes.&lt;/p&gt;
&lt;p&gt;This can become a challenge to handle all these responsibilities manually especially when an institute has courses, hundreds of students and growing faculty team. This is where online education platforms with analytics can really make a difference.&lt;/p&gt;
&lt;p&gt;These platforms combine learning management and performance tracking which enables coaching institutes to make data driven decisions boost student engagement and streamline operations. With so many solutions available how does an institute make the right choice?&lt;/p&gt;
&lt;p&gt;Knowing the features, benefits and selection criteria can help you identify the best online education platform for coaching institutes with analytics and build a more effective data-driven learning environment.&lt;/p&gt;
&lt;h3 id=&quot;what-is-an-online-education-platform-with-analytics-&quot;&gt;What is an online education platform with analytics?&lt;/h3&gt;
&lt;p&gt;An online education platform is a learning environment where teachers are able to deliver courses, share study materials, assess learners and interact with students. If the platform has analytic capabilities it can also display useful data on student participation, academic performance and learning progress.&lt;/p&gt;
&lt;p&gt;Coaching institutes can move beyond attendance registers, spreadsheets or periodic test results to a centralized platform to get insights on how students are interacting with learning content and where they need additional support.&lt;/p&gt;
&lt;p&gt;For instance an institution could observe that multiple students are struggling with the subject or that attendance declines at a particular point in a course. Learning analytics can assist educators in detecting patterns and exploring the reasons for them.&lt;/p&gt;
&lt;p&gt;The aim is not just to gather data. Its purpose is to transform information into actionable decisions that enhance teaching and learning.&lt;/p&gt;
&lt;h3 id=&quot;why-learning-analytics-should-be-adopted-by-coaching-institutes-&quot;&gt;Why Learning Analytics Should Be Adopted By Coaching Institutes?&lt;/h3&gt;
&lt;p&gt;All coaching institutes want their students to do well stay engaged and finish their courses successfully. Without reliable information it is difficult to find the reasons for the inconsistent performance.&lt;/p&gt;
&lt;p&gt;Analytics helps fill this gap shedding light on student activity and academic progress. When teachers are able to identify knowledge gap they can step in before small challenges become large learning barriers.&lt;/p&gt;
&lt;h3 id=&quot;more-insight-into-student-performance&quot;&gt;More Insight into Student Performance&lt;/h3&gt;
&lt;p&gt;Test scores are a measure of academic progress but they don’t always tell the story behind why a student is struggling. A student might understand the ideas. Make mistakes under pressure of time. Another may perpetually miss questions from a given topic because the underlying fundamentals are murky.&lt;/p&gt;
&lt;p&gt;Assessment analytics in a platform help the educators to look at the performance across the subjects, topics, tests and batches. This helps to identify problems that recur and change the teaching approach accordingly.&lt;/p&gt;
&lt;h3 id=&quot;enhance-student-engagement&quot;&gt;Enhance Student Engagement&lt;/h3&gt;
&lt;p&gt;You can also use attendance, assignment completion, participation and learning activity as measures of engagement. When all of these indicators are in one place faculty can identify students who may be falling behind.&lt;/p&gt;
&lt;p&gt;If participation drops suddenly for instance a teacher can figure out if a student is having trouble or needs more support.&lt;/p&gt;
&lt;p&gt;Analytics can’t account for every change in behavior but it can help educators to recognize when a conversation or intervention might be appropriate.&lt;/p&gt;
&lt;h3 id=&quot;smarter-college-choices&quot;&gt;Smarter College Choices&lt;/h3&gt;
&lt;p&gt;Coaching institutes decide batch allocation, revision schedules, faculty support, extra practice sessions regularly. Without information these decisions can be highly assumption-based or isolated observations.&lt;/p&gt;
&lt;p&gt;Analytics provide a structured basis for assessing what’s working and where there’s scope for improvement. Over time institutes may utilize this insight to improve their processes and provide a more consistent learning experience.&lt;/p&gt;
&lt;h3 id=&quot;important-features-to-consider-in-an-online-education-platform&quot;&gt;Important Features to Consider in an Online Education Platform&lt;/h3&gt;
&lt;p&gt;The choice of an education platform is not simply a matter of comparing the cost of subscriptions or verifying that online classes are supported. The right solution should align with the institute’s teaching model, operational needs and long-term growth plans.&lt;/p&gt;
&lt;h3 id=&quot;complete-learning-management&quot;&gt;Complete Learning Management&lt;/h3&gt;
&lt;p&gt;A good platform should allow you to organize courses upload study materials, schedule classes and manage learning resources. Students should be able to access content without having to wade through complex systems.&lt;/p&gt;
&lt;p&gt;Course and batch management is very important for coaching institutes offering programs or preparation for competitive examinations. A good learning environment keeps students on track. Reduces repetitive administrative work for faculty members.&lt;/p&gt;
&lt;h3 id=&quot;analytics-of-performance-and-assessment&quot;&gt;Analytics of Performance and Assessment&lt;/h3&gt;
&lt;p&gt;Assessment tools should consist of tests, quizzes, assignments and performance tracking. Ideally the educator should be able to see the results of a student as well as trends across batches and subjects.&lt;/p&gt;
&lt;p&gt;Useful analytics can include test scores, topic-wise accuracy, assessment completion and changes in performance over time. Specific capabilities vary by platform and institutes should assess whether the reports suit their academic needs.&lt;/p&gt;
&lt;p&gt;The best online education platform for coaching institutes with analytics should make these insights easy to understand and act upon without teachers with complex dashboards.&lt;/p&gt;
&lt;h3 id=&quot;student-support-and-communication&quot;&gt;Student Support and Communication&lt;/h3&gt;
&lt;p&gt;Regular communication keeps students informed of classes, assignments, test schedules and academic expectations. Institutes should seek features that allow sharing of updates and timely provision of guidance.&lt;/p&gt;
&lt;p&gt;These capabilities may include notifications, announcements, discussion spaces or other communication tools depending on the platform. The conclusion is that the right info is readily available to students and teachers without a lot of hassles.&lt;/p&gt;
&lt;h3 id=&quot;data-security-and-access-control&quot;&gt;Data Security and Access Control&lt;/h3&gt;
&lt;p&gt;Education platforms will store student information, assessment records and sometimes payment or contact details. This information should therefore be a consideration in platform selection and protected.&lt;/p&gt;
&lt;p&gt;Institutes should consider access rights, authentication, data protection standards, backup arrangements and the provider’s privacy position. They should also have internal procedures for determining who has access to student records and reports.&lt;/p&gt;
&lt;p&gt;A feature-rich platform is only valuable if used with the security and privacy practices.&lt;/p&gt;
&lt;h3 id=&quot;the-role-of-analytics-in-improving-teaching-and-learning-outcomes&quot;&gt;The Role of Analytics in Improving Teaching and Learning Outcomes&lt;/h3&gt;
&lt;p&gt;The true meaning of analytics is when teachers use data to enhance the learning process.&lt;/p&gt;
&lt;p&gt;Suppose there is a coaching institute which conducts a mathematics test The overall batch average seems satisfactory but if we look at topic wise results many students are finding algebra difficult. The faculty team can instead go back. Revisit the concept provide additional practice and do a follow-up assessment instead of just moving on to the next chapter.&lt;/p&gt;
&lt;p&gt;Analytics may also reveal that students who always do the practice assignments tend to perform on the mock tests. Faculty can use this observation to motivate practice and identify students who may require additional support.&lt;/p&gt;
&lt;p&gt;Analytics can also help institutes understand how successful their teaching methods are. When students keep having problems with a topic over batches it might be worthwhile to examine the teaching methodology learning resources or the test format.&lt;/p&gt;
&lt;p&gt;These examples show how data can lead to academic decision making. Analytics should support teachers’ professional judgments, not replace them. The context of the student of interactions in the classroom and of individual learning needs are critical to understanding performance.&lt;/p&gt;
&lt;h3 id=&quot;best-online-education-platforms-in-india-for-coaching-institutes-how-to-choose-&quot;&gt;Best Online Education Platforms in India for Coaching Institutes – How to Choose?&lt;/h3&gt;
&lt;p&gt;The Indian coaching industry caters to a set of learners from school students preparing for board examinations to aspirants preparing for competitive entrance and government recruitment examinations. Thus the need of one institute may be quite different from those of another institute.&lt;/p&gt;
&lt;p&gt;While judging the &lt;strong&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://imbibe.in/itlearn/&quot;&gt;best online education platforms in India for coaching institutes&lt;/a&gt;&lt;/strong&gt; know what problems your institute needs to solve first.&lt;/p&gt;
&lt;p&gt;An institute focused on classes would put emphasis on video delivery, scheduling and interactive learning tools. A test-prep center might focus more on question banks, practice tests, automated scoring and detailed performance reports. If you’re an organization with multiple branches you may require centralized administration, role-based access and consolidated reporting.&lt;/p&gt;
&lt;p&gt;Scalability is another factor. A good platform for a batch should ideally be able to handle more students, courses and faculty without creating unnecessary operational complexity.&lt;/p&gt;
&lt;p&gt;Ease of use is also important. A sophisticated system will be of value if it is difficult for teachers to manage or for students to navigate. Institutions should consider the student and faculty experience before committing term.&lt;/p&gt;
&lt;p&gt;Pricing should be taken in context well. Check subscription costs, implementation requirements, support arrangements and additional charges for some functionalities. Of going for the cheapest option look for a platform that provides the best mix of features ease of use and long-term benefits.&lt;/p&gt;
&lt;p&gt;Finally consider the level of customer support and the provider’s ability to help your team onboard to the system. A smooth implementation process can make a difference to the platform’s overall effectiveness.&lt;/p&gt;
&lt;h3 id=&quot;mistakes-to-avoid-when-selecting-an-education-platform&quot;&gt;Mistakes to Avoid When Selecting an Education Platform&lt;/h3&gt;
&lt;p&gt;A mistake is to pick a platform based on the number of features it has. More features don’t automatically mean results. Institutes should concentrate on the capabilities that're directly responsive, to their teaching and administrative needs.&lt;/p&gt;
&lt;p&gt;Another mistake is to ignore how useful analytics can be. A dashboard can show a lot of information. The institute needs to be able to understand the data and use it to make good decisions. Before you pick a platform look at sample reports. Check if they answer the questions that your faculty members usually ask.&lt;/p&gt;
&lt;p&gt;Institutes also must not use technology in place of teaching. Even though an education platform can help learning by being organized easy to access and giving insights the main thing that helps students grow is teaching, regular practice, proper feedback and real interaction between teachers and students.&lt;/p&gt;
&lt;p&gt;Finally don't forget about how to put the system into place and train people. Teachers need time to learn the system set up their ways of working and use the features in their teaching. Letting teachers be part of choosing the platform can help them use it better and make sure the platform fits what happens in classrooms.&lt;/p&gt;
&lt;h3 id=&quot;the-future-of-coaching-centers-data-driven-learning&quot;&gt;The Future of Coaching Centers: Data-Driven Learning&lt;/h3&gt;
&lt;p&gt;As digital education keeps changing analytics is expected to become more important in how coaching institutes understand what students need and provide help.&lt;/p&gt;
&lt;p&gt;Newer systems could give detailed learning insights, personalized suggestions and automatic reports. Artificial intelligence could also help teachers see patterns in a lot of test data. The quality of the insights would depend on the quality of the data and how well the tools are used.&lt;/p&gt;
&lt;p&gt;Institutes should also have a way of doing things. Students’ data needs to be treated, automated suggestions should be checked and teachers should stay involved in important academic choices.&lt;/p&gt;
&lt;p&gt;The goal is not to make education fully based on data. It is to mix the data with human understanding so that every student gets better guidance and more chances to improve.&lt;/p&gt;
&lt;h3 id=&quot;transforming-learning-data-into-improved-student-outcomes&quot;&gt;Transforming Learning Data into Improved Student Outcomes&lt;/h3&gt;
&lt;p&gt;For any coaching institute that wants to improve its efficiency, student involvement and make academic choices picking an online education platform with analytics is an important choice. The best solution includes learning materials, tests, tracking progress and helpful information to support both teachers and students.&lt;/p&gt;
&lt;p&gt;Think about what your institute wants before making any decision. Compare the features ways to report how easy it is to grow, security, how easy it is to use and support. Important make sure the platform helps your teachers take information and use it to make teaching better.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://imbibe.in/itlearn/&quot;&gt;ItLearn&lt;/a&gt;&lt;/strong&gt; helps coaching institutes move to an online education setup to manage teaching and learning. If your institute is ready to make academic work easier improve the learning experience and find better ways to track student progress contact ItLearn now. Find out how the right education technology can help your institute build an organized, interesting and ready, for the future learning experience.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/33045/the-ultimate-guide-to-online-education-platforms-with-analytics-for-coaching-institutes</guid>
<pubDate>Fri, 30 Oct 2026 05:33:49 +0000</pubDate>
</item>
<item>
<title>ERP Architect's Notes: Designing a Definition-Driven Framework · Series Index</title>
<link>https://coderlegion.com/33029/erp-architects-notes-designing-a-definition-driven-framework-series-index</link>
<description>&lt;blockquote&gt;&lt;p&gt;The complexity of an ERP is not in how hard any single feature is. It is in how many times the same fact gets restated, multiplied by how many tables, multiplied again by how many customizations.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;A thirty-post series written for &lt;a rel=&quot;nofollow&quot; href=&quot;https://ithelp.ithome.com.tw/users/20007956/ironman/9251&quot;&gt;iThome Ironman 2026&lt;/a&gt;, a thirty-day technical writing challenge in Taiwan, published from 2026-08-17 to 2026-09-15. The series is complete.&lt;/p&gt;
&lt;p&gt;This series is not about building one form with a framework. It is about designing a framework that can drive hundreds of forms: the single source of truth lives in definitions rather than code, and the database structure, SQL, screens for multiple front ends, validation and computation, multiple languages, multi-tenancy and customization are all derived from the same layer of definitions. The Northwind case study verifies it, but the case study is evidence, not the subject.&lt;/p&gt;
&lt;p&gt;It is about mechanisms and trade-offs, not a usage tutorial, and the cost of every design is written as plainly as its benefits.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;chapter-one-why-definition-driven&quot;&gt;Chapter One · Why Definition-Driven&lt;/h3&gt;
&lt;p&gt;What the problem is, where the paradigms diverge, and the case study's first appearance. This chapter sets side by side how many places one field touches under each paradigm.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Day 1 · &lt;a rel=&quot;nofollow&quot; href=&quot;https://coderlegion.com/33030/day-1-why-erp-needs-a-definition-driven-framework&quot;&gt;Why ERP Needs a Definition-Driven Framework&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Day 2 · &lt;a rel=&quot;nofollow&quot; href=&quot;https://coderlegion.com/33157/day-2-an-overview-of-definition-driven-architecture&quot;&gt;An Overview of Definition-Driven Architecture&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Day 3 · The Northwind Case Study: Eight Forms and a Custom Business Object&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;chapter-two-definition-layer&quot;&gt;Chapter Two · Definition Layer&lt;/h3&gt;
&lt;p&gt;What kinds of definition files there are, why their lifecycles differ, and how one structural description grows into the database structure, CRUD SQL, screens for multiple front ends and business rules.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Day 4 · The Purpose and Classification of Definition Files&lt;/li&gt;
&lt;li&gt;Day 5 · How FormSchema Drives SQL&lt;/li&gt;
&lt;li&gt;Day 6 · TableSchema and Database Structure Upgrades&lt;/li&gt;
&lt;li&gt;Day 7 · FormLayout and Screen Generation for Multiple Front Ends&lt;/li&gt;
&lt;li&gt;Day 8 · Declarative Business Rules and Computed Fields&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;chapter-three-data-access-and-caching&quot;&gt;Chapter Three · Data Access and Caching&lt;/h3&gt;
&lt;p&gt;Without writing DTOs, how data crosses layers; the only two things multiple databases have in common; and how a cache knows its source has changed.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Day 9 · Data and Definitions Split Apart, So Forms Need No Types of Their Own&lt;/li&gt;
&lt;li&gt;Day 10 · Multi-Database Support: CRUD and Structure Upgrades&lt;/li&gt;
&lt;li&gt;Day 11 · Connections, Transactions and Query Conditions in the Data Access Layer&lt;/li&gt;
&lt;li&gt;Day 12 · Cross-Node Invalidation for Definition and Data Caches&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;chapter-four-business-logic-and-api&quot;&gt;Chapter Four · Business Logic and API&lt;/h3&gt;
&lt;p&gt;Who takes over the part definitions cannot express, where the extension points open, and what a call passes through on its way from the caller to the business object.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Day 13 · Session Management and Lifecycle&lt;/li&gt;
&lt;li&gt;Day 14 · Extension Points on a Business Object and When to Override Them&lt;/li&gt;
&lt;li&gt;Day 15 · API Contract Layering and the Type Registry&lt;/li&gt;
&lt;li&gt;Day 16 · JSON-RPC 2.0: Protocol Choice and the Request Pipeline&lt;/li&gt;
&lt;li&gt;Day 17 · The Connector and the Convergence of Client Responsibilities&lt;/li&gt;
&lt;li&gt;Day 18 · The Error Contract and User Visible Messages&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;chapter-five-multi-tenancy-and-customization&quot;&gt;Chapter Five · Multi-Tenancy and Customization&lt;/h3&gt;
&lt;p&gt;How one codebase lets every company look different without branching and without recompiling or redeploying. Where the customization work currently stops is stated plainly too.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Day 19 · The Scope of the Customization Layer and Overlay Granularity&lt;/li&gt;
&lt;li&gt;Day 20 · Multilingual Customization and Resource Structure&lt;/li&gt;
&lt;li&gt;Day 21 · Business Logic Customization and the Four Plugin Stages&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;chapter-six-transport-security-and-audit&quot;&gt;Chapter Six · Transport, Security and Audit&lt;/h3&gt;
&lt;p&gt;What data looks like when it is sent, which protections it passes through, who is allowed to call, and what can be looked up afterward.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Day 22 · The Division of Labor Between XML, JSON and MessagePack&lt;/li&gt;
&lt;li&gt;Day 23 · The API Payload Security Pipeline: Order, Protection Levels and Keys&lt;/li&gt;
&lt;li&gt;Day 24 · The Three Axes of Permissions: Action, Row and Column&lt;/li&gt;
&lt;li&gt;Day 25 · The Audit Trail and the Before and After Values of a Change&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;chapter-seven-data-semantics&quot;&gt;Chapter Seven · Data Semantics&lt;/h3&gt;
&lt;p&gt;Amounts, quantities, dates and times of day: how the meaning of these values is kept from the definition all the way to the database, without any layer quietly changing it along the way.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Day 26 · Numeric Semantics, Multiple Currencies and Units of Measure&lt;/li&gt;
&lt;li&gt;Day 27 · The Semantic Boundaries Between Date, DateTime and Time&lt;/li&gt;
&lt;li&gt;Day 28 · Time Zone Conversion of DateTime&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;chapter-eight-case-review-and-the-big-picture&quot;&gt;Chapter Eight · Case Review and The Big Picture&lt;/h3&gt;
&lt;p&gt;The Northwind application examined item by item: which part definitions, the framework and application code each own. Then a single big picture, and the systems this design does not suit.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Day 29 · Which Part Definitions, the Framework and Application Code Each Own&lt;/li&gt;
&lt;li&gt;Day 30 · Three Depths of Development and the Big Picture of the Framework&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id=&quot;about-the-framework-behind-this-series&quot;&gt;About the framework behind this series&lt;/h3&gt;
&lt;p&gt;The mechanisms discussed in the series are all built into an open source framework, &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/jeff377/bee-library&quot;&gt;bee-library&lt;/a&gt;. The case study is a separate repository, &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/jeff377/bee-northwind-avalonia&quot;&gt;bee-northwind-avalonia&lt;/a&gt;, which pulls the framework in as a package and holds only the application's own definition files and code.&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Update: Bee.NET (bee-library), the framework used when this series was written, has since been taken over by &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/polhem-dev/polhem&quot;&gt;Polhem&lt;/a&gt;. The mechanisms carry over, the framework has been substantially refactored, and it is open source under the MIT license. The bee-library and bee-northwind-avalonia repositories are archived; type names and code in the posts follow bee-library as it was at the time of writing.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The framework's only role in this series is to show that these ideas are not just talk on paper. You can skip it entirely, take away only the idea of letting definitions be the source, and build that idea in the languages and tools you know.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;📘 Original index on HackMD, in Traditional Chinese:&lt;br&gt;
👉 &lt;a rel=&quot;nofollow&quot; href=&quot;https://hackmd.io/@jeff377/ironman-2026&quot;&gt;https://hackmd.io/@jeff377/ironman-2026&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;📢 You are welcome to republish this, please credit the source.&lt;br&gt;
📬 Follow for more engineering notes:&lt;br&gt;
&lt;a rel=&quot;nofollow&quot; href=&quot;https://www.facebook.com/profile.php?id=61574839666569&quot;&gt;Facebook&lt;/a&gt; ｜ &lt;a rel=&quot;nofollow&quot; href=&quot;https://hackmd.io/@jeff377&quot;&gt;HackMD&lt;/a&gt; ｜ &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/jeff377&quot;&gt;GitHub&lt;/a&gt; ｜ &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.nuget.org/profiles/jeff377&quot;&gt;NuGet&lt;/a&gt;&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/33029/erp-architects-notes-designing-a-definition-driven-framework-series-index</guid>
<pubDate>Fri, 30 Oct 2026 01:23:04 +0000</pubDate>
</item>
<item>
<title>Day 1: Why ERP Needs a Definition-Driven Framework</title>
<link>https://coderlegion.com/33030/day-1-why-erp-needs-a-definition-driven-framework</link>
<description>&lt;p&gt;This is the first post in a thirty-part series, ERP Architect's Notes: Designing a Definition-Driven Framework.&lt;/p&gt;
&lt;p&gt;What follows is not about building one form with some framework. It is about designing a framework that can drive hundreds of forms: which decisions it should make on your behalf, which ones it should leave to you, and why each mechanism ended up shaped the way it is. Every claim is checked against a working application built on the Northwind sample database, but the case study is evidence, not the subject.&lt;/p&gt;
&lt;p&gt;This post covers:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The three layers of ERP development cost: a single change, scale, and delivering customization&lt;/li&gt;
&lt;li&gt;The three long-standing problems those layers converge on, and why tooling does not remove them&lt;/li&gt;
&lt;li&gt;The point where the paradigms diverge: is the single source of truth code, or definitions?&lt;/li&gt;
&lt;li&gt;A controlled experiment: how many places does one field touch under each paradigm?&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;who-i-am&quot;&gt;Who I am&lt;/h3&gt;
&lt;p&gt;I have spent more than twenty years as a software architect in packaged ERP and HRM products. Packaged software has a very long product cycle, ten or twenty years is normal, and over that span the system is not merely maintained. It has to grow a new face every time the front-end generation turns over. I have been through three of them: desktop, web, and mobile apps. Each time the question was the same. Do we rebuild all several hundred screens?&lt;/p&gt;
&lt;p&gt;On a product line like that, an architectural decision does not get rewritten by someone else three years later. You watch it grow for ten years and then keep maintaining it. I stayed in the same role the whole time. I never moved into managing projects or teams and left the code behind, so I did not miss the consequences of a single one of those decisions.&lt;/p&gt;
&lt;h3 id=&quot;about-the-framework-and-about-this-series&quot;&gt;About the framework, and about this series&lt;/h3&gt;
&lt;p&gt;Some of the ideas I accumulated over those years were built inside real projects. More of them were only ever thought through, because writing all of it alone was never worth the cost. That cost is different now. I used Claude Code to build the ones I never got to, and the result is an open source framework, &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/jeff377/bee-library&quot;&gt;bee-library&lt;/a&gt;. The Northwind case study in later posts runs on it.&lt;/p&gt;
&lt;p&gt;The case study itself lives in a separate repository, &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/jeff377/bee-northwind-avalonia&quot;&gt;bee-northwind-avalonia&lt;/a&gt;. The framework repository holds the mechanisms; the case study repository holds only the application's own definition files and code, with the framework pulled in as a package. If you want to know what applying this framework actually requires, which definition files you prepare and what code you still have to write, the case study repository shows it more clearly than the framework does.&lt;/p&gt;
&lt;p&gt;The framework is still moving. Things that deserve to become mechanisms will keep being folded in, interfaces will change with them, and breaking changes will happen. If you want to reference the packages directly today, that has to be part of your calculation. You can also clone the whole thing and shape it into what you need.&lt;/p&gt;
&lt;p&gt;This series is not a manual for it. The framework has exactly one job here: to show that these ideas are not just talk. What the thirty posts discuss is the mechanisms that already exist, what problem each one ran into, how it was solved, what it cost, and why that option rather than another. Implementation details are not walked through line by line. You are entirely free not to use it, and to take only the idea that definitions should be the source, and build it in whatever language and tools you already know.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;The whole series rests on one sentence:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;The complexity of an ERP is not in how hard any single feature is. It is in how many times the same fact gets restated, multiplied by how many tables, multiplied again by how many customizations.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h3 id=&quot;1-the-cost-of-a-single-change-how-many-places-one-field-lands-in&quot;&gt;1. The cost of a single change: how many places one field lands in&lt;/h3&gt;
&lt;p&gt;The requirement: orders need one more field, Required Date. No calculation, no dependent behavior, no effect on existing logic. The user says this is &quot;just one field, it should be easy,&quot; and the user is right.&lt;/p&gt;
&lt;p&gt;In a typical Code-First stack, ORM plus web API plus front end, that field lands in thirteen places:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;# &lt;/th&gt;&lt;th&gt;Location &lt;/th&gt;&lt;th&gt;What changes&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;1 &lt;/td&gt;&lt;td&gt;Entity class &lt;/td&gt;&lt;td&gt;Add a property&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;2 &lt;/td&gt;&lt;td&gt;Type mapping configuration &lt;/td&gt;&lt;td&gt;Precision, nullability, column name mapping&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;3 &lt;/td&gt;&lt;td&gt;Migration &lt;/td&gt;&lt;td&gt;Generate it, review it, commit it, run it at deployment&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;4 &lt;/td&gt;&lt;td&gt;Query projections &lt;/td&gt;&lt;td&gt;The &lt;code&gt;Select&lt;/code&gt; for list queries, the column list for reports&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;5 &lt;/td&gt;&lt;td&gt;DTOs &lt;/td&gt;&lt;td&gt;Once for the request, once for the response&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;6 &lt;/td&gt;&lt;td&gt;Mapper configuration &lt;/td&gt;&lt;td&gt;Entity to DTO&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;7 &lt;/td&gt;&lt;td&gt;Validation &lt;/td&gt;&lt;td&gt;Required, range, ordering relative to the order date&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;8 &lt;/td&gt;&lt;td&gt;API contract &lt;/td&gt;&lt;td&gt;The OpenAPI schema, republished to consumers&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;9 &lt;/td&gt;&lt;td&gt;Front-end types &lt;/td&gt;&lt;td&gt;A TypeScript interface or a C# model&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;10 &lt;/td&gt;&lt;td&gt;Front-end form &lt;/td&gt;&lt;td&gt;Control, label, position in the layout&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;11 &lt;/td&gt;&lt;td&gt;Front-end list &lt;/td&gt;&lt;td&gt;Column, width, sorting&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;12 &lt;/td&gt;&lt;td&gt;Localization resources &lt;/td&gt;&lt;td&gt;One per language&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;13 &lt;/td&gt;&lt;td&gt;Test data &lt;/td&gt;&lt;td&gt;Fixtures, seeds, fake data generators&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;To be fair, several of those can be generated. Migrations come from a tool, OpenAPI is produced by scanning assemblies, front-end types can be generated from OpenAPI. The number you actually hand write is closer to five or six.&lt;/p&gt;
&lt;p&gt;But the number you hand write is not the point:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;The single fact that &quot;Required Date is a date, it is required, and it shows after the order date&quot; has been restated thirteen times in this stack.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Every restatement is a place where the copies can disagree:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Forgot the DTO, and the value reaches the database but never the front end&lt;/li&gt;
&lt;li&gt;Forgot the validation, and users submit it empty&lt;/li&gt;
&lt;li&gt;Forgot the list column, and users ask why they cannot see it on the query screen&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;None of these are hard problems. They are counting problems, and each inconsistency has to be found by a test or by a user report.&lt;/p&gt;
&lt;h4 id=&quot;a-team-boundary-runs-straight-through-that-table&quot;&gt;A team boundary runs straight through that table&lt;/h4&gt;
&lt;p&gt;Items 1 through 8 are back end. Items 9 through 13 are front end. If those are two teams, the line cuts through the middle of the table, and every item that crosses it changes character. It stops being editing and becomes coordination.&lt;/p&gt;
&lt;p&gt;A request to add one field then grows into this:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A meeting to agree the contract first, then separate scheduling on each side&lt;/li&gt;
&lt;li&gt;Sequencing: until the back end ships there is nothing for the front end to consume, so starting early means hand rolling a mock&lt;/li&gt;
&lt;li&gt;Release windows: whichever side goes first has to cope with the other not having caught up&lt;/li&gt;
&lt;li&gt;And during integration, the discovery that one side read &quot;required&quot; as a front end block and the other read it as a back end block&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Not one of those activities is writing the field. What they consume is not effort, it is waiting. The user counts the time from asking to shipping; the engineer counts the time actually spent working. So one side asks why changing a single field takes this long, and the other side does not feel it was slacking. Both are right, because the cost is real, it just does not land on the part where anyone is typing.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;2-the-cost-of-scale-multiply-that-table-by-a-thousand&quot;&gt;2. The cost of scale: multiply that table by a thousand&lt;/h3&gt;
&lt;p&gt;A full packaged ERP covers finance, purchasing, inventory, manufacturing, sales and HR. Business tables run into the thousands.&lt;/p&gt;
&lt;p&gt;The founding premise of an ORM is that objects map to tables, so:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;thousands of business tables
  -&amp;gt; thousands of entity classes
  -&amp;gt; plus DTOs, usually more than one: List / Detail / Create / Update
  -&amp;gt; plus mapper configuration
  -&amp;gt; plus repositories
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The overwhelming majority of those classes resemble each other. The entity for &lt;code&gt;Supplier&lt;/code&gt; and the entity for &lt;code&gt;Shipper&lt;/code&gt; differ in field names and field count; structurally they came from the same mold.&lt;/p&gt;
&lt;p&gt;This is not an argument that ORMs are bad. An ORM works well in the setting it was designed for. The problem is quantity. When the same pattern has to be copied a few thousand times, the copying itself becomes the dominant cost.&lt;/p&gt;
&lt;p&gt;Relationships then multiply that quantity again:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;ERP tables are densely connected by nature. An order points at a customer, a salesperson and a shipper; a shipment points at an order, a warehouse and a lot number.&lt;/li&gt;
&lt;li&gt;Every one of those relationships needs someone to decide how it is expressed, how it is loaded, and who maintains it. Each decision is reasonable on its own. Spread a few thousand tables out at once and nobody can hold the whole picture.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Take the thirteen slots from the first section and multiply them by a few thousand, and multiply the team boundary along with them. Cross team coordination is not paid once per table, it is paid once per request, and the number of requests is itself proportional to the number of tables.&lt;/p&gt;
&lt;p&gt;This is not inconvenience. It is loss of control. Anyone who has maintained a large system recognizes the symptoms:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Nobody dares touch shared classes, because nobody is sure who depends on them&lt;/li&gt;
&lt;li&gt;New joiners take a long time before they will go near core modules&lt;/li&gt;
&lt;li&gt;The same concept has several different spellings across modules, because different people grew them at different times&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id=&quot;3-the-cost-of-customization-how-do-you-deliver-it-once-it-is-done&quot;&gt;3. The cost of customization: how do you deliver it once it is done&lt;/h3&gt;
&lt;p&gt;The first two layers are about how much work a change takes. The third one is more fundamental, and there is no way around it, because:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;An ERP that gets installed and simply used essentially does not exist.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Every rollout comes with customization. This client numbers documents differently, that one calculates cost differently, this department wants two extra fields, that report needs a different grouping. The only variable is the path the customization travels down.&lt;/p&gt;
&lt;p&gt;Under Code-First, all thirteen of those places are code, and code has exactly one delivery path:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;edit -&amp;gt; compile -&amp;gt; test -&amp;gt; package -&amp;gt; deploy
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The same path hurts in three different ways depending on the deployment shape:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;Deployment shape &lt;/th&gt;&lt;th&gt;Where customization hurts&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;A single organization's internal system &lt;/td&gt;&lt;td&gt;The simplest case: schedule one maintenance window. But changing a decimal scale from 2 to 4 still means a full build, test and deploy&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Packaged software sold to many customers, each with their own deployment &lt;/td&gt;&lt;td&gt;Only two options. Put it in the trunk behind a switch, and the switches accumulate until nobody dares delete any of them. Or branch, and every branch evolves on its own while every fix in the trunk has to be carried across one at a time&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Multi-tenant SaaS on shared assemblies &lt;/td&gt;&lt;td&gt;Tenant A changes one formula, and that means coordinating a maintenance window across all tenants and regression testing every tenant's functionality. If any one tenant breaks, the whole batch rolls back&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Three shapes, one conclusion:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;The size of the requirement and the cost of delivering it have come completely apart.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Whether the requirement is changing a decimal scale from 2 to 4 or reworking an entire module, it travels the same delivery path and carries the same deployment risk. The result is that small requirements have to be scheduled into the release rhythm too, and the engineering team is forced to treat every change as a significant event.&lt;/p&gt;
&lt;p&gt;The root of that decoupling is that customization was expressed as code. The deployment shape only decides how much it hurts. It does not decide whether it hurts.&lt;/p&gt;
&lt;p&gt;This layer is what definition-driven design is actually for:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Definitions are data, not code. Changing data requires neither recompilation nor redeployment of assemblies.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h3 id=&quot;4-three-problems-that-never-got-solved&quot;&gt;4. Three problems that never got solved&lt;/h3&gt;
&lt;p&gt;The costs from the first three sections converge into these:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;Long-standing problem &lt;/th&gt;&lt;th&gt;Symptom&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;Specifications scattered across three layers &lt;/td&gt;&lt;td&gt;The same field is defined once in the UI, once in the DTO and once in a database migration, and they drift apart easily&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Business logic scattered &lt;/td&gt;&lt;td&gt;Different modules are maintained by different engineers, styles diverge, and the same work gets done more than once&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Customization that resists standardization &lt;/td&gt;&lt;td&gt;Custom logic cannot flow back into the standard product, and accumulates as technical debt nobody can govern&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;For the past twenty years the usual answer has been more tooling: code generators, project templates, smarter mappers, more automatic migrations. Those tools genuinely made each restatement cheaper.&lt;/p&gt;
&lt;p&gt;What they did not change is the structure in which one fact gets restated thirteen times. They only made restating it faster.&lt;/p&gt;
&lt;p&gt;For the third layer, customization cost, tooling helps even less. Generated code is still code, and still has to be compiled and deployed. The reason the third problem has been open for twenty years is right here.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;5-where-the-paradigms-diverge&quot;&gt;5. Where the paradigms diverge&lt;/h3&gt;
&lt;p&gt;The problem is not the tooling. It is the paradigm. There is only one question to answer:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Is the single source of truth in this system the code, or the definitions?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;Code-First: the entity class is the source. The database is derived from it through migrations, the API contract is derived from it through OpenAPI, and front-end types are derived from that. Definition files are a by-product.&lt;/li&gt;
&lt;li&gt;Definition-Driven: a structural description is the source. The UI, the database, the CRUD SQL and the validation rules are all derived from it. Code appears only where a real business judgment has to be made.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The difference is not that a file extension changed from &lt;code&gt;.cs&lt;/code&gt; to &lt;code&gt;.xml&lt;/code&gt;. It is the nature of the source:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;&amp;nbsp; &lt;/th&gt;&lt;th&gt;Code is the source &lt;/th&gt;&lt;th&gt;Definitions are the source&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;How a thousand tables are represented &lt;/td&gt;&lt;td&gt;A thousand classes &lt;/td&gt;&lt;td&gt;A thousand definition records&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;What you can bring to bear on them &lt;/td&gt;&lt;td&gt;Compilers, IDEs, refactoring tools &lt;/td&gt;&lt;td&gt;All of that, plus batch scripts, generators, programmatic checks, and AI&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;The delivery path for a change &lt;/td&gt;&lt;td&gt;Compile, package, deploy &lt;/td&gt;&lt;td&gt;Swap in a different definition&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;How customization is represented &lt;/td&gt;&lt;td&gt;Branches or switches scattered through source &lt;/td&gt;&lt;td&gt;A layer of data stacked on top of the standard definition&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;How the front end learns which fields exist &lt;/td&gt;&lt;td&gt;A second, hand-written set of types kept in sync by contract and by people &lt;/td&gt;&lt;td&gt;Fetching the same definition at run time&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;What a new front-end generation costs &lt;/td&gt;&lt;td&gt;Rebuilding every screen &lt;/td&gt;&lt;td&gt;Writing one new rendering layer&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Who can change it &lt;/td&gt;&lt;td&gt;Developers &lt;/td&gt;&lt;td&gt;Developers, consultants, tools, and even users&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Two of those rows deserve more than a line:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A thousand classes and a thousand definition records are the same count, but managing them is not the same order of magnitude. A thousand structurally identical XML files can be swept by one script that checks whether every amount field has the right decimal scale. That takes minutes. Doing the equivalent check across a thousand classes is considerably harder.&lt;/li&gt;
&lt;li&gt;Generational cost is the one I feel most strongly. Front-end technology turns over roughly every ten years. If screens are written by hand one at a time, a generation change means rebuilding all of them. If screens grow out of definitions, a generation change means writing one new rendering layer. The more forms you have, the wider the gap.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That is also the reason layout deserves to be its own layer of definition. Structure is stable and follows the business; layout is volatile and follows the front-end generation. Once they are separated, what gets rewritten at generation change is the rendering layer, not the thousands of structural definitions.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;6-a-controlled-experiment-how-many-places-for-the-same-field&quot;&gt;6. A controlled experiment: how many places for the same field&lt;/h3&gt;
&lt;p&gt;Back to adding Required Date to orders. I counted it in this series' case study application, and the answer is three definition edits and zero lines of code.&lt;/p&gt;
&lt;p&gt;Table structure:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-xml&quot;&gt;&amp;lt;DbField FieldName=&quot;required_date&quot; Caption=&quot;Required Date&quot; DbType=&quot;Date&quot; /&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Form structure:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-xml&quot;&gt;&amp;lt;FormField FieldName=&quot;required_date&quot; Caption=&quot;Required Date&quot; DbType=&quot;Date&quot; /&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Layout:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-xml&quot;&gt;&amp;lt;LayoutField FieldName=&quot;required_date&quot; Caption=&quot;Required Date&quot; /&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;What follows automatically after that: the database column, the form control, the list column, the SQL for insert, update and delete, and transport across layers.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;No entity class to keep in sync, because the SQL is assembled from the definition at run time&lt;/li&gt;
&lt;li&gt;No DTO or mapper to keep in sync, because the data crossing layers carries its own structural description&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The count moves by one in either direction depending on the situation, but the count is not the point. The point is that all three places are definition data.&lt;/p&gt;
&lt;h4 id=&quot;what-happened-to-the-team-boundary&quot;&gt;What happened to the team boundary&lt;/h4&gt;
&lt;p&gt;The front end no longer declares the field a second time. It asks the back end for that definition at run time and renders accordingly:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;front end starts -&amp;gt; asks the back end for the definition -&amp;gt; grows controls, list columns and validation from it
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;The contract does not have to be negotiated, because the contract is that definition. There is no second copy that could disagree with it.&lt;/li&gt;
&lt;li&gt;There is nothing to wait for. The back end changes the definition, and the front end gets the new one the next time it asks.&lt;/li&gt;
&lt;li&gt;The coordination, scheduling, sequencing and release windows from the first section mostly just disappear.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;What is left is half of the first step: what this field is called in business terms, where it goes, and whether it is required. Somebody was always going to decide that. It is a business judgment, not communication overhead.&lt;/p&gt;
&lt;p&gt;The boundary has not vanished, though. It moved.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The front end still needs a rendering layer that knows how to read definitions, and somebody has to build it. The difference is that it gets built once, and after that every form and every field is free.&lt;/li&gt;
&lt;li&gt;The team boundary drops from &quot;every field&quot; down to &quot;the rendering layer,&quot; and the frequency of conversation changes from &quot;every request&quot; to &quot;whenever framework capability changes.&quot;&lt;/li&gt;
&lt;li&gt;Anything that genuinely cannot be declared as a definition still has to be discussed. A rule about when a document may not be shipped still needs someone to decide who blocks it, at which layer, and what the message says. No architecture escapes that. What definition-driven design can eliminate is structural restatement, not semantic negotiation.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;after-the-change-lands&quot;&gt;After the change lands&lt;/h4&gt;
&lt;p&gt;Two practical problems remain, and the framework has to absorb both:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The physical database does not have that column yet. The framework compares the definition against the actual structure and adds the column, so nobody hand writes a migration. A later post takes this apart.&lt;/li&gt;
&lt;li&gt;The system is running, so how does it know the definition changed? The node that edited the definition has to be able to notify the others so their cached copies are invalidated. &lt;a rel=&quot;nofollow&quot; href=&quot;https://medium.com/@jeff377/day-12-cross-node-invalidation-for-definition-and-data-caches-dbef9c7f4b82&quot;&gt;Cross-Node Invalidation for Definition and Data Caches&lt;/a&gt; covers that.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The point is one sentence: between changing a definition and having it take effect there is no compilation, no packaging and no assembly deployment.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;7-a-map-of-the-thirty-posts&quot;&gt;7. A map of the thirty posts&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;Chapter &lt;/th&gt;&lt;th&gt;Theme&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;One (posts 1 to 3) &lt;/td&gt;&lt;td&gt;Why Definition-Driven&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Two (posts 4 to 8) &lt;/td&gt;&lt;td&gt;Definition Layer&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Three (posts 9 to 12) &lt;/td&gt;&lt;td&gt;Data Access and Caching&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Four (posts 13 to 18) &lt;/td&gt;&lt;td&gt;Business Logic and API&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Five (posts 19 to 21) &lt;/td&gt;&lt;td&gt;Multi-Tenancy and Customization&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Six (posts 22 to 25) &lt;/td&gt;&lt;td&gt;Transport, Security and Audit&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Seven (posts 26 to 28) &lt;/td&gt;&lt;td&gt;Data Semantics&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Eight (posts 29 to 30) &lt;/td&gt;&lt;td&gt;Case Review and The Big Picture&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Every post is written roughly the same way. First what the problem requires you to weigh, then how the framework is designed and why it was designed that way, and where a case study can verify it, a closing section with the actual Northwind definitions.&lt;/p&gt;
&lt;p&gt;Two things to be clear about up front:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The subject is long term cost, not development speed. In the short term Code-First wins almost every time: mature tooling, a complete ecosystem, a fast start. The cost of definition-driven design is concentrated at the beginning, and the return does not show until year three or year five. If your system ships, closes, and gets rewritten wholesale three years later, this series is of limited value to you. It is written for people who know they will be maintaining something for a long time.&lt;/li&gt;
&lt;li&gt;Trade-offs and costs will be written as plainly as benefits. The value of a framework is not in what it claims to do. It is in knowing precisely what it does not do, and why. Highly dynamic UIs, dashboards, reports and anything that is not form-shaped will only hurt more if you force them into this model.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id=&quot;a-thread-to-pick-up-later&quot;&gt;A thread to pick up later&lt;/h3&gt;
&lt;p&gt;The next post, &lt;a rel=&quot;nofollow&quot; href=&quot;https://dev.to/jeff377/day-2-an-overview-of-definition-driven-architecture-31hi&quot;&gt;An Overview of Definition-Driven Architecture&lt;/a&gt;, is about which decisions a framework should make on its users' behalf. The one after that, &lt;a rel=&quot;nofollow&quot; href=&quot;https://dev.to/jeff377/day-3-the-northwind-case-study-eight-forms-and-a-custom-business-object-3i33&quot;&gt;The Northwind Case Study: Eight Forms and a Custom Business Object&lt;/a&gt;, brings the case study on stage.&lt;/p&gt;
&lt;p&gt;The case study builds a complete application out of Northwind: eight forms, master-detail, three cross-table relationships, validation, all of it. Exactly one of those forms needs code written for it. What is inside that one, and which places we deliberately chose not to express as definitions, gets reconciled item by item in &lt;a rel=&quot;nofollow&quot; href=&quot;https://medium.com/@jeff377/day-29-which-part-definitions-the-framework-and-application-code-each-own-6d3269aa8cf5&quot;&gt;Which Part Definitions, the Framework and Application Code Each Own&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The enemy of ERP is not complexity. It is restatement. And the reason tooling cannot eliminate restatement is that restatement is a product of the paradigm. As long as code is the source, the same fact is condemned to be said many times over.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;📚 Part of the thirty-post series &lt;a rel=&quot;nofollow&quot; href=&quot;https://coderlegion.com/33029/erp-architects-notes-designing-a-definition-driven-framework-series-index&quot;&gt;ERP Architect's Notes: Designing a Definition-Driven Framework&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;📘 Original article on HackMD, in Traditional Chinese:&lt;br&gt;
👉 &lt;a rel=&quot;nofollow&quot; href=&quot;https://hackmd.io/@jeff377/day-01-why-definition-driven&quot;&gt;https://hackmd.io/@jeff377/day-01-why-definition-driven&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;📢 You are welcome to republish this, please credit the source.&lt;br&gt;
📬 Follow for more engineering notes:&lt;br&gt;
&lt;a rel=&quot;nofollow&quot; href=&quot;https://www.facebook.com/profile.php?id=61574839666569&quot;&gt;Facebook&lt;/a&gt; ｜ &lt;a rel=&quot;nofollow&quot; href=&quot;https://hackmd.io/@jeff377&quot;&gt;HackMD&lt;/a&gt; ｜ &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/jeff377&quot;&gt;GitHub&lt;/a&gt; ｜ &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.nuget.org/profiles/jeff377&quot;&gt;NuGet&lt;/a&gt;&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/33030/day-1-why-erp-needs-a-definition-driven-framework</guid>
<pubDate>Fri, 30 Oct 2026 01:23:02 +0000</pubDate>
</item>
<item>
<title>Keeping a no-signup publishing tool useful</title>
<link>https://coderlegion.com/32837/keeping-a-no-signup-publishing-tool-useful</link>
<description>&lt;p&gt;A generated report is ready. It looks fine in your browser. Now someone else needs to read it, and sharing it has become a second task: create a repository, pick a host, set up a deployment, or send a file with instructions.&lt;/p&gt;
&lt;p&gt;I work on &lt;a rel=&quot;nofollow&quot; href=&quot;https://brewpage.app&quot;&gt;BrewPage&lt;/a&gt;, a free tool for publishing HTML, Markdown, small static sites and files through a shareable HTTPS link. Publishing doesn't require an account. You can use the website or an API, and there are integrations for AI agents.&lt;/p&gt;
&lt;p&gt;That makes the first interaction pleasantly short. Running the service brings a longer question: how do you keep that convenience useful when an open publishing tool also attracts spam?&lt;/p&gt;
&lt;p&gt;The tension shows up in ordinary product decisions. Someone sharing a small demo shouldn't have to become a hosting administrator. The person opening it still needs to be able to understand what they're looking at and report a problem. Both people matter, even though only one of them pressed Publish.&lt;/p&gt;
&lt;p&gt;I don't think a signup form settles that question. For this product, publishing without an account is part of the point. Adding a mandatory account would change the experience for every person who only wants to share one report.&lt;/p&gt;
&lt;p&gt;Take ownership. A link to read a page and a link to manage it serve different purposes. BrewPage returns an owner token and a management link for updating or deleting a publication. Those stay with the publisher. The reader gets the reading link. An optional account helps manage publications across devices, but it isn't a prerequisite for sharing one.&lt;/p&gt;
&lt;p&gt;That distinction deserves attention in the interface and the instructions. Consider an agent finishing a report: its answer should contain the link a colleague can open. The credentials that allow changes belong with the publisher. A successful upload alone doesn't tell you whether the handoff was clear.&lt;/p&gt;
&lt;p&gt;Visibility needs equally plain language. Public pages are listed in the gallery. An unlisted link can still be opened by anyone who has it; password protection is a separate choice. These are different ways to share something, and the wording should help people choose deliberately.&lt;/p&gt;
&lt;p&gt;There is also a less exciting detail: expiry. BrewPage is temporary hosting. Publications expire, including those managed through an account. That suits a report being reviewed or a demo being discussed, but the author should know the lifetime before sending the link. They should keep their source files too.&lt;/p&gt;
&lt;p&gt;For spam and harmful content, the public-facing part is straightforward: keep strengthening protection for users and publications, and make reporting a problem easy to find. BrewPage has a report-abuse form. That gives readers somewhere to go when they encounter something wrong. It doesn't justify promising that every link will be harmless.&lt;/p&gt;
&lt;p&gt;These details are easy to lose behind the appeal of instant publishing. They are also what I would look at when evaluating another tool like this: who can change the result, who can read it, how long it lasts, and where a reader can ask for help.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/32837/keeping-a-no-signup-publishing-tool-useful</guid>
<pubDate>Wed, 28 Oct 2026 12:32:15 +0000</pubDate>
</item>
<item>
<title>Should a Failed Upload Clear the Form?</title>
<link>https://coderlegion.com/32810/should-a-failed-upload-clear-the-form</link>
<description>&lt;p&gt;&lt;em&gt;Cover: original AI-generated concept illustration with a fictional interface, not a working product screenshot.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Imagine filling in a project name, description and photo captions, then having the upload fail. Should the whole form reset, or should only the failed step need attention?&lt;/p&gt;
&lt;p&gt;My starting point would be to keep completed fields, identify which file failed and explain whether the project itself was saved. “Upload failed” should not leave someone guessing whether they need to recreate everything.&lt;/p&gt;
&lt;p&gt;I would also distinguish &lt;strong&gt;retrying the same file&lt;/strong&gt; from &lt;strong&gt;choosing a replacement&lt;/strong&gt;. If the original request's outcome is unknown, the app should check its status before offering a retry that might create a duplicate.&lt;/p&gt;
&lt;p&gt;Keeping work has boundaries: a shared device may make persistent local drafts inappropriate, and sensitive content should not be retained indefinitely just for convenience.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Where would you draw the line between preserving a form in the current session and saving a recoverable draft?&lt;/strong&gt; What should the interface tell users before they leave?&lt;/p&gt;
</description>
<category>Discussions</category>
<guid isPermaLink="true">https://coderlegion.com/32810/should-a-failed-upload-clear-the-form</guid>
<pubDate>Wed, 28 Oct 2026 10:23:21 +0000</pubDate>
</item>
<item>
<title>What Does an Empty Dashboard Mean?</title>
<link>https://coderlegion.com/32662/what-does-an-empty-dashboard-mean</link>
<description>&lt;p&gt;&lt;em&gt;Original AI-generated concept illustration, not a working product screenshot.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;An empty dashboard can mean three different things: nobody has added a record, filters exclude the existing records, or the data request failed. Showing “No results” for all three leaves the user guessing.&lt;/p&gt;
&lt;p&gt;My default would be one short explanation and one relevant next step: &lt;strong&gt;create a record&lt;/strong&gt;, &lt;strong&gt;clear the filters&lt;/strong&gt;, or &lt;strong&gt;retry the request&lt;/strong&gt;. I would avoid a creation prompt when the app has not confirmed whether records already exist.&lt;/p&gt;
&lt;p&gt;There is a trade-off, though. Explaining every possible cause can make a simple screen noisy, and some users may not have permission to see why data is unavailable.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;What is the minimum context you would show in an empty state, and when would you offer more than one action?&lt;/strong&gt;&lt;/p&gt;
</description>
<category>Discussions</category>
<guid isPermaLink="true">https://coderlegion.com/32662/what-does-an-empty-dashboard-mean</guid>
<pubDate>Tue, 27 Oct 2026 10:47:59 +0000</pubDate>
</item>
<item>
<title>Protect Human Edits: Conditional Saves for AI-Assisted Listing Drafts</title>
<link>https://coderlegion.com/32661/protect-human-edits-conditional-saves-for-ai-assisted-listing-drafts</link>
<description>&lt;p&gt;&lt;em&gt;Original AI-generated concept illustration. Fictional interfaces and property details, not an actual product screenshot.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Imagine a fictional property-listing editor used by two colleagues. Mira and Dev open the same draft. Mira corrects “two bedrooms” to “three bedrooms” after checking the property details, advancing revision 7 to revision 8. Dev asks an AI assistant to improve the description, reviews its wording, and clicks Save.&lt;/p&gt;
&lt;p&gt;Dev’s browser still holds the old bedroom count. If the server accepts the entire draft unconditionally, a polished description can quietly erase Mira’s correction.&lt;/p&gt;
&lt;p&gt;The useful question at save time is: &lt;strong&gt;Is the version this draft was based on still current?&lt;/strong&gt; A conditional save makes that question part of the server’s write operation, rather than a warning the interface hopes everyone notices.&lt;/p&gt;
&lt;h3 id=&quot;keep-the-baseline-with-the-draft&quot;&gt;Keep the baseline with the draft&lt;/h3&gt;
&lt;p&gt;When the editor loads a listing, retain three things together: the saved content, its ETag, and a separate working copy. Start AI generation from that captured baseline. Do not fetch a newer tag later and attach it to output generated from older content.&lt;/p&gt;
&lt;p&gt;A server might return this illustrative header excerpt, not a complete HTTP message:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-http&quot;&gt;ETag: &quot;listing-42-r7&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The corresponding save includes:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-http&quot;&gt;If-Match: &quot;listing-42-r7&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;If-Match&lt;/code&gt; uses strong comparison against the selected representation. If the condition is false, the server must not perform the requested change; the usual response is &lt;code&gt;412 Precondition Failed&lt;/code&gt;. &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.rfc-editor.org/rfc/rfc9110.html#section-13.1.1&quot;&gt;RFC 9110, section 13.1.1&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Clients should treat tags as opaque values, preserving their quotes. A weak tag such as &lt;code&gt;W/&quot;listing-42-r7&quot;&lt;/code&gt; never satisfies this comparison. Removing &lt;code&gt;W/&lt;/code&gt; does not turn it into a trustworthy strong validator. Also, &lt;code&gt;If-Match: *&lt;/code&gt; tests existence, not whether the editor has the latest revision. &lt;a rel=&quot;nofollow&quot; href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/If-Match&quot;&gt;MDN’s If-Match reference&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;For this proposed design, use one stable JSON representation for editing. Issue a new revision whenever its saved representation changes, including changes made by imports or background jobs. If serialization or representation variants can change independently, account for them in validator generation too. A revision number is useful only when the server maintains that invariant.&lt;/p&gt;
&lt;h3 id=&quot;make-comparison-and-writing-one-operation&quot;&gt;Make comparison and writing one operation&lt;/h3&gt;
&lt;p&gt;A separate “check version” request cannot protect the later save. Another writer can commit in the gap. Even checking inside the save handler is insufficient if the database permits a competing update between comparison and persistence.&lt;/p&gt;
&lt;p&gt;Here is &lt;strong&gt;untested, framework-neutral pseudocode&lt;/strong&gt;, not a deployable handler. This endpoint’s contract requires an explicit revision-tag list rather than a wildcard; its parser enforces that contract and rejects malformed input. Weak tags remain weak and cannot match.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;save_listing(request):
    authorize_and_validate_request(request)
    if request.IfMatch is missing:
        return 428 with retry_instructions, CacheControl=&quot;no-store&quot;
    candidates = parse_revision_tag_list(request.IfMatch)

    begin_transaction()
    try:
        listing = select_listing_for_update(request.listing_id)
        if listing is absent:
            rollback()
            return 404

        current = strong_etag_for_edit_representation(listing)
        if not any_strong_match(candidates, current):
            rollback()
            return 412

        replace_saved_draft(listing, request.draft)
        advance_representation_revision(listing)
        persist(listing)
        saved = snapshot_edit_representation(listing)
        commit()
    catch error:
        rollback_if_active()
        raise error

    return 200 with body=saved  # no response validator shown
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The lock must cover reading the current version, comparing it, and writing the replacement. PostgreSQL row locks normally remain held until transaction end, providing one possible building block. &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.postgresql.org/docs/current/explicit-locking.html#LOCKING-ROWS&quot;&gt;PostgreSQL row-level locking documentation&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Every writer must preserve the same versioning rules. Keep model generation and human review outside the transaction; a database lock should not wait for someone to finish editing.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;428&lt;/code&gt; response is an optional API policy, not a universal requirement for every server. It means this server requires a conditional request. Explain how to fetch a baseline and resubmit; responses with this status must not be cached. &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.rfc-editor.org/rfc/rfc6585.html#section-3&quot;&gt;RFC 6585, section 3&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://coderlegion.com/?qa=blob&amp;amp;qa_blobid=5961175084833443390&quot; alt=&quot;AI-generated concept illustration of a person using a laptop: a stale two-bedroom draft based on revision seven conflicts with the saved three-bedroom revision eight, alongside atomic version-check outcomes.&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Original AI-generated concept illustration with fictional people, interface and property details. An atomic version check protects the newer edit; the article explains the already-applied exception for a lost response.&lt;/em&gt;&lt;/p&gt;
&lt;h3 id=&quot;make-a-conflict-recoverable&quot;&gt;Make a conflict recoverable&lt;/h3&gt;
&lt;p&gt;When Dev receives &lt;code&gt;412&lt;/code&gt;, leave the working copy intact. Fetch the latest listing into a separate buffer and present a three-way comparison:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Base:&lt;/strong&gt; the content Dev originally loaded.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Mine:&lt;/strong&gt; Dev’s reviewed AI-assisted draft.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Current:&lt;/strong&gt; the latest saved content, including Mira’s correction.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Compare each side against the base. Distinct field changes may be straightforward to combine, but rewritten paragraphs can overlap semantically even when their text differs. Highlight the bedroom discrepancy for review instead of asking another model to decide which factual claim wins.&lt;/p&gt;
&lt;p&gt;After reconciliation, associate the reviewed result with the newly fetched baseline and its tag. Save conditionally again. If another edit arrives during review, repeat the comparison without discarding anyone’s work. Avoid an automatic “retry with latest tag” button: it would authorize the stale replacement without resolving the conflict.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://coderlegion.com/?qa=blob&amp;amp;qa_blobid=13343393280133622079&quot; alt=&quot;AI-generated concept illustration of two collaborators reviewing three laptop panels: base revision seven and the AI draft show two bedrooms, while current revision eight shows three; the working draft is retained for explicit review.&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Original AI-generated concept illustration. Fictional collaborators compare the base, working draft and current saved version; not an actual product screenshot.&lt;/em&gt;&lt;/p&gt;
&lt;h3 id=&quot;treat-a-missing-response-as-uncertainty&quot;&gt;Treat a missing response as uncertainty&lt;/h3&gt;
&lt;p&gt;Suppose Dev’s save commits, but its response disappears. Retrying with the original tag can produce &lt;code&gt;412&lt;/code&gt; because the first save advanced the revision. That does &lt;strong&gt;not&lt;/strong&gt; prove the first attempt failed.&lt;/p&gt;
&lt;p&gt;HTTP permits a success response when the server can determine that the requested change was already applied. The simpler handler above deliberately does not implement that optional recovery path. &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.rfc-editor.org/rfc/rfc9110.html#section-13.1.1&quot;&gt;RFC 9110, section 13.1.1&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Retain the draft and fetch current state before deciding what happened. If duplicate-operation recognition is needed, design a durable operation record and its retention rules explicitly; do not guess from a timeout.&lt;/p&gt;
&lt;h3 id=&quot;six-tests-before-shipping&quot;&gt;Six tests before shipping&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Ordinary save:&lt;/strong&gt; a current tag succeeds, and a subsequent fetch reflects the saved draft and updated validator.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Stale editor:&lt;/strong&gt; Mira saves first; Dev’s stale request gets &lt;code&gt;412&lt;/code&gt; without overwriting her correction.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Concurrent saves:&lt;/strong&gt; release two changed drafts together using the same tag; only one replacement commits.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Validator contract:&lt;/strong&gt; missing tags receive the documented &lt;code&gt;428&lt;/code&gt;; weak tags cannot match; wildcards cannot bypass revision protection.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Conflict recovery:&lt;/strong&gt; preserve the working copy, compare all three versions, and reject another stale reconciliation if a third change intervenes.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Lost response:&lt;/strong&gt; commit a save, drop its response, and retry. Verify that the client reconciles uncertainty rather than claiming the original save failed.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;AI-assisted editing can make drafting faster. Conditional saves keep that speed from silently replacing a colleague’s more recent work.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/32661/protect-human-edits-conditional-saves-for-ai-assisted-listing-drafts</guid>
<pubDate>Tue, 27 Oct 2026 10:18:08 +0000</pubDate>
</item>
<item>
<title>When Should Delete Offer Undo?</title>
<link>https://coderlegion.com/32495/when-should-delete-offer-undo</link>
<description>&lt;p&gt;Undo is useful only when it genuinely restores what the user lost. A confirmation dialog is useful only when it explains the consequence—not just “Are you sure?”&lt;/p&gt;
&lt;p&gt;Consider a hypothetical media app where someone deletes a set of property photos. My starting point would be:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Reversible removal:&lt;/strong&gt; move files to trash, show a clear Undo action, and keep a discoverable recovery route after the toast disappears.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Permanent or bulk deletion:&lt;/strong&gt; show the affected names or count, what cannot be restored, and whether shared access will be affected before proceeding.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Partial failure:&lt;/strong&gt; report which files changed instead of claiming the entire operation succeeded.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The tricky case is shared assets: restoring a file may not reverse notifications or other effects that already happened.&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://www.w3.org/WAI/WCAG22/Understanding/error-prevention-legal-financial-data.html&quot;&gt;W3C’s error-prevention guidance&lt;/a&gt; provides a useful reference for certain important submissions and changes to user-controllable stored data: reversal, checking, or confirmation. It does not mean adding a confirmation dialog to every routine edit.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Where do you draw the line between Undo, confirmation, and both?&lt;/strong&gt; Would your answer change for one recoverable file versus a folder shared with a team?&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Cover: original AI-generated conceptual illustration. The interfaces and recovery period shown are fictional, not a product demonstration.&lt;/em&gt;&lt;/p&gt;
</description>
<category>Discussions</category>
<guid isPermaLink="true">https://coderlegion.com/32495/when-should-delete-offer-undo</guid>
<pubDate>Mon, 26 Oct 2026 10:29:03 +0000</pubDate>
</item>
<item>
<title>BPMN as Code: From a Text-Based Model to a Working Microservices Sagas</title>
<link>https://coderlegion.com/32482/bpmn-as-code-from-a-text-based-model-to-a-working-microservices-sagas</link>
<description>&lt;p&gt;&lt;strong&gt;Contents&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;#issues-with-conventional-bpmn-tools&quot;&gt;Issues With Conventional BPMN Tools&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;#discovering-mermaid-bpmn&quot;&gt;Discovering Mermaid-BPMN&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;#making-it-practical&quot;&gt;Making It Practical&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;#a-real-world-example-modeling-a-payment-saga&quot;&gt;A Real-World Example: Modeling a Payment Saga&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;#from-bpmn-source-to-an-executable-saga&quot;&gt;From BPMN Source to an Executable Saga&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;#conclusions&quot;&gt;Conclusions&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;I've used Documentation as Code for years when designing backends and entire applications. Most of my diagrams are written in PlantUML, with their source stored alongside other design documents. It suits me much better than drawing everything by hand.&lt;/p&gt;
&lt;p&gt;Then I had to model a microservices Saga for Camunda. The process involved parallel checks, deadlines, human decisions and compensation. I wanted to write BPMN the same way I wrote UML, but couldn't find a toolchain I was comfortable using. Eventually I found Mermaid-BPMN and built a workbench around it.&lt;/p&gt;
&lt;h3 id=&quot;issues-with-conventional-bpmn-tools&quot;&gt;Issues With Conventional BPMN Tools&lt;/h3&gt;
&lt;p&gt;I tried the obvious options. I find arranging shapes in draw.io or Camunda Modeler slow, particularly when a process keeps changing. I'd rather edit source and immediately see what the diagram looks like.&lt;/p&gt;
&lt;p&gt;BPMN XML is source too, technically. But it is cumbersome to write from scratch and not much better to edit by hand. I also looked at other BPMN DSLs. Their diagrams didn't look right to me.&lt;/p&gt;
&lt;p&gt;I wanted readable source and conventional BPMN diagrams, including horizontal pools and lanes. I also wanted a live editor on my own host and a CLI for generating SVGs from local files. Although my implementation used Camunda, I didn't want the modeling tools tied to one process engine.&lt;/p&gt;
&lt;p&gt;And the diagrams had to be readable by people besides developers: architects, product owners, business analysts and anyone reviewing the process.&lt;/p&gt;
&lt;h3 id=&quot;discovering-mermaid-bpmn&quot;&gt;Discovering Mermaid-BPMN&lt;/h3&gt;
&lt;p&gt;After I inspected a few other tools, I came across the great &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/derari/mermaid-bpmn&quot;&gt;derari/mermaid-bpmn&lt;/a&gt;, an external Mermaid plugin with its own BPMN DSL, rich and comprehensive. It supports vertical and horizontal BPMN directions, activities, pools and lanes, gateways, events and annotations. There was also a basic live-editor example.&lt;/p&gt;
&lt;p&gt;I liked the language and the diagrams it generated. The DSL, parser, renderer and original editor are all the original author's work. I started with that project and added what I needed for day-to-day use. The result is &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/84-mermaid-bpmn-workbench/workbench&quot;&gt;Mermaid BPMN Workbench&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;making-it-practical&quot;&gt;Making It Practical&lt;/h3&gt;
&lt;p&gt;First, I packaged the editor for deployment with Docker. It builds to static files served by Nginx, so I can run it on my own host without keeping the development server running. The &lt;a rel=&quot;nofollow&quot; href=&quot;https://hub.docker.com/repository/docker/valentineshidev/mermaid-bpmn-workbench-web-editor&quot;&gt;image is available on Docker Hub&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I then worked on the editor itself. With a large process diagram, I need to zoom in, move around and quickly return to the full view. So I added pan, zoom and fit controls. I added source-pane width presets and font-size controls, too. The source and these settings are saved in the browser and restored after a reload. There is also a clear-source button and an option to open the full SVG in a separate tab.&lt;/p&gt;
&lt;p&gt;One detail took a little care. Pan and zoom change the SVG displayed in the editor. Exporting that modified SVG could give you the part you were looking at rather than the complete diagram. I keep a clean snapshot for downloads and the Open SVG action.&lt;/p&gt;
&lt;p&gt;I also needed SVGs for documentation, without opening the browser editor each time. I built a &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.npmjs.com/package/mermaid-bpmn-cli&quot;&gt;Mermaid-BPMN CLI&lt;/a&gt; for that. It generates an svg with this one-liner.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;npx mermaid-bpmn-cli --browser /path/to/chrome payment-process.mmd
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It writes &lt;code&gt;payment-process.svg&lt;/code&gt; next to the source. Currently it requires Node.js 22.12 or later and an installed Chrome or Chromium executable; you have to pass the browser path explicitly. I have used the CLI in my project's documentation workflow since that. It simplifies the workflow a lot. Also can be used in automated CI workflows for Documentation-as-Code.&lt;/p&gt;
&lt;h3 id=&quot;a-real-world-example-modeling-a-payment-saga&quot;&gt;A Real-World Example: Modeling a Payment Saga&lt;/h3&gt;
&lt;p&gt;The process that prompted all this was a payment Saga (here is the link to the &lt;a rel=&quot;nofollow&quot; href=&quot;https://gist.github.com/WhereJuly/8dfcc0018efe0c9bbc5e01c807ab3889&quot;&gt;anonymized source&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;It starts by debiting the payer, then runs anti-fraud and compliance/restriction checks in parallel. The results can lead to approval, denial, manual review or suspicious-payment handling.&lt;/p&gt;
&lt;p&gt;The checks have a 30-second deadline. If they don't finish in time, the process refunds the payer. Manual review has a 20-minute deadline, with the same default outcome if nobody makes a decision. We chose these defaults because processing an erroneous payment would cost considerably more than rejecting a valid one. I put that reasoning in annotations next to the relevant paths.&lt;/p&gt;
&lt;details&gt;
&lt;summary&gt;The Diagram&lt;/summary&gt;

![The Diagram](./images/payment-saga.svg)
    
&lt;/details&gt;
&lt;p&gt;The transfer to the payee is marked as an irreversible pivot point. Earlier failures are handled by compensation: an automatic refund and a rollback of payment status. Suspicious payments also trigger a security notification in parallel with the refund, with the paths joining before the final outcome.&lt;/p&gt;
&lt;p&gt;This is the kind of process I wanted to keep in a text file: branches, timers, compensation, and the business reasons for some of the decisions. I can change the source, review the diff and regenerate the diagram.&lt;/p&gt;
&lt;h3 id=&quot;from-bpmn-source-to-an-executable-saga&quot;&gt;From BPMN Source to an Executable Saga&lt;/h3&gt;
&lt;p&gt;I also took the Saga through implementation. The microservices were deployed, the Saga coordinated them, and I tested the cases in the implemented process.&lt;/p&gt;
&lt;p&gt;For the engine, I needed BPMN XML. I had an AI agent translate the DSL, reviewed the result myself, manually validated it and used Camunda Modeler for static debugging before deployment. I was glad to have the Modeler for that part, even though I didn't want to use it for writing the original model.&lt;/p&gt;
&lt;p&gt;This translation step isn't a Workbench feature. The workbench gives me the source editor and SVG generation; translation, engine validation, deployment and testing are steps in my project. I checked that the executable process preserved the deadlines, branches and compensation behavior in the model.&lt;/p&gt;
&lt;h3 id=&quot;conclusions&quot;&gt;Conclusions&lt;/h3&gt;
&lt;p&gt;I believe, this tooling is useful for any modeler - architects, developers, business analyst and product owners - all who has to model and align the business processes with the stakeholders conveniently and quickly.&lt;/p&gt;
&lt;p&gt;You can &lt;a rel=&quot;nofollow&quot; href=&quot;https://bpmn.valentineshi.dev/8e32d454e/&quot;&gt;try my hosted editor&lt;/a&gt;. The &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/84-mermaid-bpmn-workbench/workbench&quot;&gt;Workbench repository&lt;/a&gt; includes &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/84-mermaid-bpmn-workbench/workbench/tree/master/web-editor/examples&quot;&gt;deployment instructions&lt;/a&gt;, and the &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.npmjs.com/package/mermaid-bpmn-cli&quot;&gt;CLI is on npm&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Also, it were not be possible without the Mermaid-BPMN &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/derari/mermaid-bpmn&quot;&gt;implementation&lt;/a&gt; project by Derari.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/32482/bpmn-as-code-from-a-text-based-model-to-a-working-microservices-sagas</guid>
<pubDate>Mon, 26 Oct 2026 08:43:45 +0000</pubDate>
</item>
<item>
<title>Too Many AI Tools, Too Much Rework: Fixing the Filmmaking Process for Agencies</title>
<link>https://coderlegion.com/32399/too-many-ai-tools-too-much-rework-fixing-the-filmmaking-process-for-agencies</link>
<description>&lt;p&gt;AI video tools are getting better every month. A small creative team can now make product shots, short ads, character scenes and social media videos without arranging a full shoot every time.&lt;/p&gt;
&lt;p&gt;That sounds like good news for advertising agencies. And it is.&lt;/p&gt;
&lt;p&gt;But there is another side to it.&lt;/p&gt;
&lt;p&gt;One tool creates the image. Another turns it into a video. A third handles the voice. Someone downloads the files and opens an editing app. Then the client asks for a different version, and the team has to go back through half of the process again.&lt;/p&gt;
&lt;p&gt;The work was supposed to become easier. Somehow, the team now has more tabs open than before.&lt;/p&gt;
&lt;p&gt;This is a problem many agencies face when they start using AI for video production. The issue is not always the quality of the AI output. Often, it is the way all the tools fit together.&lt;/p&gt;
&lt;p&gt;The real challenge is not generating more clips. It is turning those clips into finished videos without doing the same work again and again.&lt;/p&gt;
&lt;h2 id=&quot;how-did-ai-filmmaking-become-so-complicated-&quot;&gt;How Did AI Filmmaking Become So Complicated?&lt;/h2&gt;
&lt;p&gt;At first, an agency may only need one AI video generator. The team tests a few prompts, creates some clips and shares them with the client.&lt;/p&gt;
&lt;p&gt;Soon, the requests become bigger.&lt;/p&gt;
&lt;p&gt;The client wants a product video with a person in it. The person needs to look the same in every scene. The product label must remain clear. The video needs a voiceover, subtitles and background music. The marketing team also wants vertical and horizontal versions.&lt;/p&gt;
&lt;p&gt;One tool may not handle all these tasks well, so the agency adds another one.&lt;/p&gt;
&lt;p&gt;Then another.&lt;/p&gt;
&lt;p&gt;Before long, the team is using different websites for images, video generation, voice, editing, resizing and other small jobs. Each tool may work well on its own, but the full process becomes difficult to manage.&lt;/p&gt;
&lt;p&gt;Files move from one place to another. Prompts get copied. References are uploaded again. Team members keep asking which version is the latest.&lt;/p&gt;
&lt;p&gt;This is where &lt;strong&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://www.lemonpeel.ai/ai-filmmaking-software&quot;&gt;AI filmmaking software&lt;/a&gt;&lt;/strong&gt; needs to be judged differently. A good tool should not only create an interesting clip. It should also help the team move from the first idea to the finished video.&lt;/p&gt;
&lt;h2 id=&quot;the-hidden-cost-of-using-too-many-tools&quot;&gt;The Hidden Cost of Using Too Many Tools&lt;/h2&gt;
&lt;p&gt;The cost of separate tools is not limited to monthly subscriptions.&lt;/p&gt;
&lt;p&gt;Think about the time people spend moving between them.&lt;/p&gt;
&lt;p&gt;An editor downloads a clip, changes its format, uploads it somewhere else and waits for the next result. A designer searches for the correct product image. A creative lead tries to find the prompt used for the earlier version. Then someone notices that the new clip does not match the old one.&lt;/p&gt;
&lt;p&gt;Each task may take only a few minutes.&lt;/p&gt;
&lt;p&gt;But when the same steps happen across dozens of videos, those minutes add up.&lt;/p&gt;
&lt;p&gt;There is also the cost of mistakes. A team might use an older logo, the wrong product image or an outdated script. A file may get saved in the wrong folder. A client may approve one version while the editor accidentally works on another.&lt;/p&gt;
&lt;p&gt;None of these problems looks serious on its own. Together, they can slow down an entire campaign.&lt;/p&gt;
&lt;p&gt;This is why agencies should look at the full production process before adding another AI tool. If a new tool solves one problem but creates three more steps, it may not be helping as much as expected.&lt;/p&gt;
&lt;h2 id=&quot;start-with-one-clear-production-workflow&quot;&gt;Start With One Clear Production Workflow&lt;/h2&gt;
&lt;p&gt;The first fix is not necessarily buying new software. It is deciding how work should move through the agency.&lt;/p&gt;
&lt;p&gt;A simple video workflow might look like this:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Brief → Script → Storyboard → References → Video generation → Editing → Sound → Review → Export&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The exact steps will depend on the project. A short product ad may need less planning than a film with several characters and scenes.&lt;/p&gt;
&lt;p&gt;Still, having a shared process gives everyone a clear starting point.&lt;/p&gt;
&lt;p&gt;The person writing the script knows what information is needed. The designer knows which product images to prepare. The video creator knows which scenes need to be generated. The editor knows where to find the approved clips.&lt;/p&gt;
&lt;p&gt;It also becomes easier to spot delays.&lt;/p&gt;
&lt;p&gt;If a project keeps getting stuck because the team does not have approved references, that is a planning problem. If the team spends hours making new formats, that may be a workflow problem. If the client keeps changing the main idea after production has started, the approval process needs attention.&lt;/p&gt;
&lt;p&gt;AI can help with production, but it cannot fix every problem caused by an unclear brief.&lt;/p&gt;
&lt;p&gt;A clear workflow gives the technology something useful to support.&lt;/p&gt;
&lt;h2 id=&quot;keep-brand-assets-and-references-in-one-place&quot;&gt;Keep Brand Assets and References in One Place&lt;/h2&gt;
&lt;p&gt;One common problem with AI-generated videos is consistency.&lt;/p&gt;
&lt;p&gt;A character looks right in the first scene but different in the next one. A product changes shape. A logo appears in the wrong place. The colours slowly move away from the brand's usual style.&lt;/p&gt;
&lt;p&gt;This becomes a bigger issue when the agency produces several videos for the same client.&lt;/p&gt;
&lt;p&gt;The solution starts with better references.&lt;/p&gt;
&lt;p&gt;Before generating a video, the team should know which product images, logos, colours, characters and visual examples are approved. Those references should be easy to find and reuse.&lt;/p&gt;
&lt;p&gt;It also helps to keep a short record of the creative decisions. Which character reference was used? Which product image was approved? What visual style did the client choose? Which version of the script is final?&lt;/p&gt;
&lt;p&gt;Without this information, team members may repeat work that has already been done.&lt;/p&gt;
&lt;p&gt;Some AI creative platforms now provide shared asset and brand features for this reason. Lemonpeel, for example, lists an Asset Reference System, Brand Kit, Team Controls and Character Consistency among its platform features. These are intended to help creative teams keep references and brand details available across their work.&lt;/p&gt;
&lt;p&gt;The important point is simple: &lt;strong&gt;do not make every new video start from zero when the same brand assets are already available.&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id=&quot;stop-treating-every-video-as-a-separate-project&quot;&gt;Stop Treating Every Video as a Separate Project&lt;/h2&gt;
&lt;p&gt;Imagine an agency making short product videos for an online clothing brand.&lt;/p&gt;
&lt;p&gt;The first video takes time because the team is still working out the process. They prepare the product images, decide on the visual style, create the scenes and finish the edit.&lt;/p&gt;
&lt;p&gt;The next video is for another product from the same brand.&lt;/p&gt;
&lt;p&gt;If the team starts from a blank page again, it will repeat much of the same work.&lt;/p&gt;
&lt;p&gt;Instead, the agency can keep a reusable workflow. The product changes, the script changes and some of the scenes change, but the basic production steps remain the same.&lt;/p&gt;
&lt;p&gt;This is where reusable workflows can make a difference.&lt;/p&gt;
&lt;p&gt;Lemonpeel's Flows library includes ready-made processes for tasks such as product campaigns, UGC-style videos, storyboards, multi-shot scenes and product-focused content. Teams can open a workflow and run it with their own inputs rather than building every process from scratch.&lt;/p&gt;
&lt;p&gt;A reusable workflow does not mean every video should look identical. It simply gives the team a starting point.&lt;/p&gt;
&lt;p&gt;The creative idea can still be new. The production process does not need to be new every time.&lt;/p&gt;
&lt;h2 id=&quot;use-storyboards-before-generating-lots-of-clips&quot;&gt;Use Storyboards Before Generating Lots of Clips&lt;/h2&gt;
&lt;p&gt;Another source of rework is creating videos before the team has agreed on what the video should contain.&lt;/p&gt;
&lt;p&gt;A creative person generates a few clips. The client sees them and says the story is wrong. The team changes the idea and starts generating again.&lt;/p&gt;
&lt;p&gt;This is expensive in terms of time, even when each clip is quick to create.&lt;/p&gt;
&lt;p&gt;A storyboard can help prevent this.&lt;/p&gt;
&lt;p&gt;For a simple product ad, the storyboard might contain four shots:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The product appears in a clear opening shot.&lt;/li&gt;
&lt;li&gt;A person picks it up or uses it.&lt;/li&gt;
&lt;li&gt;The video shows a closer view of an important feature.&lt;/li&gt;
&lt;li&gt;The final shot shows the product with the campaign message.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The client can review the direction before the team spends time generating all the shots.&lt;/p&gt;
&lt;p&gt;It is much easier to change a rough plan than to rebuild a finished video.&lt;/p&gt;
&lt;p&gt;AI tools can also help create storyboard images and turn those plans into scenes. Lemonpeel's Flows library includes a Prompt to Storyboard workflow, a Storyboard Creator and a Storyboard to Film workflow.&lt;/p&gt;
&lt;p&gt;These features do not remove the need for creative decisions. They give the team a way to make those decisions earlier.&lt;/p&gt;
&lt;p&gt;That is often where the biggest time savings begin.&lt;/p&gt;
&lt;h2 id=&quot;make-multi-shot-videos-easier-to-manage&quot;&gt;Make Multi-Shot Videos Easier to Manage&lt;/h2&gt;
&lt;p&gt;A single AI-generated clip can look impressive. A finished video, however, often needs several shots that feel like they belong together.&lt;/p&gt;
&lt;p&gt;Think of a short coffee ad. The first shot shows coffee beans falling into a grinder. The second shows the machine working. The third shows coffee pouring into a cup. The final shot shows the finished drink.&lt;/p&gt;
&lt;p&gt;If each shot is created separately without a plan, the lighting, product details or visual style may change. The editor then has to find ways to hide those differences.&lt;/p&gt;
&lt;p&gt;This is why shot planning and consistency matter.&lt;/p&gt;
&lt;p&gt;A multi-shot workflow can help the team build several connected shots around one idea. It also makes the intended order of the scenes clearer.&lt;/p&gt;
&lt;p&gt;Lemonpeel lists workflows such as Multi Shot Builder, Multi Shot Vertical and Multi Shot with Characters &amp;amp; Location. These are designed for creating short videos with several shots.&lt;/p&gt;
&lt;p&gt;Agencies should test this kind of workflow with an actual client brief. Check whether the shots work together, whether the product stays consistent and how much editing is still needed.&lt;/p&gt;
&lt;p&gt;The goal is not to remove editing. It is to reduce avoidable fixes before the editor even starts.&lt;/p&gt;
&lt;h2 id=&quot;plan-for-different-formats-from-the-beginning&quot;&gt;Plan for Different Formats From the Beginning&lt;/h2&gt;
&lt;p&gt;A video may look great on a desktop screen but fail when cropped for a phone.&lt;/p&gt;
&lt;p&gt;The product may move outside the frame. The text may become too small. A person who was clearly visible in the wide version may be cut off in the vertical version.&lt;/p&gt;
&lt;p&gt;Then the agency has to make changes and export the video again.&lt;/p&gt;
&lt;p&gt;This is a common source of rework because teams sometimes treat aspect ratios as a final step.&lt;/p&gt;
&lt;p&gt;A better approach is to decide the required formats during planning. If a campaign needs 9:16 for Reels and 16:9 for YouTube, the team should know that before generating every shot.&lt;/p&gt;
&lt;p&gt;Some scenes may need different framing. Text placement may need to change. In certain cases, separate versions may be better than a simple crop.&lt;/p&gt;
&lt;p&gt;Lemonpeel lists Reframe &amp;amp; Resize among its platform features, alongside workflows that allow different aspect ratios to be selected during creation.&lt;/p&gt;
&lt;p&gt;Still, the agency should check the actual results. Resizing a video does not automatically make every version suitable for every platform.&lt;/p&gt;
&lt;p&gt;The useful habit is to plan for the final places where the video will appear, instead of trying to fix every format at the end.&lt;/p&gt;
&lt;h2 id=&quot;choose-tools-based-on-the-work-your-agency-does&quot;&gt;Choose Tools Based on the Work Your Agency Does&lt;/h2&gt;
&lt;p&gt;Not every agency needs the same AI setup.&lt;/p&gt;
&lt;p&gt;An agency making simple social media ads may need quick product videos, captions and resizing. A team making story-led films may need character references, scene planning and more control over shots. An agency serving many brands may care more about shared assets, team access and repeatable workflows.&lt;/p&gt;
&lt;p&gt;So there is no need to choose a tool just because it has the longest feature list.&lt;/p&gt;
&lt;p&gt;Start with the tasks your team repeats most often.&lt;/p&gt;
&lt;p&gt;Where do people lose time? Which steps cause the most revisions? Which jobs require opening several different tools? What do clients ask for again and again?&lt;/p&gt;
&lt;p&gt;Then look for software that helps with those exact problems.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://www.lemonpeel.ai/ai-filmmaking-software&quot;&gt;AI filmmaking software for agencies&lt;/a&gt;&lt;/strong&gt; should be judged by how well it fits real agency work, not just by how good a sample video looks.&lt;/p&gt;
&lt;p&gt;It is also worth checking how the pricing works. A tool that looks affordable may become costly if every revision or generation uses more credits than expected. Test the full process before making a long-term decision.&lt;/p&gt;
&lt;h2 id=&quot;give-your-team-a-simple-way-to-review-and-approve-work&quot;&gt;Give Your Team a Simple Way to Review and Approve Work&lt;/h2&gt;
&lt;p&gt;A messy review process can waste as much time as a messy production process.&lt;/p&gt;
&lt;p&gt;The creative team may finish a video, but feedback comes from several people in different places. One person comments on the script. Another asks for a new opening. Someone else sends a message about the product colour.&lt;/p&gt;
&lt;p&gt;The editor then has to work out which feedback is final.&lt;/p&gt;
&lt;p&gt;A better process gives each project a clear review stage. The team should know who can approve the script, who checks brand details and who gives the final approval.&lt;/p&gt;
&lt;p&gt;It also helps to collect feedback in one place and keep track of the version being reviewed.&lt;/p&gt;
&lt;p&gt;This is not an AI feature. It is a team habit.&lt;/p&gt;
&lt;p&gt;But it makes AI production much easier to manage because faster generation can lead to more versions. Without a clear review process, the agency may end up creating more work rather than finishing work sooner.&lt;/p&gt;
&lt;h2 id=&quot;measure-the-time-saved-not-just-the-number-of-videos-made&quot;&gt;Measure the Time Saved, Not Just the Number of Videos Made&lt;/h2&gt;
&lt;p&gt;It is easy to feel productive when the team generates 30 clips in an afternoon.&lt;/p&gt;
&lt;p&gt;But how many of those clips became finished videos? How many needed major changes? How much time did the editor spend fixing them? How many versions did the client actually use?&lt;/p&gt;
&lt;p&gt;These are better questions to ask.&lt;/p&gt;
&lt;p&gt;An agency can track a few simple things: time from brief to approval, number of revision rounds, number of tools used for one project and the amount of manual work needed for each format.&lt;/p&gt;
&lt;p&gt;The team should also check the quality of the final output. A faster workflow is not useful if the brand looks inconsistent or the message is unclear.&lt;/p&gt;
&lt;p&gt;Over time, these numbers can show which parts of the process are working and which parts need attention.&lt;/p&gt;
&lt;p&gt;They can also help an agency decide whether it needs another tool or simply a better way to use the tools it already has.&lt;/p&gt;
&lt;h2 id=&quot;what-a-better-ai-filmmaking-process-looks-like&quot;&gt;What a Better AI Filmmaking Process Looks Like&lt;/h2&gt;
&lt;p&gt;A better process does not have to be complicated.&lt;/p&gt;
&lt;p&gt;The agency starts with a clear brief. It prepares the right references. It plans the main shots. It uses a repeatable workflow where possible. The team generates the footage, checks consistency, edits the video and prepares the required formats.&lt;/p&gt;
&lt;p&gt;Feedback is collected in a clear way. Approved assets and useful workflows are kept for the next project.&lt;/p&gt;
&lt;p&gt;The next video still needs creative thinking, but the team does not have to repeat every small task.&lt;/p&gt;
&lt;p&gt;This is the real value of &lt;strong&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://www.lemonpeel.ai/ai-filmmaking-software&quot;&gt;AI filmmaking software for agencies&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;It is not about making every step automatic. It is about letting technology handle useful parts of production while people focus on the story, the brand and the quality of the final work.&lt;/p&gt;
&lt;p&gt;AI tools will keep changing. New models will appear, and some current tools may become less useful over time. A clear process helps the agency adapt without rebuilding its entire way of working every few months.&lt;/p&gt;
&lt;h2 id=&quot;final-thoughts-fewer-disconnected-tools-less-repeated-work&quot;&gt;Final Thoughts: Fewer Disconnected Tools, Less Repeated Work&lt;/h2&gt;
&lt;p&gt;Using more AI tools does not always mean an agency will work faster.&lt;/p&gt;
&lt;p&gt;If each tool adds another upload, another download and another place to manage files, the team may end up with a more complicated process.&lt;/p&gt;
&lt;p&gt;The better goal is to connect the work.&lt;/p&gt;
&lt;p&gt;Start with a clear workflow. Keep brand references organised. Plan shots before generating them. Reuse processes where it makes sense. Think about aspect ratios early. Make client reviews easier to manage. Measure the time needed to deliver the final video.&lt;/p&gt;
&lt;p&gt;Then choose software that supports those habits.&lt;/p&gt;
&lt;p&gt;An agency does not need to use every new AI feature. It needs a process that helps its people deliver good work without repeating the same tasks on every project.&lt;/p&gt;
&lt;h3 id=&quot;want-to-bring-more-of-your-video-workflow-together-&quot;&gt;Want to Bring More of Your Video Workflow Together?&lt;/h3&gt;
&lt;p&gt;Lemonpeel is building an AI creative workspace for agencies, studios and brands. Its platform brings multiple AI models and creative tasks together, including scripts, hooks, shot lists, image creation, video generation, sound, editing, subtitles and resizing. It also offers Marketing Studio, Film Studio, reusable Flows, asset references, Brand Kit and team features.&lt;/p&gt;
&lt;p&gt;If your team is tired of switching between different tools just to finish one campaign, take a look at Lemonpeel and see whether its workflows fit the way your agency works.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Explore Lemonpeel:&lt;/strong&gt; &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.lemonpeel.ai&quot;&gt;https://www.lemonpeel.ai&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The aim is not to add one more tool to your list. It is to find a better way to turn creative ideas into finished videos, with less repeated work along the way.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/32399/too-many-ai-tools-too-much-rework-fixing-the-filmmaking-process-for-agencies</guid>
<pubDate>Sun, 25 Oct 2026 13:27:10 +0000</pubDate>
</item>
<item>
<title>When Is a Progress Bar Misleading?</title>
<link>https://coderlegion.com/32377/when-is-a-progress-bar-misleading</link>
<description>&lt;p&gt;A progress bar should tell users what has been measured, not just reassure them that something is happening.&lt;/p&gt;
&lt;p&gt;Consider a hypothetical photo-processing tool: upload progress is measurable in bytes, but the next step might wait in a queue or take a different amount of time for each image. A bar that reaches 99% and then stalls can turn a useful signal into false precision.&lt;/p&gt;
&lt;p&gt;My proposed default would be:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Use a percentage for work with a meaningful, known total.&lt;/li&gt;
&lt;li&gt;Use explicit stages such as &lt;strong&gt;Queued → Processing → Ready&lt;/strong&gt; when completion time is uncertain.&lt;/li&gt;
&lt;li&gt;Show completed-file counts for batches, while making clear that files may take different amounts of time.&lt;/li&gt;
&lt;li&gt;Keep failure and cancellation states separate from progress.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;There is an accessibility detail too: status updates should be available to assistive technology without moving keyboard focus. &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.w3.org/WAI/WCAG22/Understanding/status-messages.html&quot;&gt;W3C's guidance on status messages&lt;/a&gt; also warns against making updates too chatty; announcing every tick is not necessarily helpful.&lt;/p&gt;
&lt;p&gt;Where would you draw the line between a useful estimate and false precision? Would you show stage labels, an estimated range, or a percentage when processing time varies?&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Cover: original AI-generated concept illustration of fictional processing interfaces, not a product screenshot or performance result.&lt;/em&gt;&lt;/p&gt;
</description>
<category>Discussions</category>
<guid isPermaLink="true">https://coderlegion.com/32377/when-is-a-progress-bar-misleading</guid>
<pubDate>Sun, 25 Oct 2026 10:26:33 +0000</pubDate>
</item>
<item>
<title>When Should a Client Preview Link Expire?</title>
<link>https://coderlegion.com/32262/when-should-a-client-preview-link-expire</link>
<description>&lt;p&gt;&lt;em&gt;Cover: original AI-generated concept illustration, not a screenshot of a working product. The durations are discussion examples, not recommendations.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;A photographer shares an unpublished property-photo preview on Friday. The agent opens it on Monday, after the link has expired. A longer lifetime would reduce friction, but the link could also be forwarded.&lt;/p&gt;
&lt;p&gt;For a small client-review tool, which approach would you choose?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A short-lived direct link, with a clear way to request a new one.&lt;/li&gt;
&lt;li&gt;A longer review window with an explicit expiry notice.&lt;/li&gt;
&lt;li&gt;An authenticated review page that issues short-lived asset links after checking access.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;AWS documents that S3 presigned URLs act as bearer tokens and can expire earlier when their underlying credentials expire: &lt;a rel=&quot;nofollow&quot; href=&quot;https://docs.aws.amazon.com/AmazonS3/latest/userguide/using-presigned-url.html&quot;&gt;official S3 guidance&lt;/a&gt;. That distinction matters when the UI promises a review window. Expiry also cannot recall a file someone has already downloaded.&lt;/p&gt;
&lt;p&gt;What should the recipient see after expiry, and how would you make recovery easy without silently extending access? I would favour a clear expiry message and an explicit renewal path, but I am interested in the trade-offs for clients who do not want another account.&lt;/p&gt;
&lt;p&gt;This is a hypothetical design question, not a claim about any product’s current implementation.&lt;/p&gt;
</description>
<category>Discussions</category>
<guid isPermaLink="true">https://coderlegion.com/32262/when-should-a-client-preview-link-expire</guid>
<pubDate>Sat, 24 Oct 2026 10:39:25 +0000</pubDate>
</item>
<item>
<title>Should a Resized Image Export Need a Fresh Visual Review?</title>
<link>https://coderlegion.com/32193/should-a-resized-image-export-need-a-fresh-visual-review</link>
<description>&lt;p&gt;&lt;em&gt;Cover: an original AI-generated illustration of a hypothetical review/export workflow, not an actual product result.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;A reviewer approves revision R-2 of an AI-edited property photo. The export step then creates a smaller JPEG for delivery.&lt;/p&gt;
&lt;p&gt;Where should the review boundary sit?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Allow predetermined resizing/compression after approval, with a separate export record.&lt;/li&gt;
&lt;li&gt;Require a final visual check of every delivered file.&lt;/li&gt;
&lt;li&gt;Use both, with a stricter check when the transform changes the crop, label or visible content.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The distinction I would test is whether the derivative merely changes encoding or changes what the client sees. Even a routine export can make a disclosure label unreadable.&lt;/p&gt;
&lt;p&gt;A file digest can identify the exact exported bytes, but it does not establish visual correctness. MDN explains the narrow role of a digest here: &lt;a rel=&quot;nofollow&quot; href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/digest&quot;&gt;https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/digest&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;What rule has worked in your image or document pipeline, and which edge case made you change it?&lt;/p&gt;
&lt;p&gt;Context: I work on EstateAI content and SEO. This is a hypothetical workflow question, not a claim about its current implementation.&lt;/p&gt;
</description>
<category>Discussions</category>
<guid isPermaLink="true">https://coderlegion.com/32193/should-a-resized-image-export-need-a-fresh-visual-review</guid>
<pubDate>Fri, 23 Oct 2026 13:03:59 +0000</pubDate>
</item>
<item>
<title>Keep the Source: Provenance Records for AI Property Photos</title>
<link>https://coderlegion.com/32192/keep-the-source-provenance-records-for-ai-property-photos</link>
<description>&lt;p&gt;&lt;em&gt;Cover: an original AI-generated editorial concept showing fictional room-photo revisions. It is not an EstateAI result or a real property's before-and-after comparison.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;A property photographer delivers a staged image. A week later, someone asks a simple question: which original photograph produced this version?&lt;/p&gt;
&lt;p&gt;The answer should be easier to find than a message thread called “final images.” A useful application can connect the source file, each revision, the review decision and the exact export that left the system. This record matters even when the team never uses a formal content-provenance standard.&lt;/p&gt;
&lt;p&gt;The following is a proposed design for a fictional property-photo application. It is not a claim about how EstateAI or another provider currently stores its files.&lt;/p&gt;
&lt;h3 id=&quot;start-with-four-separate-questions&quot;&gt;Start with four separate questions&lt;/h3&gt;
&lt;p&gt;Before choosing a database schema, separate the questions people actually ask:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Identity:&lt;/strong&gt; Which file is this?&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;History:&lt;/strong&gt; What inputs and operations produced it?&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Review:&lt;/strong&gt; Who approved this particular revision, and for what use?&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Permission:&lt;/strong&gt; Is the requested processing or publication covered by the recorded permission?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;One “verified” boolean cannot answer all four. A checksum can help identify bytes, but it does not establish copyright ownership. A reviewer can approve the furniture placement while the team still lacks permission to publish the photograph in a public portfolio. A readable edit log can also contain an incorrect description.&lt;/p&gt;
&lt;p&gt;Keeping these distinctions visible makes the product easier to reason about and gives support teams something specific to investigate.&lt;/p&gt;
&lt;h3 id=&quot;preserve-the-source-as-a-separate-asset&quot;&gt;Preserve the source as a separate asset&lt;/h3&gt;
&lt;p&gt;Store the original upload as its own asset with a stable internal ID. Create new records for derivatives instead of replacing that source whenever a preview is regenerated.&lt;/p&gt;
&lt;p&gt;A minimal source record could contain:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;An opaque source ID and the relevant workspace ID&lt;/li&gt;
&lt;li&gt;An internal storage reference&lt;/li&gt;
&lt;li&gt;A digest of the received file bytes&lt;/li&gt;
&lt;li&gt;Media type, dimensions and ingestion time&lt;/li&gt;
&lt;li&gt;A reference to the applicable processing-permission record&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Treat the storage URL as a delivery mechanism rather than the asset's identity. URLs and access tokens can change; the relationship between a source and its derivatives should remain understandable.&lt;/p&gt;
&lt;p&gt;For a browser-side preflight, the Web Crypto API supports SHA-256 through &lt;code&gt;SubtleCrypto.digest()&lt;/code&gt;. The &lt;a rel=&quot;nofollow&quot; href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/digest&quot;&gt;MDN documentation&lt;/a&gt; also notes that this method reads the entire input into memory rather than supporting streaming. Set sensible upload limits, and calculate the authoritative digest again on the server over the bytes actually stored.&lt;/p&gt;
&lt;p&gt;A digest comparison has a narrow purpose. Re-encoding an image can change its file bytes even when it looks similar, so record the new export separately. Do not describe a matching hash as proof that the photographed scene is accurate.&lt;/p&gt;
&lt;h3 id=&quot;make-every-revision-explain-its-parent&quot;&gt;Make every revision explain its parent&lt;/h3&gt;
&lt;p&gt;A revision should point to the source or earlier revision it used. Record the operation and the tool information you can actually observe. If a provider does not expose its model version, leave that field unknown; a guessed value creates false confidence.&lt;/p&gt;
&lt;p&gt;For example, a revision record might say:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Revision R-2 belongs to source S-41&lt;/li&gt;
&lt;li&gt;The requested operation was virtual staging&lt;/li&gt;
&lt;li&gt;The input was the original source S-41&lt;/li&gt;
&lt;li&gt;The output was stored as a new asset&lt;/li&gt;
&lt;li&gt;The provider and version were recorded only where available&lt;/li&gt;
&lt;li&gt;The revision was created at a particular time&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;A multi-input edit needs a list of inputs. A single parent field is insufficient when a workflow combines an original photograph, a mask and another supplied asset.&lt;/p&gt;
&lt;p&gt;The initial implementation does not need to become a complicated event-sourcing system. The important properties are stable relationships, controlled updates and an audit trail for changes to the history itself.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://coderlegion.com/?qa=blob&amp;amp;qa_blobid=10277587644727798223&quot; alt=&quot;Source S-41 links to revision R-2, its delivery review and export E-7; a new revision R-3 requires another review.&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Original AI-generated diagram of the illustrative application records used in this article. It is not a product screenshot, a signed Content Credential or a guarantee of image accuracy.&lt;/em&gt;&lt;/p&gt;
&lt;h3 id=&quot;bind-approval-to-a-revision-not-the-whole-job&quot;&gt;Bind approval to a revision, not the whole job&lt;/h3&gt;
&lt;p&gt;Suppose a reviewer approves R-2. A later request changes the sofa and produces R-3. Copying an &lt;code&gt;approved&lt;/code&gt; flag from the job onto R-3 would silently approve an image nobody reviewed.&lt;/p&gt;
&lt;p&gt;Instead, make the review record refer to the exact revision. Include the outcome, time, reviewer identifier and review scope. A new revision starts with a fresh review state. The approved revision can remain available in the history without making it the current delivery candidate.&lt;/p&gt;
&lt;p&gt;Likewise, an export should point back to its approved revision and record its own digest. If delivery uses a resized JPEG, keep that relationship explicit rather than pretending the JPEG is byte-identical to the reviewed source format.&lt;/p&gt;
&lt;p&gt;Keep the review label honest. “Approved for delivery” describes a workflow decision. It does not certify that the room exists exactly as shown, that every material detail is unchanged or that every destination permits that presentation.&lt;/p&gt;
&lt;h3 id=&quot;understand-where-content-credentials-fit&quot;&gt;Understand where Content Credentials fit&lt;/h3&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://spec.c2pa.org/specifications/specifications/2.2/explainer/Explainer.html&quot;&gt;C2PA's explainer&lt;/a&gt; describes Content Credentials as cryptographically bound provenance information. They can carry assertions about a file's origin, edits and AI use. C2PA also makes an important distinction: provenance does not, by itself, establish that depicted content is true or factual.&lt;/p&gt;
&lt;p&gt;An ordinary database row is not a C2PA Content Credential. Calling an internal audit log “C2PA verified” would be inaccurate unless the actual implementation and verification support that statement.&lt;/p&gt;
&lt;p&gt;If a workflow handles credentials, preserve and inspect them using appropriate tooling, and record the validation result separately from human review. Absence of a credential should remain “no credential found” rather than becoming “camera original.” The &lt;a rel=&quot;nofollow&quot; href=&quot;https://iptc.org/std/guidelines/trust-and-credibility/&quot;&gt;IPTC guidance on trust and credibility&lt;/a&gt; is useful background on embedding structured media information and working with C2PA.&lt;/p&gt;
&lt;h3 id=&quot;keep-public-disclosure-smaller-than-the-internal-record&quot;&gt;Keep public disclosure smaller than the internal record&lt;/h3&gt;
&lt;p&gt;An internal support record may need a reviewer ID, permission reference and provider request ID. A public caption usually needs far less. Avoid putting client names, private storage locations, access tokens or detailed prompts into downloadable metadata by default.&lt;/p&gt;
&lt;p&gt;Design separate internal and public representations. The public version can explain an editing category and the relevant disclosure without exposing the operational record behind it. Maintain a retention policy for originals and revisions; preserving useful history is not a reason to retain every file forever.&lt;/p&gt;
&lt;h3 id=&quot;test-the-relationships-before-adding-a-badge&quot;&gt;Test the relationships before adding a badge&lt;/h3&gt;
&lt;p&gt;A focused test set can catch common mistakes:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Create two revisions from the same source. Confirm that each can retrieve the correct parent.&lt;/li&gt;
&lt;li&gt;Approve R-2, then create R-3. Confirm that R-3 is unreviewed.&lt;/li&gt;
&lt;li&gt;Export a resized copy. Confirm that it receives its own file identity and points to R-2.&lt;/li&gt;
&lt;li&gt;Import an image without credentials. Confirm that the UI does not call it authentic or unedited.&lt;/li&gt;
&lt;li&gt;Try to open another workspace's source through a known asset ID. Confirm that authorization is enforced.&lt;/li&gt;
&lt;li&gt;Generate a public record. Confirm that private IDs, tokens and internal notes are excluded.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The result is a more useful answer to “where did this image come from?” The system can show which source was used, how the current revision relates to it and what was actually reviewed. That is a practical foundation for accountable image workflows.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/32192/keep-the-source-provenance-records-for-ai-property-photos</guid>
<pubDate>Fri, 23 Oct 2026 13:01:05 +0000</pubDate>
</item>
<item>
<title>From Lab to Code #1: Exploring Classical Computer Vision with Python and OpenCV</title>
<link>https://coderlegion.com/32074/from-lab-to-code-1-exploring-classical-computer-vision-with-python-and-opencv</link>
<description>&lt;p&gt;Most conversations about computer vision today revolve around deep learning, neural networks, and increasingly sophisticated AI models.&lt;/p&gt;
&lt;p&gt;But not every image processing problem requires a trained model.&lt;/p&gt;
&lt;p&gt;Sometimes, a Gaussian filter is enough to reduce noise. A threshold can separate objects from their background. A Fourier transform can reveal interference that is difficult to understand in the spatial domain.&lt;/p&gt;
&lt;p&gt;These techniques are relatively simple, mathematically interpretable, and still useful in practical applications.&lt;/p&gt;
&lt;p&gt;Recently, while exploring image and signal processing concepts in my computer engineering studies, I decided to start a small programming series called &lt;strong&gt;From Lab to Code&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;The idea is simple: take engineering concepts I encounter, build independent implementations, visualize the results, and share what I learn.&lt;/p&gt;
&lt;p&gt;For the first project, I built a small interactive image processing toolkit using Python, OpenCV, NumPy, and Streamlit.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;GitHub repository:&lt;/strong&gt; &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/Ertugrulmutlu/from-lab-to-code&quot;&gt;https://github.com/Ertugrulmutlu/from-lab-to-code&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Project 01 — Image Processing:&lt;/strong&gt; &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/Ertugrulmutlu/from-lab-to-code/tree/main/01-image-processing&quot;&gt;https://github.com/Ertugrulmutlu/from-lab-to-code/tree/main/01-image-processing&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;what-are-we-building-&quot;&gt;What Are We Building?&lt;/h3&gt;
&lt;p&gt;The application explores four classical computer vision techniques:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Image denoising&lt;/strong&gt; using Gaussian and median filters.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Object counting&lt;/strong&gt; using illumination correction, thresholding, and connected components.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Periodic noise removal&lt;/strong&gt; using the Fast Fourier Transform (FFT).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Color clustering&lt;/strong&gt; using K-means.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The application provides interactive controls, generated example images, and visual comparisons.&lt;/p&gt;
&lt;p&gt;No pretrained models, API calls, or external datasets are required.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/Ertugrulmutlu/from-lab-to-code/main/01-image-processing/results/overview.png&quot; alt=&quot;Image processing examples&quot;&gt;&lt;/p&gt;
&lt;p&gt;All bundled demonstration images are generated programmatically.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;1-image-denoising-gaussian-vs-median-filtering&quot;&gt;1. Image Denoising: Gaussian vs. Median Filtering&lt;/h3&gt;
&lt;p&gt;Digital images are often affected by noise.&lt;/p&gt;
&lt;p&gt;Noise can originate from camera sensors, poor lighting conditions, signal transmission, or image acquisition hardware.&lt;/p&gt;
&lt;p&gt;Two common types are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Gaussian noise:&lt;/strong&gt; Random variations in pixel intensity, often modeled using a normal distribution.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Salt-and-pepper noise:&lt;/strong&gt; Individual pixels unexpectedly taking very dark or very bright values.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Different noise distributions require different filtering strategies.&lt;/p&gt;
&lt;h4 id=&quot;gaussian-filtering&quot;&gt;Gaussian Filtering&lt;/h4&gt;
&lt;p&gt;A Gaussian filter performs a weighted average of neighboring pixels.&lt;/p&gt;
&lt;p&gt;Pixels closer to the center receive higher weights, following a Gaussian distribution.&lt;/p&gt;
&lt;p&gt;Mathematically, the two-dimensional Gaussian kernel can be written as:&lt;/p&gt;
&lt;p&gt;G(x, y) = (1 / 2πσ²) × exp(-(x² + y²) / 2σ²)&lt;/p&gt;
&lt;p&gt;The parameter σ controls the amount of smoothing.&lt;/p&gt;
&lt;p&gt;A larger σ produces stronger smoothing but can also remove fine image details.&lt;/p&gt;
&lt;p&gt;In OpenCV, Gaussian filtering is straightforward:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;import cv2

gaussian = cv2.GaussianBlur(
    image, (5, 5), 0
)
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;median-filtering&quot;&gt;Median Filtering&lt;/h4&gt;
&lt;p&gt;Unlike Gaussian filtering, a median filter replaces the center pixel with the median value of its neighborhood.&lt;/p&gt;
&lt;p&gt;Consider these pixel values:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;[10, 11, 12, 13, 255]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Their average is 60.2, but their median is 12.&lt;/p&gt;
&lt;p&gt;If the value 255 represents an isolated noise spike, the median is much closer to the surrounding pixel values.&lt;/p&gt;
&lt;p&gt;This makes median filtering particularly useful for salt-and-pepper noise.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;median = cv2.medianBlur(image, 5)
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;comparing-the-results&quot;&gt;Comparing the Results&lt;/h4&gt;
&lt;p&gt;The application applies both filters to the same noisy image.&lt;/p&gt;
&lt;p&gt;It also calculates the Mean Squared Error (MSE) against the original image when a reference is available.&lt;/p&gt;
&lt;p&gt;MSE = (1/N) × Σ(original - processed)²&lt;/p&gt;
&lt;p&gt;A lower MSE indicates a smaller average squared difference from the reference image.&lt;/p&gt;
&lt;p&gt;However, MSE alone does not fully describe perceptual image quality.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Key takeaway:&lt;/strong&gt; Gaussian filtering is useful for smoothing distributed noise, while median filtering is often more effective against isolated intensity spikes.&lt;/p&gt;
&lt;p&gt;Neither method is universally superior.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;2-object-counting-from-pixels-to-objects&quot;&gt;2. Object Counting: From Pixels to Objects&lt;/h3&gt;
&lt;p&gt;Suppose we want to count small objects placed on a table.&lt;/p&gt;
&lt;p&gt;At first, this sounds like a simple thresholding problem.&lt;/p&gt;
&lt;p&gt;If the objects are darker than the background, we could classify pixels based on their intensity.&lt;/p&gt;
&lt;p&gt;But what happens when the background illumination is uneven?&lt;/p&gt;
&lt;p&gt;One side of the image might be noticeably darker than the other, making a single global threshold unreliable.&lt;/p&gt;
&lt;h4 id=&quot;step-1-estimate-the-background&quot;&gt;Step 1: Estimate the Background&lt;/h4&gt;
&lt;p&gt;We can approximate slowly changing illumination using a large Gaussian blur.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;background = cv2.GaussianBlur(
    gray, (0, 0), sigmaX=30
)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Subtracting the original image from the estimated background helps emphasize darker foreground objects.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;corrected = cv2.subtract(
    background, gray
)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This reduces the influence of gradual illumination changes.&lt;/p&gt;
&lt;h4 id=&quot;step-2-apply-otsu-thresholding&quot;&gt;Step 2: Apply Otsu Thresholding&lt;/h4&gt;
&lt;p&gt;Otsu's method automatically selects a threshold based on the image's intensity distribution.&lt;/p&gt;
&lt;p&gt;Instead of manually choosing a number such as 100 or 150, it searches for a threshold that separates the intensity classes according to their statistical variance.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;_, mask = cv2.threshold(
    corrected,
    0,
    255,
    cv2.THRESH_BINARY + cv2.THRESH_OTSU
)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The output is a binary image containing foreground and background pixels.&lt;/p&gt;
&lt;h4 id=&quot;step-3-identify-connected-components&quot;&gt;Step 3: Identify Connected Components&lt;/h4&gt;
&lt;p&gt;After thresholding, neighboring foreground pixels can be grouped into connected regions.&lt;/p&gt;
&lt;p&gt;OpenCV provides a useful function for this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;count, labels, stats, centroids = (
    cv2.connectedComponentsWithStats(
        mask, connectivity=8
    )
)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The function returns component labels and information such as bounding boxes and pixel areas.&lt;/p&gt;
&lt;p&gt;By excluding the background and removing regions below a minimum area, we can estimate the number of objects.&lt;/p&gt;
&lt;p&gt;The project also applies a small morphological opening operation before counting to remove isolated foreground noise.&lt;/p&gt;
&lt;h4 id=&quot;where-can-this-be-useful-&quot;&gt;Where Can This Be Useful?&lt;/h4&gt;
&lt;p&gt;This approach can be applied to controlled visual inspection tasks involving separate objects, such as small manufactured components.&lt;/p&gt;
&lt;p&gt;However, it comes with important assumptions.&lt;/p&gt;
&lt;p&gt;Objects should be sufficiently separated, and the contrast between objects and background should be reasonably clear.&lt;/p&gt;
&lt;p&gt;Touching objects may merge into a single connected component.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Key takeaway:&lt;/strong&gt; Reliable object counting often requires preprocessing, not just thresholding.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;3-fourier-transform-removing-periodic-noise&quot;&gt;3. Fourier Transform: Removing Periodic Noise&lt;/h3&gt;
&lt;p&gt;This was one of the most interesting parts of the project.&lt;/p&gt;
&lt;p&gt;Images are normally represented in the spatial domain, where each pixel corresponds to a position.&lt;/p&gt;
&lt;p&gt;But some image artifacts are easier to identify in the frequency domain.&lt;/p&gt;
&lt;p&gt;For example, imagine an image affected by regularly repeating stripes.&lt;/p&gt;
&lt;p&gt;Those stripes represent periodic variations in pixel intensity.&lt;/p&gt;
&lt;h4 id=&quot;moving-to-the-frequency-domain&quot;&gt;Moving to the Frequency Domain&lt;/h4&gt;
&lt;p&gt;The two-dimensional Discrete Fourier Transform decomposes an image into spatial frequency components.&lt;/p&gt;
&lt;p&gt;Each component represents a particular frequency and orientation.&lt;/p&gt;
&lt;p&gt;Using NumPy:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;import numpy as np

spectrum = np.fft.fft2(gray)

shifted = np.fft.fftshift(spectrum)

magnitude = np.log1p(
    np.abs(shifted)
)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;fft2()&lt;/code&gt; computes the two-dimensional Fourier transform.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;fftshift()&lt;/code&gt; moves the zero-frequency component to the center.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;abs()&lt;/code&gt; obtains the magnitude.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;log1p()&lt;/code&gt; makes the magnitude spectrum easier to visualize.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Periodic interference may appear as distinct peaks in the Fourier spectrum.&lt;/p&gt;
&lt;h4 id=&quot;removing-specific-frequencies&quot;&gt;Removing Specific Frequencies&lt;/h4&gt;
&lt;p&gt;If we know which frequencies correspond to interference, we can suppress them using a notch filter.&lt;/p&gt;
&lt;p&gt;A notch filter removes selected frequency regions while preserving much of the remaining spectrum.&lt;/p&gt;
&lt;p&gt;For real-valued images, the Fourier spectrum has conjugate symmetry.&lt;/p&gt;
&lt;p&gt;This means a sinusoidal interference component generally produces a symmetric pair of frequency peaks.&lt;/p&gt;
&lt;p&gt;Both should be handled to preserve the symmetry of the filtered spectrum.&lt;/p&gt;
&lt;p&gt;The basic operation is:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;filtered_spectrum = shifted * mask
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here, &lt;code&gt;mask&lt;/code&gt; contains zeros at the frequencies we want to suppress and ones elsewhere.&lt;/p&gt;
&lt;p&gt;We then reconstruct the image using the inverse Fourier transform.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;restored = np.fft.ifft2(
    np.fft.ifftshift(filtered_spectrum)
).real
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;an-important-limitation&quot;&gt;An Important Limitation&lt;/h4&gt;
&lt;p&gt;In this project, the periodic noise is generated with known frequencies.&lt;/p&gt;
&lt;p&gt;Therefore, the notch filter already knows where the unwanted frequency components are located.&lt;/p&gt;
&lt;p&gt;This is intentional: it lets us isolate and study the effect of frequency-domain filtering.&lt;/p&gt;
&lt;p&gt;Automatically identifying unknown interference frequencies in arbitrary photographs is a separate problem and is not implemented here.&lt;/p&gt;
&lt;p&gt;A notch filter can also remove useful image information if the rejected frequencies overlap with actual image content.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Key takeaway:&lt;/strong&gt; The Fourier transform provides another perspective on image processing, allowing us to manipulate structures according to their spatial frequencies rather than their pixel positions.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;4-k-means-unsupervised-color-clustering&quot;&gt;4. K-Means: Unsupervised Color Clustering&lt;/h3&gt;
&lt;p&gt;The final technique explores unsupervised learning.&lt;/p&gt;
&lt;p&gt;Unlike classification models, K-means does not require predefined class labels.&lt;/p&gt;
&lt;p&gt;Instead, it groups observations based on similarity.&lt;/p&gt;
&lt;p&gt;For an RGB image, each pixel can be represented as a three-dimensional vector:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;Pixel = [Red, Green, Blue]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;K-means attempts to divide these vectors into K clusters by minimizing the squared distances between pixels and their assigned cluster centers.&lt;/p&gt;
&lt;p&gt;The objective function is:&lt;/p&gt;
&lt;p&gt;J = Σ ||xᵢ - μ(clusterᵢ)||²&lt;/p&gt;
&lt;p&gt;where xᵢ is a pixel vector and μ is the corresponding cluster center.&lt;/p&gt;
&lt;h4 id=&quot;implementing-k-means-with-opencv&quot;&gt;Implementing K-Means with OpenCV&lt;/h4&gt;
&lt;p&gt;First, reshape the image into a collection of RGB vectors:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;pixels = image.reshape(
    -1, 3
).astype(np.float32)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then run K-means:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;criteria = (
    cv2.TERM_CRITERIA_EPS +
    cv2.TERM_CRITERIA_MAX_ITER,
    25,
    0.2
)

_, labels, centers = cv2.kmeans(
    pixels,
    4,
    None,
    criteria,
    5,
    cv2.KMEANS_PP_CENTERS
)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can reconstruct an image by replacing each pixel with the color of its assigned cluster center.&lt;/p&gt;
&lt;p&gt;This produces a simplified image containing a small number of representative colors.&lt;/p&gt;
&lt;h4 id=&quot;color-clustering-is-not-semantic-segmentation&quot;&gt;Color Clustering Is Not Semantic Segmentation&lt;/h4&gt;
&lt;p&gt;This distinction is important.&lt;/p&gt;
&lt;p&gt;K-means only sees RGB values in this implementation.&lt;/p&gt;
&lt;p&gt;It does not understand objects, scenes, or semantic categories.&lt;/p&gt;
&lt;p&gt;Two unrelated objects with similar colors may belong to the same cluster.&lt;/p&gt;
&lt;p&gt;Likewise, one object with multiple colors may be split across several clusters.&lt;/p&gt;
&lt;p&gt;Still, color clustering is useful for exploring image structure, reducing color complexity, and understanding unsupervised learning.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;running-the-project-locally&quot;&gt;Running the Project Locally&lt;/h3&gt;
&lt;p&gt;The project is available on GitHub:&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/Ertugrulmutlu/from-lab-to-code/tree/main/01-image-processing&quot;&gt;https://github.com/Ertugrulmutlu/from-lab-to-code/tree/main/01-image-processing&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;You will need Python 3.10 or newer.&lt;/p&gt;
&lt;p&gt;Clone the repository:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;git clone https://github.com/Ertugrulmutlu/from-lab-to-code.git
cd from-lab-to-code/01-image-processing
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Create a virtual environment:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;python -m venv .venv
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Activate it on Windows PowerShell:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-powershell&quot;&gt;.\.venv\Scripts\Activate.ps1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Install the dependencies:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;python -m pip install -r requirements.txt
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Start the application:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;python -m streamlit run app.py
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Streamlit will open an interactive application in your browser.&lt;/p&gt;
&lt;p&gt;You can switch between the four techniques, adjust processing parameters, and compare the results.&lt;/p&gt;
&lt;p&gt;Each mode includes a programmatically generated example image. You can also upload an image of your own.&lt;/p&gt;
&lt;p&gt;To regenerate the demonstration gallery:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;python demo.py
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To run the included tests:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;python -m unittest discover -s tests -v
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The processing algorithms are implemented in &lt;code&gt;processing.py&lt;/code&gt;, while &lt;code&gt;synthetic.py&lt;/code&gt; generates the example images and &lt;code&gt;app.py&lt;/code&gt; contains the Streamlit interface.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;what-i-learned&quot;&gt;What I Learned&lt;/h3&gt;
&lt;p&gt;Building this project reinforced several important ideas.&lt;/p&gt;
&lt;p&gt;First, &lt;strong&gt;understanding the characteristics of a problem matters as much as selecting an algorithm&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Gaussian filtering and median filtering may look similar at first, but they behave differently depending on the noise distribution.&lt;/p&gt;
&lt;p&gt;Second, preprocessing can determine whether a simple computer vision algorithm succeeds or fails.&lt;/p&gt;
&lt;p&gt;In the object counting example, background illumination correction can be just as important as the thresholding operation itself.&lt;/p&gt;
&lt;p&gt;Third, the frequency domain provides a different way to reason about images.&lt;/p&gt;
&lt;p&gt;Some patterns that are difficult to isolate spatially become much easier to analyze after a Fourier transform.&lt;/p&gt;
&lt;p&gt;Finally, unsupervised clustering demonstrates that useful structure can be extracted from images without labeled training data, even though grouping colors is not the same as understanding a scene.&lt;/p&gt;
&lt;h3 id=&quot;why-i-started-from-lab-to-code&quot;&gt;Why I Started From Lab to Code&lt;/h3&gt;
&lt;p&gt;I wanted a lightweight way to turn engineering concepts into practical software projects.&lt;/p&gt;
&lt;p&gt;Not every project needs to become a startup, a large framework, or a long-term product.&lt;/p&gt;
&lt;p&gt;Sometimes, a small, well-explained implementation is enough to explore an idea and demonstrate what you have learned.&lt;/p&gt;
&lt;p&gt;That is the philosophy behind &lt;strong&gt;From Lab to Code&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Each entry will focus on an engineering topic, build a small independent implementation, and document the underlying concepts and results.&lt;/p&gt;
&lt;p&gt;The projects are independently implemented, with original demonstration materials rather than redistributed university coursework.&lt;/p&gt;
&lt;p&gt;This first project focuses on classical image processing. Future entries may explore other engineering topics as I encounter them.&lt;/p&gt;
&lt;p&gt;If you are learning computer vision or signal processing, I hope the code and explanations help you experiment with these concepts yourself.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Explore the series:&lt;/strong&gt; &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/Ertugrulmutlu/from-lab-to-code&quot;&gt;https://github.com/Ertugrulmutlu/from-lab-to-code&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Explore the image processing project:&lt;/strong&gt; &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/Ertugrulmutlu/from-lab-to-code/tree/main/01-image-processing&quot;&gt;https://github.com/Ertugrulmutlu/from-lab-to-code/tree/main/01-image-processing&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Thanks for reading!&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/32074/from-lab-to-code-1-exploring-classical-computer-vision-with-python-and-opencv</guid>
<pubDate>Thu, 22 Oct 2026 12:53:42 +0000</pubDate>
</item>
<item>
<title>C$2.6 Billion to Fix, C$57 Million to Replace: Canada Was Told in 2019, and Phoenix Is Still Running</title>
<link>https://coderlegion.com/32055/c2-billion-to-fix-c57-million-to-replace-canada-was-told-in-2019-and-phoenix-still-running</link>
<description>&lt;blockquote&gt;&lt;p&gt;Originally published at &lt;a rel=&quot;nofollow&quot; href=&quot;https://vibeagentmaking.com/blog/phoenix-payroll-2-6-billion-to-fix-57-million-to-replace/&quot;&gt;vibeagentmaking.com&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;em&gt;A payroll is not a cost center. It is insurance against the one loss a state cannot pool, and Canada priced that risk at zero.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Somewhere in the Government of Canada, a public servant opened a pay stub and the number was wrong. Then it happened again, to a nurse at Veterans Affairs, to a border officer, to a scientist at Environment Canada, to tens of thousands of people who had done nothing but show up to work. Between 2016 and the early 2020s, Canada eventually paid damages to 324,346 current and former employees for the experience of being paid wrong: about C$400.5 million in a single fiscal year, roughly C$1,200 a head. That payment is usually filed under &lt;a rel=&quot;nofollow&quot; href=&quot;https://vibeagentmaking.com/blog/what-ai-has-actually-cost-companies/&quot;&gt;&quot;settlement.&quot;&lt;/a&gt; It is worth reading it as something more precise. It has the exact shape of an insurance claim, paid out on a risk that the system's original business case had priced at zero.&lt;/p&gt;
&lt;p&gt;Phoenix, the federal pay system that went live in February 2016, is one of the most documented public technology failures in the world. It is usually told as a story about project governance, or vendor management, or the culture of a public service that shipped a system it knew was not ready. Those stories are true. But underneath them is an economics story that the Auditor General's own 2026 tables and the Parliamentary Budget Officer's 2019 numbers tell cleanly, and it is a story every technical leader who has ever signed off on a migration should read. A payroll system is not a cost center. It is insurance against the one loss a state cannot pool. Phoenix is what happens when you buy that insurance as if it were a savings plan.&lt;/p&gt;
&lt;h3 id=&quot;the-payment-that-was-a-claim-not-a-settlement&quot;&gt;The payment that was a claim, not a settlement&lt;/h3&gt;
&lt;p&gt;Look first at the structure of what Canada paid its workers, because the structure is the argument. Two damages agreements, one in June 2019 with several unions and one in October 2020 with the Public Service Alliance of Canada, compensated employees not for provable financial loss but for the ordinary experience of being paid through Phoenix. The schedule reads like an actuarial table: C$1,000 for anyone who worked at least one day in fiscal 2016 to 2017, then C$500 for each of the next three fiscal years, with separate processes layered on top for people who could demonstrate real money lost or severe personal hardship.&lt;/p&gt;
&lt;p&gt;That is not how a lawsuit pays out. A lawsuit pays the harmed party in proportion to the harm. This paid a flat per-person indemnity on a defined event, which is what an insurance policy does. And it went to 324,346 people, which is close to the entire federal public service of the period. When a payout is that near-universal, the state has quietly conceded something large: the exposure was not to some unlucky subset, it was to everyone on the payroll at once.&lt;/p&gt;
&lt;p&gt;That concession is the whole insurance problem in one sentence. &lt;a rel=&quot;nofollow&quot; href=&quot;https://vibeagentmaking.com/blog/the-insurance-problem/&quot;&gt;Insurance works by pooling risks that are independent.&lt;/a&gt; Ten thousand houses can be insured against fire because they do not all burn on the same night. But when a single pay system is the common cause, every employee's risk becomes the same risk, perfectly correlated, and there is nothing left to pool. The loss cannot be diversified away inside the workforce, because the workforce shares one point of failure. Phoenix did not just fail some people. It made everyone's paycheck depend on the same broken thing, which is the one shape of risk that insurance cannot absorb.&lt;/p&gt;
&lt;h3 id=&quot;what-a-payroll-actually-insures&quot;&gt;What a payroll actually insures&lt;/h3&gt;
&lt;p&gt;So what is the payroll insuring against? Not fraud, and not arithmetic. It is insuring the willingness of hundreds of thousands of people to keep coming to work while trusting that the money will be right. That trust is an asset, and it is the one asset a government cannot reinsure, borrow, or buy back on short notice. Most state risks can be pooled or transferred. This one cannot, because the moment the same system pays everyone wrong, the trust breaks for everyone at the same time, in the same direction, for the same reason.&lt;/p&gt;
&lt;p&gt;The 2009 business case did not have a line for that. In July 2009 the cabinet approved C$310 million for the Transformation of Pay Administration, a two-part plan to modernize and consolidate federal pay, and the promise attached to it was about C$70 million a year in savings once an automated, self-serve system replaced hundreds of compensation advisors. Read that promise carefully. It is a savings case. It prices the upside of automation, and it is silent on the cost of the one scenario that would matter most, the scenario where the automated system pays a third of a million people incorrectly for years. That risk was not mispriced. It was assumed away, which is worse, because a number you leave off the page cannot be argued down. It simply is not there when the decision is made.&lt;/p&gt;
&lt;h3 id=&quot;a-catastrophe-that-keeps-paying-out&quot;&gt;A catastrophe that keeps paying out&lt;/h3&gt;
&lt;p&gt;Phoenix went live in February 2016. By 2018 the Auditor General estimated it would cost up to C$2.2 billion to fix by 2023. The claims kept arriving: the C$400.5 million in damages in the year ending March 2021, and a reported cumulative total around C$560 million. And behind the claims sat the backlog, which is the number that decides whether any of this ever ends. As at 30 September 2025, the Auditor General reported that the pay center's backlog &quot;stood at over 233,000 pay transactions, impacting over 133,000 employees.&quot; Of those transactions, 155,217 were older than a single year. The department's own reporting through 2025 already conceded it would miss the target, set back in early 2023, to clear that year-old cohort by March 2026.&lt;/p&gt;
&lt;p&gt;This is the shape of a catastrophe, and it is worth being precise about why. A normal operational error looks like a bell curve: small mistakes scattered around a mean, most of them minor, canceling out in the aggregate. Phoenix is not that. It is a fat tail, where one correlated failure produces a loss that does not cancel and does not settle, it compounds. Each uncleared case is not a static debt. It generates more errors downstream, more manual interventions, more overpayments to claw back and underpayments to make good. And each year the government keeps running the failed system, it pays another year of what is, in every meaningful sense, a premium: the ongoing cost of carrying a policy that has already paid out and keeps paying.&lt;/p&gt;
&lt;h3 id=&quot;the-2019-number-that-should-stop-you&quot;&gt;The 2019 number that should stop you&lt;/h3&gt;
&lt;p&gt;Here is the fact that should stop any reader who has ever argued for keeping a system alive one more year. In May 2019, the Parliamentary Budget Officer, Parliament's own independent costing office, reported that stabilizing Phoenix would cost about C$2.6 billion and take another four years, while building a replacement would cost, in its words, &quot;a comparatively modest $57 million,&quot; a figure covering procurement, testing, and training over six years, with annual operating costs later. Its conclusion was blunt: the cost of building a new pay system &quot;should pale in comparison to the cost of stabilizing the failed Phoenix system.&quot;&lt;/p&gt;
&lt;p&gt;Sit with the gap. In 2019, in public, the government's own budget watchdog said that exit was on the order of forty times cheaper than continuing to fix what it had. The replacement is now scheduled to finish somewhere between 2028 and 2031. That is roughly a decade of paying to run a system after being told, officially, that replacing it was the cheaper choice.&lt;/p&gt;
&lt;p&gt;The reason is not stupidity, and pretending it is misses the lesson. The exit cost is a visible, budgeted, attributable line that someone has to stand up and defend in a given fiscal year. The premium, the ongoing cost of running the broken system, is diffuse, already appropriated, and paid largely by people who are not in the room when the decision gets made. One number has a name attached to it and a meeting on the calendar. The other is spread across a workforce and a decade. Institutions keep paying an invisible premium over a visible exit cost, not because they cannot do the arithmetic, but because only one of the two numbers ever has to be said out loud.&lt;/p&gt;
&lt;h3 id=&quot;the-root-cause-now-a-line-item-in-the-rescue&quot;&gt;The root cause, now a line item in the rescue&lt;/h3&gt;
&lt;p&gt;The most quotable sentence in the entire 2026 record is the Auditor General's, from her statement to reporters: &quot;It is concerning to me that, a decade later, there has been little progress made to simplify these rules.&quot; Phoenix was never mainly a software failure. The pay rules it had to implement, accumulated across dozens of collective agreements over decades, were extraordinarily complex, and the plan had assumed those rules would be simplified before or alongside the new system. They were not. A decade on, they still have not been.&lt;/p&gt;
&lt;p&gt;So look at what the replacement does about it. Rather than simplify the rules, the plan is to buy cloud extensions, estimated at at least C$4 million a year, to carry the unsimplified rules into Dayforce, the new system. The rescue budget now contains a permanent annual line whose only purpose is to preserve the exact complexity that caused the disaster.&lt;/p&gt;
&lt;p&gt;Any engineer who has led a migration knows this move in their bones. It is the decision to port the legacy logic instead of retiring it, to wrap the old mess in an adapter because rewriting it is politically or technically expensive, and to tell yourself the cleanup will happen later. Later does not come. The complexity that broke the old system moves into the new one, now with a recurring invoice attached, and the new system inherits its predecessor's root cause as a fixed operating cost. Canada has put a dollar figure on &quot;we will simplify it later,&quot; and the figure is at least four million a year, forever.&lt;/p&gt;
&lt;h3 id=&quot;the-number-that-is-not-yet-counted-and-the-bet&quot;&gt;The number that is not yet counted, and the bet&lt;/h3&gt;
&lt;p&gt;There is one more line the honest version of this ledger has to carry, and the Auditor General wrote it herself. Public Services and Procurement Canada's estimate for Dayforce is &quot;more than $4.2 billion,&quot; and in the same breath the report adds: &quot;This estimate did not include important costs needed for all departments and agencies to transition.&quot; So C$4.2 billion is not the cost of the rescue. It is the cost of the rescue before the departments are moved, and the auditor says so on the page. A ledger built honestly has a line that reads &quot;not yet counted,&quot; and here that line is load-bearing. It is also worth remembering the trajectory: the replacement that Parliament's costing office sketched at tens of millions in 2019 is now a program estimated in the billions, on broader scope and different accounting, but pointing the same way. Delay shows up in the exit price.&lt;/p&gt;
&lt;p&gt;And then there is the acceleration. The end date for Phoenix moved from 2034, to March 2031, and then, in January 2026 after the audit period had closed, Public Services and Procurement Canada shortened the schedule by about three more years, largely to avoid the cost of running two pay systems in parallel. The Auditor General named the trade in a single sentence: &quot;Although this decision is intended to reduce the complexity and cost of running 2 systems in parallel, it means less time to clear the backlog and prepare departments for the transition.&quot;&lt;/p&gt;
&lt;p&gt;That is a clean, legible bet, and it points two ways at once. What accelerating buys is fewer years of paying to operate Phoenix and Dayforce at the same time, and each parallel year is a real, recurring, avoidable cost. That is cost arithmetic, and by cost arithmetic the acceleration is defensible. What it bets is that the 233,000-odd transactions, 155,217 of them over a year old, can be cleared faster than they have ever been cleared, by a department that already said it would miss the March 2026 target for exactly that older cohort. That is risk arithmetic, and by risk arithmetic the acceleration raises the probability of the one failure the whole project exists to avoid. The Auditor General states the danger directly: the backlog &quot;creates a risk of transferring existing pay errors into the Dayforce system, which could undermine its effectiveness from the start.&quot; Every uncleared case is a wrong number that the replacement inherits as an opening balance. The failed system's output becomes the successful system's starting data. A government that had priced the 2016 risk correctly might notice it is making the same trade again: a certain saving now against an unpriced, correlated loss later.&lt;/p&gt;
&lt;h3 id=&quot;what-a-technical-leader-can-take-from-this&quot;&gt;What a technical leader can take from this&lt;/h3&gt;
&lt;p&gt;The temptation is to read Phoenix as a story about government, and to conclude that large public IT is uniquely doomed. That is the comfortable reading and the useless one. The transferable lesson is about pricing a specific risk that every migration and every automation carries, and there are three moves that follow directly from the ledger above.&lt;/p&gt;
&lt;p&gt;First, put the correlated-failure line in the business case. A savings case that omits &quot;what does this cost if it is wrong for everyone at once&quot; is not a business case, it is a brochure. The worst case is not the average error multiplied by the number of users. It is a single correlated failure across all of them at the same time, and that number belongs on the page next to the projected savings, where it can be argued about before the decision, not discovered after.&lt;/p&gt;
&lt;p&gt;Second, never migrate the errors. The backlog is the tell in the Phoenix story, and it is the tell in most failed cutovers. A migration that carries uncleared wrong state into the new system does not start clean. It starts with the old system's liabilities as its ground truth. Clear the bad data, or quarantine it explicitly, before it becomes the opening balance the new system trusts.&lt;/p&gt;
&lt;p&gt;Third, fix the root cause or price it honestly and forever. If the complexity that broke the old system is ported into the new one, you have not solved the problem. You have bought an annuity on it, and the C$4 million a year for cloud extensions is what that annuity costs when &quot;we will simplify it later&quot; turns into never.&lt;/p&gt;
&lt;p&gt;Underneath all three is the thing a payroll actually protects, which is trust, and trust is the one asset that insurance cannot pool, because when it breaks it breaks for everyone on the payroll at once, in the same direction, for the same reason. Price that risk at zero in the business case and you will not avoid paying for it. You will pay for it later, per person, under someone else's signature. Canada paid about C$1,200 a head, to 324,346 people, and a decade after its own budget office said the exit was cheaper than the fix, it is still paying the premium.&lt;/p&gt;
&lt;h4 id=&quot;the-old-system-s-wrong-numbers-become-the-new-system-s-opening-balance&quot;&gt;The old system's wrong numbers become the new system's opening balance&lt;/h4&gt;
&lt;p&gt;The Auditor General's warning about Phoenix is a provenance problem stated in payroll terms: uncleared errors get inherited by the replacement as ground truth, because nothing in the handover records which figures were already known to be wrong. Chain of Consciousness is that record for agent work, a verifiable log of what was seen, decided and asserted, so &quot;do not migrate the errors&quot; becomes a query you can actually run instead of a resolution you make.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;pip install chain-of-consciousness&lt;/code&gt;  ·  &lt;code&gt;npm install chain-of-consciousness&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://vibeagentmaking.com/hosted-coc/&quot;&gt;Hosted Chain of Consciousness&lt;/a&gt;  ·  &lt;a rel=&quot;nofollow&quot; href=&quot;https://vibeagentmaking.com/verify/&quot;&gt;Verify a record&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;sources&quot;&gt;Sources&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Office of the Auditor General of Canada, 2026 report on modernizing the federal pay system, and the Auditor General's accompanying opening statement (canada.ca), for the &quot;more than $4.2 billion&quot; Dayforce estimate and its exclusion of department transition costs, the 30 September 2025 backlog of over 233,000 transactions affecting over 133,000 employees, the &quot;little progress made to simplify these rules&quot; statement, the risk of transferring pay errors into Dayforce, and the parallel-running trade-off.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Parliamentary Budget Officer, May 2019 report on the cost of Phoenix, for the roughly C$2.6 billion stabilization estimate over four years versus the approximately C$57 million to build a replacement, and the finding that replacing would be cheaper than stabilizing (as reported by CBC News, Global News, The Globe and Mail, and Canada's National Observer).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Government of Canada Public Accounts and reporting by The Globe and Mail, for the C$400.5 million in Phoenix damages paid to 324,346 employees in the year ending 31 March 2021 and the reported cumulative damages of about C$560 million; the June 2019 and October 2020 damages agreements for the C$1,000-plus-C$500 schedule.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Contemporaneous audit reporting (The Globe and Mail on the C$4.2 billion estimate; CBC on the Dayforce transition risks; Global News on backlog progress) and the July 2009 cabinet approval of C$310 million for the Transformation of Pay Administration with its roughly C$70 million annual savings projection.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Figures note: the 2026 Auditor General quotations were read from the primary report; the 2009 business-case figures, the 2018 C$2.2-billion estimate, the 2019 Parliamentary Budget Officer figures, and the damages totals are drawn from public reporting of the underlying documents and are consistent across independent outlets. The C$4.2 billion Dayforce estimate is preliminary and, by the auditor's own note, excludes department transition costs; it is a different system and a different accounting from Phoenix's own multi-year cost, and the two should not be added together. The backlog is reported in &lt;a rel=&quot;nofollow&quot; href=&quot;https://vibeagentmaking.com/blog/read-the-denominator-waymo-safety-numbers/&quot;&gt;three distinct denominators&lt;/a&gt;, pay transactions, cases older than one year, and employees affected, which are not interchangeable.&lt;/p&gt;
</description>
<category>Dev</category>
<guid isPermaLink="true">https://coderlegion.com/32055/c2-billion-to-fix-c57-million-to-replace-canada-was-told-in-2019-and-phoenix-still-running</guid>
<pubDate>Thu, 22 Oct 2026 09:34:19 +0000</pubDate>
</item>
<item>
<title>When an AI Image Job Times Out, Should Retry Create a New Job?</title>
<link>https://coderlegion.com/31954/when-an-ai-image-job-times-out-should-retry-create-a-new-job</link>
<description>&lt;p&gt;A photographer submits a batch of property photos for AI enhancement. The browser times out before it receives a job ID. Clicking Retry could recover the existing job, or create another paid batch while the first is still running.&lt;/p&gt;
&lt;p&gt;The timeout only tells the client it did not receive a usable response. It does not prove the server did nothing.&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://docs.stripe.com/api/idempotent_requests&quot;&gt;Stripe’s idempotent-request documentation&lt;/a&gt; provides a concrete example of reusing an operation key to avoid duplicate work after a connection error. An image-processing service would need its own explicit contract; a payment API’s behavior should not be assumed to apply automatically.&lt;/p&gt;
&lt;p&gt;For a proposed image-job API, I would separate:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Retry this submission:&lt;/strong&gt; same operation key and unchanged inputs, recover its status or result.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Generate another version:&lt;/strong&gt; new operation, with any credit cost visible before submission.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Change the inputs:&lt;/strong&gt; explicitly create a new request rather than silently reusing the old key.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The difficult case is an unknown outcome: no response, no job ID in the browser, and credits possibly reserved. How would your API let the user recover that job safely? Would you require the client to save an operation ID before submitting, and how would you handle key expiry?&lt;/p&gt;
&lt;p&gt;This is a design scenario, not a claim that a named platform currently double-charges. Disclosure: I work with Pixel Perfects Solutions LLC / EstateAI.&lt;/p&gt;
</description>
<category>Discussions</category>
<guid isPermaLink="true">https://coderlegion.com/31954/when-an-ai-image-job-times-out-should-retry-create-a-new-job</guid>
<pubDate>Wed, 21 Oct 2026 10:37:40 +0000</pubDate>
</item>
<item>
<title>Building What Users Ask For vs. Solving What They Actually Need</title>
<link>https://coderlegion.com/31860/building-what-users-ask-for-vs-solving-what-they-actually-need</link>
<description>&lt;p&gt;Every developer has faced this situation.&lt;/p&gt;
&lt;p&gt;A user asks for a new feature. You understand the requirement, write the code, test it, and ship it. The feature works exactly as requested.&lt;/p&gt;
&lt;p&gt;But after the release, something unexpected happens.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Nobody uses it.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;At first, you might think users need more time to understand the feature. Maybe the UI needs improvement. Maybe you need to add a few more options.&lt;/p&gt;
&lt;p&gt;But sometimes, the real problem is much simpler.&lt;/p&gt;
&lt;p&gt;You built what the user asked for, not what the user actually needed.&lt;/p&gt;
&lt;h3 id=&quot;the-difference-between-a-request-and-a-problem&quot;&gt;The Difference Between a Request and a Problem&lt;/h3&gt;
&lt;p&gt;Imagine a user tells you:&lt;/p&gt;
&lt;p&gt;&quot;We need a search button on this page.&quot;&lt;/p&gt;
&lt;p&gt;You add a search button, connect the API, handle the results, and ship the feature.&lt;/p&gt;
&lt;p&gt;A few days later, the user complains that finding information is still difficult.&lt;/p&gt;
&lt;p&gt;After investigating, you discover that the actual problem wasn't the absence of a search button. The page contains hundreds of records, and users need better filters, sorting, and a way to find relevant results quickly.&lt;/p&gt;
&lt;p&gt;The search button was their proposed solution, not necessarily the right solution.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Users usually understand their problems better than they understand the technical solutions to those problems.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;And that's completely normal. Understanding the solution is part of the engineering work.&lt;/p&gt;
&lt;h3 id=&quot;why-developers-make-this-mistake&quot;&gt;Why Developers Make This Mistake&lt;/h3&gt;
&lt;p&gt;There are a few common reasons.&lt;/p&gt;
&lt;h4 id=&quot;1-we-want-to-start-coding-immediately&quot;&gt;1. We Want to Start Coding Immediately&lt;/h4&gt;
&lt;p&gt;A new feature sounds exciting. We open the IDE, create components, write API endpoints, and start implementing.&lt;/p&gt;
&lt;p&gt;Asking more questions can feel like slowing down the work.&lt;/p&gt;
&lt;p&gt;But starting quickly doesn't always mean finishing quickly. If we misunderstand the requirement, we may spend days building something that needs to be redesigned.&lt;/p&gt;
&lt;h4 id=&quot;2-we-treat-every-request-as-a-complete-requirement&quot;&gt;2. We Treat Every Request as a Complete Requirement&lt;/h4&gt;
&lt;p&gt;&quot;Add an export button.&quot;&lt;/p&gt;
&lt;p&gt;&quot;Send an email notification.&quot;&lt;/p&gt;
&lt;p&gt;&quot;Create another dashboard.&quot;&lt;/p&gt;
&lt;p&gt;These requests describe features, but they don't always explain the problem, the intended user, or the expected outcome.&lt;/p&gt;
&lt;p&gt;Before implementing, we need to understand why the feature matters.&lt;/p&gt;
&lt;h4 id=&quot;3-we-focus-on-whether-the-feature-works&quot;&gt;3. We Focus on Whether the Feature Works&lt;/h4&gt;
&lt;p&gt;Developers naturally test whether the code behaves correctly.&lt;/p&gt;
&lt;p&gt;Does the API return the right response? Does the button work? Is the data saved correctly?&lt;/p&gt;
&lt;p&gt;Those checks matter, but there's another question:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Does this feature actually make the user's job easier?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;A feature can pass every technical test and still fail to solve the real problem.&lt;/p&gt;
&lt;h3 id=&quot;ask-better-questions-before-writing-code&quot;&gt;Ask Better Questions Before Writing Code&lt;/h3&gt;
&lt;p&gt;You don't need a long meeting for every small feature. Sometimes, a few clear questions are enough.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;What problem are we trying to solve?&lt;/li&gt;
&lt;li&gt;Who experiences this problem?&lt;/li&gt;
&lt;li&gt;How do they handle it today?&lt;/li&gt;
&lt;li&gt;What would a successful outcome look like?&lt;/li&gt;
&lt;li&gt;Is there a simpler way to achieve the same result?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These questions don't replace implementation. They help ensure that implementation is worth doing.&lt;/p&gt;
&lt;p&gt;For example, instead of immediately building a complex reporting dashboard, you might discover that users only need a downloadable CSV with three specific columns.&lt;/p&gt;
&lt;p&gt;Less code. Less maintenance. A better outcome.&lt;/p&gt;
&lt;h3 id=&quot;learn-to-challenge-the-proposed-solution&quot;&gt;Learn to Challenge the Proposed Solution&lt;/h3&gt;
&lt;p&gt;This doesn't mean developers should reject every request or assume they know better than the user.&lt;/p&gt;
&lt;p&gt;Users have valuable context that developers may not have. The goal is to combine that context with technical understanding.&lt;/p&gt;
&lt;p&gt;If someone requests an unnecessary feature, explain the trade-offs. If their proposed solution is appropriate, build it. If a simpler alternative solves the same problem, discuss it.&lt;/p&gt;
&lt;p&gt;The key is to challenge the solution respectfully, not the person asking for it.&lt;/p&gt;
&lt;p&gt;Instead of saying, &quot;We don't need this feature,&quot; try asking:&lt;/p&gt;
&lt;p&gt;&quot;Would it solve your problem if we did it this way instead?&quot;&lt;/p&gt;
&lt;p&gt;That small change can lead to much better conversations.&lt;/p&gt;
&lt;h3 id=&quot;build-measure-and-learn&quot;&gt;Build, Measure, and Learn&lt;/h3&gt;
&lt;p&gt;Sometimes, you won't know the best solution until users try it.&lt;/p&gt;
&lt;p&gt;That's where a smaller first version helps.&lt;/p&gt;
&lt;p&gt;Build the simplest useful solution, share it with the intended users, observe how they interact with it, and gather feedback.&lt;/p&gt;
&lt;p&gt;Don't assume that a feature is successful just because it has been deployed.&lt;/p&gt;
&lt;p&gt;Look for evidence: Are people using it? Does it reduce manual work? Does it save time? Has the original problem become less frequent?&lt;/p&gt;
&lt;p&gt;Real usage often reveals things that requirements documents cannot.&lt;/p&gt;
&lt;h3 id=&quot;ai-makes-this-even-more-important&quot;&gt;AI Makes This Even More Important&lt;/h3&gt;
&lt;p&gt;AI tools can generate components, APIs, forms, dashboards, and entire application flows faster than ever.&lt;/p&gt;
&lt;p&gt;But faster implementation doesn't guarantee better problem-solving.&lt;/p&gt;
&lt;p&gt;If you give an AI tool a poorly understood requirement, it may produce a polished implementation of the wrong idea.&lt;/p&gt;
&lt;p&gt;The better approach is to use AI to explore the problem before committing to a solution. Ask it to identify assumptions, suggest simpler alternatives, compare trade-offs, and consider edge cases.&lt;/p&gt;
&lt;p&gt;AI can help you implement faster, but you still need to decide what deserves to be implemented.&lt;/p&gt;
&lt;h3 id=&quot;the-real-skill-is-knowing-what-to-build&quot;&gt;The Real Skill Is Knowing What to Build&lt;/h3&gt;
&lt;p&gt;As developers gain experience, their work gradually changes.&lt;/p&gt;
&lt;p&gt;They still write code, debug issues, and review pull requests. But they also learn to question assumptions, understand user workflows, simplify requirements, and recognize when a feature adds unnecessary complexity.&lt;/p&gt;
&lt;p&gt;The goal isn't to build the biggest solution or deliver the most features.&lt;/p&gt;
&lt;p&gt;It's to solve the right problem with a reliable, maintainable solution.&lt;/p&gt;
&lt;p&gt;The next time someone asks you to build a feature, don't stop at asking yourself, &quot;How should I implement this?&quot;&lt;/p&gt;
&lt;p&gt;Ask one more question:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&quot;If I build this exactly as requested, will it actually solve the problem?&quot;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;That question might save you more time than writing the code faster ever could.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/31860/building-what-users-ask-for-vs-solving-what-they-actually-need</guid>
<pubDate>Tue, 20 Oct 2026 14:07:41 +0000</pubDate>
</item>
<item>
<title>Real Estate AI Workflows in 2026: Comparing Review, Revisions, and Human Handoffs</title>
<link>https://coderlegion.com/31835/real-estate-ai-workflows-in-2026-comparing-review-revisions-and-human-handoffs</link>
<description>&lt;p&gt;&lt;em&gt;Cover illustration: AI-generated concept showing an empty room, virtual staging and review. It is not an actual product result.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;A property photographer sends an edited room image to an agent. The agent requests a furniture change. Another team member downloads the earlier version and adds it to the listing.&lt;/p&gt;
&lt;p&gt;The image generator might have worked perfectly. The delivery workflow still failed.&lt;/p&gt;
&lt;p&gt;When comparing real estate AI and editing platforms, it helps to follow one asset from upload through revision and approval. This reveals who controls each step, where context can get lost, and what the surrounding software needs to track.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Disclosure:&lt;/strong&gt; I work in Content &amp;amp; SEO at Pixel Perfects Solutions, the company behind EstateAI. This comparison uses official product pages checked on 20 October 2026. The capabilities below are vendor-described; hands-on image-quality and performance testing has not been performed.&lt;/p&gt;
&lt;h2&gt;A. Three different workflow choices&lt;/h2&gt;
&lt;h3&gt;1. EstateAI with Pixel Perfects: a property-centered hybrid workflow&lt;/h3&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://estateai.pxlperfects.com/&quot;&gt;EstateAI's product overview&lt;/a&gt; describes keeping original photos, generated versions, review, and delivery connected to a property. Its stated sequence is to create the property, analyze its photos, choose services, review versions, and deliver approved media.&lt;/p&gt;
&lt;p&gt;The &lt;a rel=&quot;nofollow&quot; href=&quot;https://estateai.pxlperfects.com/professional-editing&quot;&gt;professional-editing page&lt;/a&gt; describes a route to Pixel Perfects editors for tasks such as complex masking, detailed object removal, retouching, and custom instructions.&lt;/p&gt;
&lt;p&gt;The useful evaluation question is whether the brief survives that handoff. In a trial, choose a difficult image, request a manual correction, and check whether the source, requested changes, and returned version remain easy to trace. The public description is a starting point; a real job should establish the operational details.&lt;/p&gt;
&lt;h3&gt;2. Apply Design: user-controlled staging revisions&lt;/h3&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://www.applydesign.io/&quot;&gt;Apply Design&lt;/a&gt; describes virtual staging with furniture removal, design reuse across images of the same space, and a drag-and-drop editor for revisions.&lt;/p&gt;
&lt;p&gt;That suggests a different evaluation emphasis: how much control does the person preparing the listing need over the staged scene?&lt;/p&gt;
&lt;p&gt;Use two angles of one room in a trial. Make a specific change, such as replacing a sofa, then inspect both views. Check placement, scale, sightlines, and whether the revision process remains manageable. Multi-angle consistency is a vendor-described capability here, not a measured result from this comparison.&lt;/p&gt;
&lt;h3&gt;3. BoxBrownie: managed editing and quality assurance&lt;/h3&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://www.boxbrownie.com/&quot;&gt;BoxBrownie's workflow&lt;/a&gt; describes uploading images, professional editing, a quality-assurance step, and downloading the result. Its site presents a pay-as-you-go service model.&lt;/p&gt;
&lt;p&gt;For a managed service, evaluate the brief and correction loop. Can you describe the required change precisely? What happens when the result needs a rework? Who confirms that it is ready for the client?&lt;/p&gt;
&lt;p&gt;A provider's internal QA and the agent's final approval answer different questions. Build room for both in the delivery process.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://coderlegion.com/?qa=blob&amp;amp;qa_blobid=2182409931604896222&quot; alt=&quot;Decision guide comparing self-service AI, editable staging and editor-led real-estate photo workflows.&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Editorial decision guide: compare revision control and human-review needs before cost and service terms. This is a decision aid, not a performance ranking.&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;B. Compare the same job, with the same acceptance criteria&lt;/h2&gt;
&lt;p&gt;These options place control in different parts of the workflow. A useful trial should expose those differences without pretending that a feature list proves image quality.&lt;/p&gt;
&lt;p&gt;Prepare a small, authorized test set with:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A vacant room shown from two angles.&lt;/li&gt;
&lt;li&gt;A furnished room with a clearly defined removal request.&lt;/li&gt;
&lt;li&gt;A difficult area such as a reflection or partly obscured fixture.&lt;/li&gt;
&lt;li&gt;A precise brief listing what may change and what must remain faithful to the photographed property.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Run only comparable services against the same brief. Do not score an enhancement service as though it were a full staging service.&lt;/p&gt;
&lt;p&gt;Record five things for each result:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Source traceability: Can a reviewer find the original photo and the instructions that produced this version?&lt;/li&gt;
&lt;li&gt;Revision control: Can the team tell which version was requested, returned, approved, and delivered?&lt;/li&gt;
&lt;li&gt;Correction effort: How many actions and how much active staff time did the requested correction take?&lt;/li&gt;
&lt;li&gt;Property fidelity: Did the agreed review identify changes to permanent features, proportions, or views?&lt;/li&gt;
&lt;li&gt;Delivery clarity: Can the recipient identify the approved set and distinguish concepts or staged images from original photos?&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Record observed results and unresolved questions separately. A vendor promise, a successful test, and a missing answer should not share one score.&lt;/p&gt;
&lt;h2&gt;C. A small data model can protect the workflow&lt;/h2&gt;
&lt;p&gt;For developers building the surrounding system, the following is a proposed model, not a description of any provider's internal database:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Source asset: property ID, asset ID, original file reference, and upload time.&lt;/li&gt;
&lt;li&gt;Editing job: source asset IDs, requested operation, brief revision, provider, and status.&lt;/li&gt;
&lt;li&gt;Output version: job ID, output file reference, creation time, and parent version if revised.&lt;/li&gt;
&lt;li&gt;Review decision: exact output version, reviewer, decision, and notes.&lt;/li&gt;
&lt;li&gt;Delivery record: exact approved version IDs, recipient or destination, and delivery time.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Keep a completed job separate from an approved output. A job can finish successfully while its result still requires correction.&lt;/p&gt;
&lt;p&gt;A manual handoff should carry the source and brief with it. If a new output arrives after approval, create a new review decision rather than silently treating the earlier approval as transferable.&lt;/p&gt;
&lt;p&gt;For batch work, track each asset separately. One failed room image should be visible even when the rest of the property is ready.&lt;/p&gt;
&lt;h2&gt;D. The acceptance test that catches a costly mix-up&lt;/h2&gt;
&lt;p&gt;Before adopting a workflow, rehearse this sequence:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Generate or commission version A.&lt;/li&gt;
&lt;li&gt;Request a correction and receive version B.&lt;/li&gt;
&lt;li&gt;Approve B.&lt;/li&gt;
&lt;li&gt;Attempt to deliver A from an older browser tab or saved selection.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The system should make the mismatch visible and require an intentional decision. Also check what happens when the client requests another revision after delivery.&lt;/p&gt;
&lt;p&gt;This test measures a concrete operational risk. It does not depend on which provider produced the image.&lt;/p&gt;
&lt;h2&gt;E. Choose around the work your team needs to own&lt;/h2&gt;
&lt;p&gt;A property-centered hybrid workflow deserves a closer look when preserving context across automated and manual steps is important. An editable staging workflow deserves a closer look when the operator needs direct scene control. A managed editing service deserves a closer look when the team wants to submit a brief and review the returned work.&lt;/p&gt;
&lt;p&gt;The final choice needs evidence from your own authorized images, service scope, review process, and actual costs. Public documentation alone cannot establish a universal winner.&lt;/p&gt;
&lt;p&gt;For background on deciding which work to automate, see &lt;a rel=&quot;nofollow&quot; href=&quot;https://coderlegion.com/26989/ai-vs-human-real-estate-photo-editing-what-should-you-automate-and-what-still-needs-a-human&quot;&gt;AI vs Human Real Estate Photo Editing&lt;/a&gt;. For organizing the source set before editing, see the &lt;a rel=&quot;nofollow&quot; href=&quot;https://coderlegion.com/28541/estateai-photo-library-organize-and-select-photos-before-ai-editing&quot;&gt;EstateAI Photo Library guide&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Sources checked:&lt;/strong&gt; &lt;a rel=&quot;nofollow&quot; href=&quot;https://estateai.pxlperfects.com/&quot;&gt;EstateAI&lt;/a&gt;, &lt;a rel=&quot;nofollow&quot; href=&quot;https://estateai.pxlperfects.com/professional-editing&quot;&gt;EstateAI professional editing&lt;/a&gt;, &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.applydesign.io/&quot;&gt;Apply Design&lt;/a&gt;, and &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.boxbrownie.com/&quot;&gt;BoxBrownie&lt;/a&gt;, 20 October 2026.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/31835/real-estate-ai-workflows-in-2026-comparing-review-revisions-and-human-handoffs</guid>
<pubDate>Tue, 20 Oct 2026 10:51:56 +0000</pubDate>
</item>
<item>
<title>Should an AI Listing Draft Be Allowed to Overwrite Newer Agent Edits?</title>
<link>https://coderlegion.com/31834/should-an-ai-listing-draft-be-allowed-to-overwrite-newer-agent-edits</link>
<description>&lt;p&gt;Imagine a property agent starts an AI rewrite of a listing description. While it runs, a colleague corrects a factual detail in the same listing. The AI result then arrives, based on the older version.&lt;/p&gt;
&lt;p&gt;What should the application do with that result?&lt;/p&gt;
&lt;p&gt;A useful distinction is &lt;strong&gt;finishing generation&lt;/strong&gt; versus &lt;strong&gt;accepting a change&lt;/strong&gt;. The generated draft can be retained without replacing the current description.&lt;/p&gt;
&lt;p&gt;HTTP already has a building block for this problem. &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.rfc-editor.org/rfc/rfc9110.html#section-13.1.1&quot;&gt;RFC 9110, section 13.1.1&lt;/a&gt; describes &lt;code&gt;If-Match&lt;/code&gt; as a way to guard against accidental overwrites when clients update the same resource. With a supporting API, a client sends the strong ETag it read; a failed precondition can produce &lt;code&gt;412 Precondition Failed&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;For a property-content editor, a possible design is:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Record the listing revision used to generate the draft.&lt;/li&gt;
&lt;li&gt;Save the AI output as a separate proposal.&lt;/li&gt;
&lt;li&gt;Compare that base revision with the current listing when someone applies it.&lt;/li&gt;
&lt;li&gt;If they differ, show the original, current, and proposed text for review.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The difficult part is the user experience. A changed punctuation mark and a corrected room count should not necessarily receive the same treatment. A version check catches a conflict; it does not decide whether the new wording is factually safe.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Would you block every stale AI draft, allow field-level merging, or always require a review screen? What would make your choice change?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;This is a hypothetical design question, not a claim about a particular product's implementation.&lt;/p&gt;
</description>
<category>Discussions</category>
<guid isPermaLink="true">https://coderlegion.com/31834/should-an-ai-listing-draft-be-allowed-to-overwrite-newer-agent-edits</guid>
<pubDate>Tue, 20 Oct 2026 10:49:18 +0000</pubDate>
</item>
<item>
<title>Why Your Product Design Is Costing You Users and You Do Not Know It</title>
<link>https://coderlegion.com/31815/why-your-product-design-is-costing-you-users-and-you-do-not-know-it</link>
<description>&lt;p&gt;There is a version of your product that works perfectly. The features are built. The flows are connected. Every screen renders correctly on every device. You have tested it yourself, and it makes sense to you.&lt;/p&gt;
&lt;p&gt;And users are still leaving.&lt;/p&gt;
&lt;p&gt;Not because your product does not work. Because it does not communicate clearly enough for someone who is encountering it for the first time, under real conditions, with divided attention and no patience for confusion. The product works. The design is failing them, quietly, in ways that do not show up as errors and do not trigger support tickets. They just leave.&lt;/p&gt;
&lt;p&gt;This is the most expensive kind of design problem because it is invisible until you look for it specifically. It does not announce itself as a design failure. It announces itself as a conversion rate that is lower than it should be, a retention number that does not reflect the quality of the product, a user acquisition cost that keeps climbing because you are filling a leaking bucket rather than fixing the leak.&lt;/p&gt;
&lt;p&gt;This post is about what those failures look like in practice, why they are so easy to miss, and what it takes to fix them before they cost you another month of users who could have stayed.&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Design problems do not announce themselves as design problems. They announce themselves as low conversion, poor retention, and users who churn after a single session without leaving a reason.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;why-good-products-have-bad-design&quot;&gt;WHY GOOD PRODUCTS HAVE BAD DESIGN&lt;/h2&gt;
&lt;p&gt;The most common reason is that the people who built the product know it too well to see it clearly.&lt;/p&gt;
&lt;p&gt;When you have been working on a product for months, every screen makes intuitive sense. You know what the button does. You know where to find the settings. You know that the empty state means you need to create something first. That knowledge is so deeply embedded that it is impossible to unknow it when you test the product yourself.&lt;/p&gt;
&lt;p&gt;Your users do not have that knowledge. They arrive at your product with no context, a low tolerance for confusion, and a competing option one tap away. Every moment of uncertainty is a moment where they are deciding whether to push through or leave. Most of them leave without telling you why.&lt;/p&gt;
&lt;p&gt;The second reason is that design quality is usually evaluated visually rather than functionally. Does it look good? Does it feel modern? Is the colour scheme consistent? These are valid questions but they are the wrong primary measure. The right primary measure is: does a user who has never seen this product before know what to do within the first ten seconds? Everything else is secondary to that.&lt;/p&gt;
&lt;h2 id=&quot;the-design-failures-that-cost-you-the-most&quot;&gt;THE DESIGN FAILURES THAT COST YOU THE MOST&lt;/h2&gt;
&lt;h3 id=&quot;no-clear-primary-action&quot;&gt;No clear primary action&lt;/h3&gt;
&lt;p&gt;Every screen in your product should have one thing it is primarily asking the user to do. One button, one link, one action that is visually dominant and immediately obvious. When a screen has three buttons of equal size and equal visual weight, the user does not see three choices. They see a decision they have to make before they can proceed, and many of them will not make it.&lt;/p&gt;
&lt;p&gt;A common version of this failure: an onboarding screen that asks the user to connect an integration, invite a team member, or complete their profile, presented as three equally prominent options. The user came to use the product, not to manage their account setup. The right design leads them to the value first and surfaces the setup options progressively. The wrong design stalls them at the threshold and calls it onboarding.&lt;/p&gt;
&lt;h3 id=&quot;forms-that-ask-for-too-much-too-early&quot;&gt;Forms that ask for too much too early&lt;/h3&gt;
&lt;p&gt;Every field you add to a form is a question you are asking the user to answer before they have received any value from your product. Early in a user journey, before trust is established, before the user has experienced what your product does for them, that question has a cost. The longer the form, the higher the cost, and the more users abandon before completing it.&lt;/p&gt;
&lt;p&gt;A registration form that asks for name, email, password, company name, company size, role, and phone number is not collecting useful data. It is filtering out users who are not willing to invest that much before seeing anything. Most of them are not unwilling to give you that information eventually. They are unwilling to give it before they have a reason to trust you with it.&lt;/p&gt;
&lt;p&gt;The fix is not to remove fields permanently. It is to defer them. Ask for the minimum required to create an account. Let the user experience the product. Ask for the rest when you have given them a reason to provide it.&lt;/p&gt;
&lt;h3 id=&quot;error-messages-that-describe-what-went-wrong-without-explaining-what-to-do&quot;&gt;Error messages that describe what went wrong without explaining what to do&lt;/h3&gt;
&lt;p&gt;Error messages are the moment a product most needs to communicate clearly and most often fails to. A user who hits an error is already frustrated. An error message that says &quot;Invalid input&quot; or &quot;Something went wrong&quot; gives them nothing to work with. They do not know what was invalid, what went wrong, or what action would resolve it. Many of them close the tab.&lt;/p&gt;
&lt;p&gt;A form that rejects a password and says &quot;Password does not meet requirements&quot; without showing those requirements anywhere on the screen is a form that will lose a measurable percentage of its signups at that exact moment. A checkout that fails and says &quot;Payment declined. Please try again&quot; without suggesting an alternative payment method or explaining why the payment was declined is a checkout that is losing revenue at its most critical point.&lt;/p&gt;
&lt;p&gt;Every error message in your product should answer three questions: what happened, why it happened, and what the user should do next. If it only answers the first one, it is an error message that creates friction rather than resolving it.&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;An error message that describes what went wrong without explaining what to do next is not helpful. It is a dead end dressed up as communication.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;navigation-that-reflects-how-the-product-was-built-rather-than-how-users-think&quot;&gt;Navigation that reflects how the product was built rather than how users think&lt;/h3&gt;
&lt;p&gt;Products built by developers often have navigation structures that mirror the underlying data model or the feature development sequence. The sections are named for what they are technically rather than what they mean to the user. The hierarchy reflects the engineering architecture rather than the user journey.&lt;/p&gt;
&lt;p&gt;A user looking for their billing information should not have to choose between Account, Settings, and Profile before finding it. A user trying to invite a team member should not have to navigate through three levels of menu to reach a function they will use regularly. Navigation that makes technical sense and user sense are different things, and conflating them is one of the most common sources of silent user frustration.&lt;/p&gt;
&lt;p&gt;The test for navigation is simple: give the product to someone who has never used it and ask them to complete a specific task without guidance. Where they hesitate, where they click the wrong thing, where they express uncertainty, is where the navigation is failing them.&lt;/p&gt;
&lt;h3 id=&quot;empty-states-that-leave-users-stranded&quot;&gt;Empty states that leave users stranded&lt;/h3&gt;
&lt;p&gt;Every new user encounters your product in its empty state: no data, no history, no content, no evidence of what the product does or why they should keep going. This is the moment most products lose the users who were genuinely interested but needed one more signal to commit.&lt;/p&gt;
&lt;p&gt;An empty state that shows a blank screen with a small grey message that says &quot;No items yet&quot; is an empty state that offers nothing. An empty state that shows what the product looks like when it is working, explains the first action the user should take, and makes that action immediately available is an empty state that converts curiosity into commitment.&lt;/p&gt;
&lt;p&gt;The empty state is not an afterthought. It is the first real experience a new user has of your product in use. Designing it with the same care as the populated states is not extra work. It is the work that determines whether a new user becomes a retained user.&lt;/p&gt;
&lt;h3 id=&quot;mobile-experiences-that-were-designed-on-a-desktop&quot;&gt;Mobile experiences that were designed on a desktop&lt;/h3&gt;
&lt;p&gt;A product designed on a large monitor by someone using a mouse will have interaction patterns optimised for that context. Hover states that reveal information. Small click targets that a cursor navigates precisely. Navigation placed at the top of the screen where a thumb cannot comfortably reach. Dense information layouts that make sense on a 27-inch display and become overwhelming on a 6-inch one.&lt;/p&gt;
&lt;p&gt;When that product is used on a phone by someone in a real-world context, one-handed, distracted, in variable lighting, those decisions become friction. The user does not diagnose the problem as a design failure. They just find the product harder to use than they expected and stop using it.&lt;/p&gt;
&lt;p&gt;If more than thirty percent of your users are on mobile and your product was not specifically designed for that experience, you have a gap between your actual user behaviour and your design assumptions that is costing you retention every day.&lt;/p&gt;
&lt;h2 id=&quot;why-these-problems-are-so-hard-to-see-from-inside&quot;&gt;WHY THESE PROBLEMS ARE SO HARD TO SEE FROM INSIDE&lt;/h2&gt;
&lt;p&gt;The people closest to the product are the worst positioned to see these failures. Not because they lack skill, but because they lack distance. Every confusion that would stop a new user is invisible to someone who knows the product well enough to navigate around it unconsciously.&lt;/p&gt;
&lt;p&gt;The most reliable way to see these problems is to watch a real user use the product for the first time without helping them. Not a user test with instructions and prompts. A genuine first-time session where you observe without intervening. What you see in thirty minutes of watching someone encounter your product fresh will surface more design problems than months of internal review.&lt;/p&gt;
&lt;p&gt;The second most reliable method is your own data. Screens with high drop-off rates are screens with design problems. Forms with high abandonment rates have specific fields where users are stopping. Flows with low completion rates have a specific point where most users are leaving. The data does not tell you what the problem is. It tells you exactly where to look for it.&lt;/p&gt;
&lt;h2 id=&quot;what-fixing-these-problems-actually-requires&quot;&gt;WHAT FIXING THESE PROBLEMS ACTUALLY REQUIRES&lt;/h2&gt;
&lt;p&gt;Most of the failures described in this post are not expensive to fix once they are identified. Clarifying a primary action is a design change, not a rebuild. Simplifying a registration form is a product decision, not a development project. Improving an error message is an afternoon of work. Redesigning an empty state is a focused sprint.&lt;/p&gt;
&lt;p&gt;What they require is the willingness to look at the product through the eyes of someone who does not know it, to take the friction they experience seriously rather than explaining it away, and to treat the user experience as a product metric rather than an aesthetic concern.&lt;/p&gt;
&lt;p&gt;The products that retain users are not always the ones with the most features or the best technology. They are the ones that make the first ten minutes feel effortless, because someone thought carefully about what a new user needs at each moment and designed for that rather than for the team that built it.&lt;/p&gt;
&lt;h2 id=&quot;closing-thought&quot;&gt;CLOSING THOUGHT&lt;/h2&gt;
&lt;p&gt;Your product is probably better than your metrics suggest. The gap between the product you built and the results you are seeing is often a design gap rather than a feature gap. Before adding the next feature, it is worth asking whether the features you already have are communicating clearly enough for the users who are encountering them for the first time. The answer, in most products, is that they are not. And the cost of that gap compounds with every user you acquire into it.&lt;/p&gt;
&lt;p&gt;If your product is losing users at a specific point in the flow and you are not sure why, that is exactly the kind of problem I work on. Tell me about what you are seeing and I can tell you whether design is where the answer lives.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/31815/why-your-product-design-is-costing-you-users-and-you-do-not-know-it</guid>
<pubDate>Tue, 20 Oct 2026 09:08:55 +0000</pubDate>
</item>
<item>
<title>AI Building Its Own Successor: Are Developers Ready?</title>
<link>https://coderlegion.com/31811/ai-building-its-own-successor-are-developers-ready</link>
<description>&lt;h2 id=&quot;ai-is-starting-to-build-its-own-successor-are-developers-ready-&quot;&gt;AI Is Starting to Build Its Own Successor. Are Developers Ready?&lt;/h2&gt;
&lt;p&gt;In May 2025, Google DeepMind introduced something that raised an interesting question about the future of artificial intelligence.&lt;/p&gt;
&lt;p&gt;Its system, AlphaEvolve, could generate algorithms, test alternatives, and identify improvements to computing infrastructure. Some of those improvements were applied to systems used in AI development itself.&lt;/p&gt;
&lt;p&gt;AI was no longer simply helping programmers write software. It was contributing to the engineering processes behind future AI systems.&lt;/p&gt;
&lt;p&gt;This is an important development in &lt;strong&gt;AI-assisted AI development&lt;/strong&gt;, but it needs context. Improving an algorithm or optimizing model-training infrastructure is not the same as an AI independently designing, training, and deploying a more intelligent successor.&lt;/p&gt;
&lt;p&gt;That level of unrestricted recursive self-improvement has not been demonstrated.&lt;/p&gt;
&lt;p&gt;Still, the direction of research deserves attention.&lt;/p&gt;
&lt;p&gt;When machines begin contributing to algorithm discovery and scientific experimentation, developers face a different engineering challenge: evaluating whether the proposed improvements actually work.&lt;/p&gt;
&lt;p&gt;Understanding that challenge means examining what current systems have achieved, where their limitations remain, and how software engineering responsibilities may change.&lt;/p&gt;
&lt;h2 id=&quot;when-ai-becomes-part-of-the-research-process&quot;&gt;When AI Becomes Part of the Research Process&lt;/h2&gt;
&lt;p&gt;AI coding assistants have become familiar tools for generating functions, explaining code, and suggesting corrections.&lt;/p&gt;
&lt;p&gt;Research systems are beginning to take a different approach.&lt;/p&gt;
&lt;p&gt;Instead of producing one implementation for a developer to inspect, they can generate multiple candidate solutions, test them against defined objectives, and use the results to guide additional experiments.&lt;/p&gt;
&lt;p&gt;Consider a hypothetical engineering team trying to optimize a scheduling algorithm.&lt;/p&gt;
&lt;p&gt;A conventional approach might involve profiling performance, identifying bottlenecks, modifying the algorithm, and running benchmarks.&lt;/p&gt;
&lt;p&gt;An AI-assisted experimentation system could explore several implementations automatically.&lt;/p&gt;
&lt;p&gt;Some candidates might improve execution time. Others might reduce memory usage. Many would fail correctness tests or offer no meaningful improvement.&lt;/p&gt;
&lt;p&gt;The interesting part is how successful candidates are identified.&lt;/p&gt;
&lt;p&gt;A language model can propose code, but proposing code does not prove that it works. Automated testing and evaluation provide the feedback needed to distinguish promising changes from ineffective ones.&lt;/p&gt;
&lt;p&gt;Earlier research already demonstrated elements of this approach. In 2023, Google DeepMind reported that &lt;a rel=&quot;nofollow&quot; href=&quot;https://deepmind.google/blog/alphadev-discovers-faster-sorting-algorithms/&quot;&gt;AlphaDev discovered improved sorting algorithms&lt;/a&gt;, with selected results incorporated into the LLVM C++ library.&lt;/p&gt;
&lt;p&gt;The broader implication is that some engineering tasks can be treated as search problems: generate alternatives, measure results, and retain improvements.&lt;/p&gt;
&lt;p&gt;That creates new opportunities for automation, especially when objectives are measurable.&lt;/p&gt;
&lt;h2 id=&quot;alphaevolve-an-early-glimpse-of-ai-improving-ai-infrastructure&quot;&gt;AlphaEvolve: An Early Glimpse of AI Improving AI Infrastructure&lt;/h2&gt;
&lt;p&gt;AlphaEvolve offers one of the clearest examples of AI contributing to the development of technology used by other AI systems.&lt;/p&gt;
&lt;p&gt;Introduced by &lt;strong&gt;Google DeepMind in May 2025&lt;/strong&gt;, the system combines Gemini language models, automated evaluation, and evolutionary search.&lt;/p&gt;
&lt;p&gt;Its purpose is to discover and optimize algorithms across mathematical and computing problems.&lt;/p&gt;
&lt;h3 id=&quot;how-alphaevolve-works&quot;&gt;How AlphaEvolve Works&lt;/h3&gt;
&lt;p&gt;The process begins with a problem that can be expressed in code and evaluated programmatically.&lt;/p&gt;
&lt;p&gt;Gemini models propose possible solutions. Automated evaluators then execute and score those candidates according to criteria such as correctness or performance.&lt;/p&gt;
&lt;p&gt;Promising implementations are retained and used to inform future proposals.&lt;/p&gt;
&lt;p&gt;Over repeated iterations, the system explores a broader range of algorithmic possibilities than a single model response would provide.&lt;/p&gt;
&lt;p&gt;The basic workflow looks like this:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Problem Definition → Candidate Algorithms → Automated Evaluation → Selection → Further Optimization&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Unlike ordinary code completion, this process includes a feedback mechanism that helps determine which changes are worth keeping.&lt;/p&gt;
&lt;h3 id=&quot;what-has-alphaevolve-achieved-&quot;&gt;What Has AlphaEvolve Achieved?&lt;/h3&gt;
&lt;p&gt;Google reported several concrete applications.&lt;/p&gt;
&lt;p&gt;One involved data center scheduling. According to DeepMind, an AlphaEvolve-discovered heuristic helped recover an average of 0.7% of Google's worldwide computing resources.&lt;/p&gt;
&lt;p&gt;Another involved optimizing a matrix multiplication kernel used in Gemini's architecture. Google reported a 23% speedup for that kernel, contributing to approximately a 1% reduction in model training time.&lt;/p&gt;
&lt;p&gt;These are specific, Google-reported results, not performance improvements that automatically apply to other systems.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Source:&lt;/strong&gt; &lt;a rel=&quot;nofollow&quot; href=&quot;https://deepmind.google/blog/alphaevolve-a-gemini-powered-coding-agent-for-designing-advanced-algorithms/&quot;&gt;Google DeepMind — AlphaEvolve: A Gemini-Powered Coding Agent&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Why does this matter?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Training and operating advanced AI models depend on enormous amounts of computation. Even relatively small efficiency gains can have practical value when an operation runs repeatedly at scale.&lt;/p&gt;
&lt;p&gt;But AlphaEvolve's success also reveals an important limitation.&lt;/p&gt;
&lt;p&gt;The system is particularly effective when candidate solutions can be evaluated objectively. It does not demonstrate that AI can independently solve every research problem or guarantee that generated algorithms are suitable for production.&lt;/p&gt;
&lt;p&gt;Its results depend on the quality of the evaluation process and subsequent engineering validation.&lt;/p&gt;
&lt;h2 id=&quot;the-difference-between-ai-assisted-progress-and-recursive-self-improvement&quot;&gt;The Difference Between AI-Assisted Progress and Recursive Self-Improvement&lt;/h2&gt;
&lt;p&gt;The phrase &lt;em&gt;AI improving AI sounds straightforward&lt;/em&gt;, but it describes several different levels of capability.&lt;/p&gt;
&lt;p&gt;Understanding those differences helps prevent genuine research achievements from becoming exaggerated predictions.&lt;/p&gt;
&lt;h3 id=&quot;ai-assisted-optimization&quot;&gt;AI-Assisted Optimization&lt;/h3&gt;
&lt;p&gt;An AI model proposes improvements to an algorithm, software component, or computing process.&lt;/p&gt;
&lt;p&gt;The proposed changes are evaluated using predefined tests or performance objectives.&lt;/p&gt;
&lt;p&gt;Human engineers still define the problem and decide how validated improvements should be implemented.&lt;/p&gt;
&lt;h3 id=&quot;ai-assisted-research&quot;&gt;AI-Assisted Research&lt;/h3&gt;
&lt;p&gt;Research systems help generate hypotheses, analyze literature, compare possible explanations, and suggest experimental directions.&lt;/p&gt;
&lt;p&gt;They may reduce the effort required to explore a large research space, but scientific findings still depend on suitable evidence.&lt;/p&gt;
&lt;h3 id=&quot;autonomous-experimentation&quot;&gt;Autonomous Experimentation&lt;/h3&gt;
&lt;p&gt;An automated system may coordinate several stages of experimentation, including generating candidates, running tests, analyzing feedback, and selecting the next experiment.&lt;/p&gt;
&lt;p&gt;The degree of autonomy depends on its permissions, capabilities, and operating environment.&lt;/p&gt;
&lt;h3 id=&quot;recursive-self-improvement&quot;&gt;Recursive Self-Improvement&lt;/h3&gt;
&lt;p&gt;Recursive self-improvement describes a more ambitious possibility: an AI system repeatedly improves its own capabilities, with those improvements enabling further advances.&lt;/p&gt;
&lt;p&gt;Improving one algorithm used in AI training does not establish that an entire AI system can autonomously improve its general intelligence.&lt;/p&gt;
&lt;h2 id=&quot;ai-co-scientists-and-the-new-research-workflow&quot;&gt;AI Co-Scientists and the New Research Workflow&lt;/h2&gt;
&lt;p&gt;Algorithm optimization is not the only area where AI has begun contributing to research.&lt;/p&gt;
&lt;p&gt;In &lt;strong&gt;February 2025&lt;/strong&gt;, Google Research introduced an AI co-scientist designed to assist scientists with generating hypotheses and developing research proposals.&lt;/p&gt;
&lt;p&gt;The system uses multiple specialized agents built around Gemini models.&lt;/p&gt;
&lt;p&gt;These agents perform activities such as hypothesis generation, reflection, ranking, and refinement.&lt;/p&gt;
&lt;p&gt;Given a research objective, the system can explore different explanations and propose directions for further investigation.&lt;/p&gt;
&lt;p&gt;Google reported applications involving biomedical research, including drug repurposing and antimicrobial resistance.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Source:&lt;/strong&gt; &lt;a rel=&quot;nofollow&quot; href=&quot;https://research.google/blog/accelerating-scientific-breakthroughs-with-an-ai-co-scientist/&quot;&gt;Google Research — Accelerating Scientific Breakthroughs With an AI Co-Scientist&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;One especially interesting example involved bacterial gene-transfer mechanisms.&lt;/p&gt;
&lt;p&gt;Researchers reported that the system independently proposed a mechanism consistent with findings scientists had already established experimentally but had not yet publicly disclosed.&lt;/p&gt;
&lt;p&gt;That is a meaningful research demonstration. It does not mean the AI independently performed the experiments or established a new scientific fact without human involvement.&lt;/p&gt;
&lt;p&gt;A plausible hypothesis and a verified discovery are different things.&lt;/p&gt;
&lt;p&gt;The former suggests something worth investigating. The latter requires sufficient evidence, appropriate experiments, and reproducible findings.&lt;/p&gt;
&lt;p&gt;AI-assisted scientific discovery may help researchers explore ideas more efficiently, but it cannot eliminate the need for scientific judgment.&lt;/p&gt;
&lt;h2 id=&quot;the-problem-nobody-can-skip-who-verifies-the-improvements-&quot;&gt;The Problem Nobody Can Skip: Who Verifies the Improvements?&lt;/h2&gt;
&lt;p&gt;Suppose an AI system discovers a scheduling algorithm that performs 15% faster than an existing implementation.&lt;/p&gt;
&lt;p&gt;Would that make it a better algorithm?&lt;/p&gt;
&lt;p&gt;Not necessarily.&lt;/p&gt;
&lt;p&gt;The answer depends on what was measured, which inputs were tested, and whether the optimization introduces other problems.&lt;/p&gt;
&lt;p&gt;A scheduling algorithm might perform well under average workloads but become unreliable when requests arrive simultaneously.&lt;/p&gt;
&lt;p&gt;A generated implementation could also consume more memory, introduce security vulnerabilities, or fail under unusual conditions.&lt;/p&gt;
&lt;h2 id=&quot;when-the-benchmark-measures-the-wrong-thing&quot;&gt;When the Benchmark Measures the Wrong Thing&lt;/h2&gt;
&lt;p&gt;Automated systems optimize against the feedback they receive.&lt;/p&gt;
&lt;p&gt;If an evaluator measures only execution speed, it may reward a solution that sacrifices correctness.&lt;/p&gt;
&lt;p&gt;If tests cover only common inputs, the system may produce changes that fail on edge cases.&lt;/p&gt;
&lt;p&gt;This creates a fundamental challenge for autonomous AI research: &lt;strong&gt;an optimization is meaningful only when the evaluation process measures what actually matters.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The problem becomes harder when objectives are ambiguous.&lt;/p&gt;
&lt;p&gt;Scientific novelty, code maintainability, production reliability, and security cannot always be reduced to a single numerical score.&lt;/p&gt;
&lt;h3 id=&quot;why-reproducibility-matters&quot;&gt;Why Reproducibility Matters&lt;/h3&gt;
&lt;p&gt;An improvement discovered in one environment may not transfer to another.&lt;/p&gt;
&lt;p&gt;Hardware configuration, compiler versions, datasets, runtime dependencies, and execution settings can all influence results.&lt;/p&gt;
&lt;p&gt;Developers therefore need reproducible experiments.&lt;/p&gt;
&lt;p&gt;A credible AI-assisted optimization workflow should preserve:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The original implementation and proposed changes.&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Test datasets and evaluation criteria.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Environment and dependency versions.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Performance results and failure cases.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Human review and approval records.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Security deserves particular attention.&lt;/p&gt;
&lt;p&gt;An AI agent executing generated code should operate within clearly defined permissions and controlled environments.&lt;/p&gt;
&lt;p&gt;Automated experimentation should not grant unrestricted access to production systems.&lt;/p&gt;
&lt;p&gt;Even when automated tests confirm functional behavior, engineers still need to examine broader operational consequences.&lt;/p&gt;
&lt;p&gt;The ability to generate more solutions increases the importance of deciding which evidence is sufficient to trust them.&lt;/p&gt;
&lt;h2 id=&quot;the-developer-s-role-is-changing-not-disappearing&quot;&gt;The Developer's Role Is Changing, Not Disappearing&lt;/h2&gt;
&lt;p&gt;AI coding systems are improving, but their effect on actual software development is more complicated than faster code generation suggests.&lt;/p&gt;
&lt;p&gt;In &lt;strong&gt;July 2025&lt;/strong&gt;, research organization &lt;strong&gt;METR&lt;/strong&gt; published a randomized study examining how AI tools affected experienced open-source developers working on familiar repositories.&lt;/p&gt;
&lt;p&gt;The researchers found that participants took 19% longer to complete selected tasks when using the AI tools available during the study.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Source&lt;/strong&gt;: &lt;a rel=&quot;nofollow&quot; href=&quot;https://metr.org/blog/2025-07-10-early-2025-ai-experienced-os-dev-study/&quot;&gt;METR — Measuring the Impact of Early-2025 AI on Experienced Open-Source Developer Productivity&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;That result does not establish that AI tools always reduce productivity. It reflects particular developers, tasks, tools, and experimental conditions.&lt;/p&gt;
&lt;p&gt;In &lt;strong&gt;February 2026&lt;/strong&gt;, &lt;strong&gt;METR&lt;/strong&gt; reported that newer tools showed signs of improving productivity, while participant-selection effects prevented reliable estimates from its updated experiment.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Source&lt;/strong&gt;: &lt;a rel=&quot;nofollow&quot; href=&quot;https://metr.org/blog/2026-02-24-uplift-update/&quot;&gt;METR — Developer Productivity Experiment Update&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;These findings illustrate why productivity claims require careful measurement.&lt;/p&gt;
&lt;p&gt;Generating code quickly is not identical to delivering working, maintainable software quickly.&lt;/p&gt;
&lt;p&gt;Developers still need to understand requirements, investigate unexpected behavior, design systems, and verify that changes do not create new problems.&lt;/p&gt;
&lt;p&gt;As AI-assisted AI development advances, these responsibilities may become more important.&lt;/p&gt;
&lt;p&gt;Engineers may spend less time manually exploring every implementation and more time defining objectives, reviewing experiments, and evaluating results.&lt;/p&gt;
&lt;p&gt;There is genuine uncertainty about how employment and team structures will change.&lt;/p&gt;
&lt;p&gt;What is clearer is that trustworthy AI-assisted engineering requires expertise beyond operating a coding assistant.&lt;/p&gt;
&lt;h2 id=&quot;what-comes-next-for-ai-assisted-research-&quot;&gt;What Comes Next for AI-Assisted Research?&lt;/h2&gt;
&lt;p&gt;The next stage of AI-assisted research will probably involve deeper integration between language models, development tools, evaluators, and scientific workflows.&lt;/p&gt;
&lt;p&gt;Some developments are relatively easy to imagine because their foundations already exist.&lt;/p&gt;
&lt;p&gt;Automated systems may become better at generating alternative implementations, executing controlled experiments, and identifying optimization opportunities.&lt;/p&gt;
&lt;p&gt;Specialized research agents may help organize literature, compare hypotheses, and prepare experimental designs.&lt;/p&gt;
&lt;p&gt;AI coding systems may also become more effective at longer engineering tasks.&lt;/p&gt;
&lt;p&gt;However, longer task execution does not automatically mean reliable autonomy.&lt;/p&gt;
&lt;p&gt;Research from &lt;strong&gt;METR&lt;/strong&gt; examines the duration of software tasks that AI agents can complete at specified success rates.&lt;/p&gt;
&lt;p&gt;Its work highlights progress in agent capabilities while emphasizing that results are tied to particular benchmarks and evaluation conditions.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Source&lt;/strong&gt;: &lt;a rel=&quot;nofollow&quot; href=&quot;https://metr.org/blog/2025-03-19-measuring-ai-ability-to-complete-long-tasks/&quot;&gt;METR — Measuring AI Ability to Complete Long Software Tasks&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;More ambitious possibilities remain uncertain.&lt;/p&gt;
&lt;p&gt;A system might eventually coordinate complex research projects with limited human intervention. It might also encounter difficulties involving incomplete evidence, poorly defined objectives, or errors accumulated across many steps.&lt;/p&gt;
&lt;p&gt;Predictions about unrestricted recursive self-improvement go considerably further.&lt;/p&gt;
&lt;p&gt;Today's algorithm-discovery systems provide evidence that AI can contribute to parts of the development process. They do not establish a timeline for autonomous systems capable of independently designing and validating more intelligent successors.&lt;/p&gt;
&lt;p&gt;The practical question is how much of the engineering and research workflow can be automated reliably.&lt;/p&gt;
&lt;h2 id=&quot;conclusion-progress-is-real-verification-still-matters-&quot;&gt;Conclusion: Progress Is Real. Verification Still Matters.&lt;/h2&gt;
&lt;p&gt;AI is beginning to contribute to the algorithms, computing systems, and research processes that influence future AI development.&lt;/p&gt;
&lt;p&gt;AlphaEvolve demonstrates how automated evaluation can help discover useful algorithmic improvements. AI co-scientist research shows how models can assist with generating and refining scientific hypotheses.&lt;/p&gt;
&lt;p&gt;These achievements are significant, but their limitations are equally important.&lt;/p&gt;
&lt;p&gt;A proposed improvement still needs verification. A promising scientific hypothesis still requires evidence. An AI-generated implementation must still meet real engineering requirements.&lt;/p&gt;
&lt;p&gt;For developers, the emerging challenge is not simply learning to generate more code with AI.&lt;/p&gt;
&lt;p&gt;It is learning how to design better evaluations, investigate generated solutions, and build systems where automated experimentation produces trustworthy results.&lt;/p&gt;
&lt;p&gt;As AI becomes more capable of proposing its own improvements, &lt;strong&gt;could verifying those improvements become one of the most valuable skills in software engineering?&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id=&quot;frequently-asked-questions-1&quot;&gt;Frequently Asked Questions&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;1. Can AI really improve itself?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;AI can optimize selected algorithms and infrastructure used in AI development. However, these achievements do not demonstrate unrestricted autonomous recursive self-improvement.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2. What is AlphaEvolve?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;AlphaEvolve is a Google DeepMind system that combines language models, automated evaluation, and evolutionary search to discover and optimize algorithms.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3. Can AI independently conduct scientific research?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;AI can assist with hypothesis generation, literature analysis, and some automated experiments. Scientific conclusions still require appropriate validation, and reliable general-purpose autonomous research remains an open challenge.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4. Will AI replace software developers?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Future employment effects remain uncertain. AI may automate some implementation tasks, while architecture, testing, security, verification, and production reliability remain essential engineering responsibilities.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/31811/ai-building-its-own-successor-are-developers-ready</guid>
<pubDate>Tue, 20 Oct 2026 08:17:53 +0000</pubDate>
</item>
<item>
<title>Your TypeScript Is Lying to You, and These 5 Places Hide Real Bugs</title>
<link>https://coderlegion.com/31717/your-typescript-is-lying-to-you-and-these-5-places-hide-real-bugs</link>
<description>&lt;p&gt;TypeScript's most dangerous keyword is two letters long.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;as&lt;/code&gt; does not convert anything. It does not check anything. It tells the compiler &quot;trust me&quot;, and the compiler does. If you are wrong, the error moves from your editor to your users.&lt;/p&gt;
&lt;p&gt;Here are five places I keep finding it in real codebases, each one compiling cleanly and each one a crash waiting for the right input.&lt;/p&gt;
&lt;h3 id=&quot;1-casting-an-api-response&quot;&gt;1. Casting an API Response&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;const res = await fetch('/api/users/1');
const user = (await res.json()) as User;

console.log(user.profile.avatarUrl);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;res.json()&lt;/code&gt; returns &lt;code&gt;Promise&amp;lt;any&amp;gt;&lt;/code&gt;. Casting &lt;code&gt;any&lt;/code&gt; to &lt;code&gt;User&lt;/code&gt; is the compiler equivalent of a handshake agreement. If the backend renames a field, makes &lt;code&gt;profile&lt;/code&gt; optional, or returns an error object with a 200 status, this line still compiles and the crash happens three functions later with a message that has nothing to do with the cause.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;What to write instead:&lt;/strong&gt; validate the data at the boundary.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;import { z } from 'zod';

const UserSchema = z.object({
  id: z.string(),
  email: z.string().email(),
  profile: z.object({ avatarUrl: z.string().url().nullable() }),
});

type User = z.infer&amp;lt;typeof UserSchema&amp;gt;;

const user = UserSchema.parse(await res.json());
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now the type is derived from a check that actually ran. If the shape is wrong, you get a clear error at the exact line where bad data entered your app.&lt;/p&gt;
&lt;h3 id=&quot;2-the-non-null-assertion-on-environment-variables&quot;&gt;2. The Non-Null Assertion on Environment Variables&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;const client = new MongoClient(process.env.DATABASE_URL!);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;process.env.X&lt;/code&gt; is typed &lt;code&gt;string | undefined&lt;/code&gt;, which is honest. The &lt;code&gt;!&lt;/code&gt; deletes the &lt;code&gt;undefined&lt;/code&gt; from the type and nothing else. When the variable is missing in a new environment, you get a confusing failure from deep inside a library instead of &quot;DATABASE_URL is missing&quot;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;function requireEnv(name: string): string {
  const value = process.env[name];
  if (!value) throw new Error(`Missing environment variable: ${name}`);
  return value;
}

const client = new MongoClient(requireEnv('DATABASE_URL'));
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The app now fails at startup, with the variable name in the message. That is the cheapest place to find out.&lt;/p&gt;
&lt;h3 id=&quot;3-the-non-null-assertion-on-find-&quot;&gt;3. The Non-Null Assertion on &lt;code&gt;find()&lt;/code&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;const user = users.find((u) =&amp;gt; u.id === id)!;
sendEmail(user.email);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;find&lt;/code&gt; returns &lt;code&gt;undefined&lt;/code&gt; when nothing matches, and &quot;nothing matches&quot; is exactly the case your tests forgot. The &lt;code&gt;!&lt;/code&gt; makes the compiler stop warning you about the one scenario worth warning about.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;const user = users.find((u) =&amp;gt; u.id === id);
if (!user) throw new Error(`User ${id} not found`);

sendEmail(user.email);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After the check, TypeScript narrows &lt;code&gt;user&lt;/code&gt; to a real object on its own. No assertion required.&lt;/p&gt;
&lt;h3 id=&quot;4-casting-object-keys&quot;&gt;4. Casting Object.keys&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;type Settings = { theme: string; lang: string };

function logSettings(s: Settings) {
  (Object.keys(s) as (keyof Settings)[]).forEach((k) =&amp;gt; {
    console.log(s[k].toUpperCase());
  });
}

const fromApi = { theme: 'dark', lang: 'en', retries: 3 };
logSettings(fromApi);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This compiles. TypeScript types are structural, so an object with extra properties is still a valid &lt;code&gt;Settings&lt;/code&gt; when it arrives through a variable. &lt;code&gt;Object.keys&lt;/code&gt; returns plain &lt;code&gt;string[]&lt;/code&gt; for exactly this reason, and the cast to &lt;code&gt;keyof Settings&lt;/code&gt; overrides that protection. At runtime &lt;code&gt;retries&lt;/code&gt; is a number, and &lt;code&gt;toUpperCase&lt;/code&gt; is not a function.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;const SETTING_KEYS = ['theme', 'lang'] as const;

function logSettings(s: Settings) {
  SETTING_KEYS.forEach((k) =&amp;gt; console.log(s[k].toUpperCase()));
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Iterate over a list of keys you control, not over whatever the object happens to contain.&lt;/p&gt;
&lt;h3 id=&quot;5-casting-the-caught-error&quot;&gt;5. Casting the Caught Error&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;try {
  await saveProfile(data);
} catch (e) {
  toast((e as Error).message);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In JavaScript you can throw anything: a string, a number, &lt;code&gt;undefined&lt;/code&gt;, a plain object. Libraries do it. With &lt;code&gt;strict&lt;/code&gt; on, TypeScript types &lt;code&gt;e&lt;/code&gt; as &lt;code&gt;unknown&lt;/code&gt; to keep you honest, and &lt;code&gt;as Error&lt;/code&gt; is the line that undoes it. If something throws a string, &lt;code&gt;.message&lt;/code&gt; is &lt;code&gt;undefined&lt;/code&gt; and your toast says &quot;undefined&quot;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;catch (e) {
  const message = e instanceof Error ? e.message : 'Something went wrong';
  toast(message);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;a-bonus-lie-that-lives-in-your-config&quot;&gt;A Bonus Lie That Lives in Your Config&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;const items: Item[] = [];
const first = items[0];
first.name; // typed as Item, crashes at runtime
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Array index access is typed as &lt;code&gt;Item&lt;/code&gt;, not &lt;code&gt;Item | undefined&lt;/code&gt;, because the compiler cannot know the length. One line in &lt;code&gt;tsconfig.json&lt;/code&gt; fixes it:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-json&quot;&gt;{
  &quot;compilerOptions&quot;: {
    &quot;strict&quot;: true,
    &quot;noUncheckedIndexedAccess&quot;: true
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This flag is not part of &lt;code&gt;strict&lt;/code&gt;, so most projects never turn it on. It adds a few extra checks, and every one of them is a real case where the array could have been empty.&lt;/p&gt;
&lt;h3 id=&quot;the-rule&quot;&gt;The Rule&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;A cast is a promise you make to the compiler that it cannot verify. Data from outside your program needs a runtime check, not a type assertion.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;An easy audit: search your project for &lt;code&gt; as &lt;/code&gt; and for &lt;code&gt;!.&lt;/code&gt; and &lt;code&gt;!;&lt;/code&gt;. For each hit, ask what happens when the assumption is false. Casts on your own literals are usually fine. Casts on network responses, environment variables, search results and caught errors are where the bugs live.&lt;/p&gt;
&lt;p&gt;I keep these checks in the shared &lt;code&gt;lib/&lt;/code&gt; folder of the &lt;a rel=&quot;nofollow&quot; href=&quot;https://pixelanas.com&quot;&gt;TypeScript-first templates on pixelanas.com&lt;/a&gt;, so a missing environment variable or a changed API shape fails loudly on the first request instead of silently in production.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Get the templates:&lt;/strong&gt; &lt;a rel=&quot;nofollow&quot; href=&quot;https://pixelanas.gumroad.com&quot;&gt;https://pixelanas.gumroad.com&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;How many &lt;code&gt;as&lt;/code&gt; casts does your project have, and how many of them are on data you do not control? Drop your count in the comments.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;Anas, full-stack Next.js developer building SaaS products and premium templates. X: &lt;a rel=&quot;nofollow&quot; href=&quot;https://x.com/ASheikh69751&quot;&gt;@ASheikh69751&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/31717/your-typescript-is-lying-to-you-and-these-5-places-hide-real-bugs</guid>
<pubDate>Mon, 19 Oct 2026 12:30:35 +0000</pubDate>
</item>
<item>
<title>AI Hasn’t Shifted The Bottleneck From Coding To Code Review</title>
<link>https://coderlegion.com/31714/ai-hasnt-shifted-the-bottleneck-from-coding-to-code-review</link>
<description>&lt;p&gt;Why we’ve stopped noticing the real improvement opportunity&lt;/p&gt;
&lt;p&gt;Since AI arrived, many people have mentioned that the bottleneck has shifted from coding to code review. This isn’t quite right, as coding wasn’t the bottleneck in the first place, and it’s not code review now. The reason we think either of these things is constraining the flow of value is that mossy hill we all stare past when we look at the mountains.&lt;/p&gt;
&lt;p&gt;Here’s a simple test. For the application or service you work on, &lt;em&gt;how many changes have passed code review but haven’t yet been deployed and enabled for users&lt;/em&gt;? If the answer is none or one, accept my apologies: in your specific case, I’m wrong. But I rarely get that answer. It’s usually more than one and that tells you the bottleneck is elsewhere.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://coderlegion.com/?qa=blob&amp;amp;qa_blobid=14146861425426186446&quot; alt=&quot;Number of changes per deployment batch&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Number of changes per deployment batch (Source: &lt;a rel=&quot;nofollow&quot; href=&quot;https://octopus.com/&quot;&gt;Octopus Deploy&lt;/a&gt;)&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;We’re conducting original research in this area right now, and half of all teams have between 2 and 10 changes sitting in a batch, and a quarter have 11–50. Overall, more than 90% of teams ship in batches rather than one change at a time.&lt;/p&gt;
&lt;p&gt;This number reveals an industry-wide visibility gap. People believe Claude Code, Cursor, and GitHub Copilot have shifted the bottleneck from coding to code review, but that ignores everything that happens after the review and that’s not a personal failing, it’s an industry-wide misperception.&lt;/p&gt;
&lt;p&gt;We’ve grown so used to working in batches that the practice looks like it belongs. It’s overgrown with moss, indistinguishable from the surrounding hills. When you search for ways to speed up software delivery, you won’t see it, because it doesn’t look like a problem. It looks just as things have always been.&lt;/p&gt;
&lt;h3 id=&quot;what-happens-when-ai-floods-a-batch&quot;&gt;What happens when AI floods a batch&lt;/h3&gt;
&lt;p&gt;Writing code is a small part of a longer value stream that starts with an opportunity and ends when a user gets the value they need. The impact across this whole value stream is lumpy. AI will help more in some areas than others, and the end-to-end benefit depends on you noticing the areas where work accumulates.&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://about.gitlab.com/resources/ai-accountability-survey-2026/&quot;&gt;GitLab’s 2026 AI Accountability Report&lt;/a&gt; found 85% of respondents agree AI has shifted the bottleneck from writing code to reviewing it. Yet we can see from deployment batches that 92% of these people are likely wrong, because if there’s accumulation after the code review, it means code review isn’t the bottleneck. It also means speeding up the reviews will make the real bottleneck worse.&lt;/p&gt;
&lt;p&gt;This isn’t to say increased coding speed doesn’t put pressure on code review. &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.faros.ai/blog/ai-software-engineering&quot;&gt;Faros AI’s research&lt;/a&gt; across 10,000 developers found that teams with high AI adoption merge 98% more pull requests, but review time for those changes grows 91%, and average pull request size grows 154%. &lt;a rel=&quot;nofollow&quot; href=&quot;https://cursor.com/blog/productivity&quot;&gt;Cursor’s own study&lt;/a&gt;, run with a University of Chicago economist, found companies merge 39% more pull requests once its coding agent becomes the default.&lt;/p&gt;
&lt;p&gt;However, approving changes faster is a strategy that only works if the change then flows smoothly to production. In the majority of cases, it simply moves into the queue of changes awaiting some other handling, like testing and deployment. If you up the rate and size of changes passing through the review stage, pressure is simply transferred to the real bottleneck.&lt;/p&gt;
&lt;p&gt;Code review looks like a constraint only because it has a visible queue, while the downstream queues are hidden by their general acceptance across the industry. The job of your pipeline is to get changes to production, where they can be used, not to gather them in a “pending deployment” queue.&lt;/p&gt;
&lt;p&gt;And while all those unreleased changes are building up, risk accumulates with it.&lt;/p&gt;
&lt;h3 id=&quot;batches-are-signposts&quot;&gt;Batches are signposts&lt;/h3&gt;
&lt;p&gt;Ask the batch-size question, and you’ll find what’s really constraining your value stream: a manual verification step, a cumbersome change approval or release train process, or no easy way to deploy changes. Not coding. Not code review.&lt;/p&gt;
&lt;p&gt;It’s likely you were working in batches before you started your AI initiative. The introduction of AI will increase the size of your batches and that can cause problems. Increasing code review throughput doesn’t solve the problem, it simply moves changes to the bottleneck faster.&lt;/p&gt;
&lt;p&gt;Using the true constraint to set the pace of your whole value stream will help you invest in solving the right problem. If your retrospectives keep failing to produce noticeable improvements, you’re likely missing the batch problem. It’s why some AI initiatives pay off while others flop.&lt;/p&gt;
&lt;h3 id=&quot;the-studies-miss-it-too&quot;&gt;The studies miss it, too&lt;/h3&gt;
&lt;p&gt;The research into the impact of AI is useful, but like many studies on this topic, it stops at the point code gets merged. It looks at pull requests opened, pull requests merged, hours spent reviewing. None of it asks how long changes wait after the review, or how many are bundled together before anyone sees them in production. Without that number, you can’t find the real constraint.&lt;/p&gt;
&lt;p&gt;Having invested in AI to speed up coding, you’ll be tempted to fix code review next, or give up on code reviews altogether. If you’re shipping in batches, neither will make much difference to how quickly you remove risk or deliver value to the people using the software.&lt;/p&gt;
&lt;p&gt;The reason your organization resists fixing the batch problem is the real problem you need to solve.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/31714/ai-hasnt-shifted-the-bottleneck-from-coding-to-code-review</guid>
<pubDate>Mon, 19 Oct 2026 12:09:32 +0000</pubDate>
</item>
<item>
<title>Database Migrations: Can Your Previous Release Still Run?</title>
<link>https://coderlegion.com/31693/database-migrations-can-your-previous-release-still-run</link>
<description>&lt;p&gt;A deployment can roll back cleanly while the database stays incompatible with the old application. Before shipping a schema change, check whether the previous release can still read and write the data that the new release creates.&lt;/p&gt;
&lt;p&gt;Consider a fictional account service. Version 1 reads a column called &lt;code&gt;display_name&lt;/code&gt;. Version 2 expects &lt;code&gt;public_name&lt;/code&gt;. Renaming the column and deploying the new code together looks tidy, until an old worker, a rolling-deployment instance, or an emergency rollback still asks for &lt;code&gt;display_name&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The migration succeeded. The service can still fail.&lt;/p&gt;
&lt;h3 id=&quot;separate-compatibility-from-cleanup&quot;&gt;Separate compatibility from cleanup&lt;/h3&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://martinfowler.com/bliki/ParallelChange.html&quot;&gt;Danilo Sato’s explanation of parallel change&lt;/a&gt; describes three stages: expand, migrate, and contract. The useful idea is to introduce the new interface while the old one remains available, move consumers, then remove the old interface.&lt;/p&gt;
&lt;p&gt;For a database rollout, turn that idea into explicit release gates:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Expand:&lt;/strong&gt; introduce the new storage shape without removing what the currently deployed application needs.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Migrate:&lt;/strong&gt; move reads and writes deliberately, checking compatibility during the transition.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Contract:&lt;/strong&gt; remove the old shape only after its consumers and the intended rollback window are accounted for.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These stages are a planning model, not a guarantee of zero downtime. The exact migration still depends on the database, data volume, application behavior, and operational limits.&lt;/p&gt;
&lt;h3 id=&quot;work-through-the-awkward-middle&quot;&gt;Work through the awkward middle&lt;/h3&gt;
&lt;p&gt;In the account-service example, adding &lt;code&gt;public_name&lt;/code&gt; is only the beginning. Before copying values, decide which field is authoritative at each stage.&lt;/p&gt;
&lt;p&gt;A possible transition is to deploy compatibility code that continues serving old readers while maintaining both values. If both columns live in the same row, update them in the same database transaction where practical. That still leaves an important question: can any old application instance or background job write only the original column?&lt;/p&gt;
&lt;p&gt;Do not switch reads to the new column while those writers can silently make it stale. Either finish upgrading writers first or design a deliberate synchronization mechanism. A fallback read helps with missing values; it does not detect an outdated non-empty value.&lt;/p&gt;
&lt;p&gt;Backfills need the same care. A naive copy can overwrite a newer value if it races with a live update. Define an update condition, concurrency strategy, and restart behavior before running the job. Record progress so an interrupted backfill can resume without blindly replaying the entire table.&lt;/p&gt;
&lt;h3 id=&quot;test-the-rollback-with-new-data&quot;&gt;Test the rollback with new data&lt;/h3&gt;
&lt;p&gt;A useful rehearsal includes more than deploying version 2 successfully:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Start with representative data, including empty and unusual values.&lt;/li&gt;
&lt;li&gt;Apply the expansion and deploy the compatibility release.&lt;/li&gt;
&lt;li&gt;Run the backfill alongside representative writes.&lt;/li&gt;
&lt;li&gt;Create and edit records using the new release.&lt;/li&gt;
&lt;li&gt;Restore the application version you actually intend to roll back to.&lt;/li&gt;
&lt;li&gt;Verify that it can read and update those new records correctly.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Include background workers, scheduled jobs, exports, and direct database consumers in that exercise. A healthy web endpoint does not prove that every writer is compatible.&lt;/p&gt;
&lt;p&gt;Use synthetic or appropriately protected test data. This checklist proposes evidence to collect; it does not claim that a production migration has been tested.&lt;/p&gt;
&lt;h3 id=&quot;a-compatible-change-can-still-block-traffic&quot;&gt;A compatible change can still block traffic&lt;/h3&gt;
&lt;p&gt;In PostgreSQL, &lt;code&gt;ALTER TABLE&lt;/code&gt; generally acquires an &lt;code&gt;ACCESS EXCLUSIVE&lt;/code&gt; lock unless the documentation specifies otherwise. Compatibility at the application level does not eliminate lock waits.&lt;/p&gt;
&lt;p&gt;PostgreSQL also supports separating certain constraint additions from validation with &lt;code&gt;NOT VALID&lt;/code&gt; and &lt;code&gt;VALIDATE CONSTRAINT&lt;/code&gt;. The permitted constraint types and locking details matter. Check the documentation for your deployed version rather than applying the technique to every schema operation. See the &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.postgresql.org/docs/current/sql-altertable.html&quot;&gt;PostgreSQL ALTER TABLE reference&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Before execution, define acceptable lock wait, query latency, replication lag, and backfill load. Decide who can stop the rollout and what the safe next step is if a threshold is crossed.&lt;/p&gt;
&lt;h3 id=&quot;make-cleanup-its-own-decision&quot;&gt;Make cleanup its own decision&lt;/h3&gt;
&lt;p&gt;Keep a short release note that answers four questions:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Which application versions can run against this schema?&lt;/li&gt;
&lt;li&gt;Which data changes can the previous version understand?&lt;/li&gt;
&lt;li&gt;What evidence allows the old column or interface to be removed?&lt;/li&gt;
&lt;li&gt;After removal, does recovery mean application rollback, a forward fix, or data restoration?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Give the cleanup an owner. Leaving parallel paths indefinitely creates its own maintenance cost, but removing them too early destroys the compatibility you built.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The practical takeaway:&lt;/strong&gt; rehearse rollback against data written by the new release before treating it as a recovery plan.&lt;/p&gt;
&lt;p&gt;What is the most easily missed database consumer in your deployment process?&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Waqas Ahmad | Software Engineer | Content Creator&lt;/em&gt;&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/31693/database-migrations-can-your-previous-release-still-run</guid>
<pubDate>Mon, 19 Oct 2026 07:45:55 +0000</pubDate>
</item>
<item>
<title>Build a Search Box That Won’t Show Stale Results</title>
<link>https://coderlegion.com/31595/build-a-search-box-that-wont-show-stale-results</link>
<description>&lt;p&gt;A search box can display the wrong answer even when every request succeeds.&lt;/p&gt;
&lt;p&gt;Imagine someone types &lt;code&gt;cat&lt;/code&gt;, pauses long enough to trigger a request, then adds an &lt;code&gt;s&lt;/code&gt;. The request for &lt;code&gt;cats&lt;/code&gt; finishes first. A moment later, the older &lt;code&gt;cat&lt;/code&gt; request finishes and replaces the results. The input says one thing; the page answers something else.&lt;/p&gt;
&lt;p&gt;A reliable implementation needs a clear rule: only work associated with the latest input may change the search UI. That rule must cover results, errors, and loading indicators.&lt;/p&gt;
&lt;p&gt;Here, “stale” means belonging to an older input. Data freshness inside your backend or cache is a separate concern.&lt;/p&gt;
&lt;h3 id=&quot;a-give-each-mechanism-one-job&quot;&gt;A. Give each mechanism one job&lt;/h3&gt;
&lt;p&gt;Three pieces work together:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Debouncing&lt;/strong&gt; delays starting a request while the user keeps typing.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cancellation&lt;/strong&gt; stops an obsolete fetch when possible.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;A version guard&lt;/strong&gt; decides whether completed work still owns the UI.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Debouncing alone leaves previously started requests running. Cancellation helps with those requests, but a version guard makes the UI rule explicit, including for future asynchronous work that does not support cancellation.&lt;/p&gt;
&lt;p&gt;The subtle detail is when to advance the version: immediately when input changes. If you wait until the next request starts, an old response can still render during the debounce interval.&lt;/p&gt;
&lt;h3 id=&quot;b-start-with-a-small-page-and-an-explicit-response-shape&quot;&gt;B. Start with a small page and an explicit response shape&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-html&quot;&gt;&amp;lt;label for=&quot;query&quot;&amp;gt;Search articles&amp;lt;/label&amp;gt;
&amp;lt;input id=&quot;query&quot; type=&quot;search&quot; autocomplete=&quot;off&quot;&amp;gt;
&amp;lt;p id=&quot;status&quot; role=&quot;status&quot;&amp;gt;Type to search.&amp;lt;/p&amp;gt;
&amp;lt;ul id=&quot;results&quot; aria-busy=&quot;false&quot;&amp;gt;&amp;lt;/ul&amp;gt;
&amp;lt;script src=&quot;search.js&quot; defer&amp;gt;&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The example expects a same-origin endpoint, &lt;code&gt;/api/search?q=...&lt;/code&gt;, that returns JSON like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-json&quot;&gt;{&quot;items&quot;: [{&quot;title&quot;: &quot;Understanding browser requests&quot;}]}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That endpoint is an application dependency; the browser code does not implement it. An empty search response should be &lt;code&gt;{&quot;items&quot;: []}&lt;/code&gt;. This example treats a missing or malformed body as an error.&lt;/p&gt;
&lt;h3 id=&quot;c-invalidate-first-then-schedule&quot;&gt;C. Invalidate first, then schedule&lt;/h3&gt;
&lt;p&gt;Put this first part in &lt;code&gt;search.js&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;const input = document.querySelector(&quot;#query&quot;);
const status = document.querySelector(&quot;#status&quot;);
const results = document.querySelector(&quot;#results&quot;);

let version = 0;
let timer;
let activeController;

function scheduleSearch() {
  const mine = ++version;
  clearTimeout(timer);
  activeController?.abort();
  activeController = undefined;

  const query = input.value.trim();
  results.replaceChildren();
  results.setAttribute(&quot;aria-busy&quot;, &quot;false&quot;);

  if (!query) {
    status.textContent = &quot;Type to search.&quot;;
    return;
  }

  status.textContent = &quot;Waiting for you to finish typing…&quot;;
  timer = setTimeout(() =&amp;gt; search(query, mine), 250);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Every edit clears the timer, invalidates older work, and aborts the active request. Clearing the field follows the same path, so a pending request cannot refill an intentionally empty results list.&lt;/p&gt;
&lt;p&gt;The example clears visible results immediately. If your product keeps previous results visible while loading, label which query they belong to so users do not mistake them for the current answer.&lt;/p&gt;
&lt;p&gt;The 250 ms delay is an illustrative product choice. Adjust it for your interface. Timers also do not promise exact execution times; busy execution and browser throttling can delay them. See &lt;a rel=&quot;nofollow&quot; href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Window/setTimeout#reasons_for_delays_longer_than_specified&quot;&gt;MDN’s timer behavior notes&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;d-guard-every-asynchronous-outcome&quot;&gt;D. Guard every asynchronous outcome&lt;/h3&gt;
&lt;p&gt;Add the request function and event listener below the first block:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;async function search(query, mine) {
  if (mine !== version) return;

  const controller = new AbortController();
  activeController = controller;
  status.textContent = &quot;Searching…&quot;;
  results.setAttribute(&quot;aria-busy&quot;, &quot;true&quot;);

  try {
    const params = new URLSearchParams({ q: query });
    const response = await fetch(`/api/search?${params}`, {
      signal: controller.signal
    });

    if (!response.ok) {
      throw new Error(`HTTP ${response.status}`);
    }

    const data = await response.json();
    if (mine !== version || controller.signal.aborted) return;

    if (!Array.isArray(data?.items) ||
        !data.items.every(item =&amp;gt; typeof item?.title === &quot;string&quot;)) {
      throw new Error(&quot;Unexpected search response&quot;);
    }

    const rows = data.items.map(item =&amp;gt; {
      const li = document.createElement(&quot;li&quot;);
      li.textContent = item.title;
      return li;
    });
    results.replaceChildren(...rows);
    status.textContent = rows.length
      ? `${rows.length} result${rows.length === 1 ? &quot;&quot; : &quot;s&quot;}.`
      : &quot;No results found.&quot;;
  } catch (error) {
    if (mine !== version || controller.signal.aborted) return;
    status.textContent = &quot;Search failed. Edit the query to try again.&quot;;
    console.error(&quot;Search failed:&quot;, error);
  } finally {
    if (mine === version) results.setAttribute(&quot;aria-busy&quot;, &quot;false&quot;);
    if (activeController === controller) activeController = undefined;
  }
}

input.addEventListener(&quot;input&quot;, scheduleSearch);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Each request gets its own controller. An aborted signal cannot be reset and reused for a later request. Passing that signal to &lt;code&gt;fetch&lt;/code&gt; connects the request to cancellation. See &lt;a rel=&quot;nofollow&quot; href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal&quot;&gt;MDN’s AbortSignal documentation&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;URLSearchParams&lt;/code&gt; handles query-string encoding, including characters such as &lt;code&gt;&amp;amp;&lt;/code&gt; and &lt;code&gt;+&lt;/code&gt;. Pass the original query value rather than pre-encoding it. See &lt;a rel=&quot;nofollow&quot; href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams&quot;&gt;MDN’s URLSearchParams reference&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;There are two asynchronous boundaries here: receiving the response and reading its JSON body. Aborting can also interrupt body consumption. The guard after &lt;code&gt;response.json()&lt;/code&gt; checks ownership before any results are rendered. &lt;a rel=&quot;nofollow&quot; href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch&quot;&gt;MDN explains the fetch and response-body lifecycle&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Notice that the catch block performs the same ownership check. An obsolete request's failure should not replace a newer success with an error message. Cancellation caused by another edit is expected behavior and stays quiet.&lt;/p&gt;
&lt;p&gt;Cleanup needs ownership checks too. An older &lt;code&gt;finally&lt;/code&gt; block must not turn off a newer request's loading state or discard its controller. The version protects the loading state; the identity comparison protects the controller reference.&lt;/p&gt;
&lt;p&gt;HTTP errors deserve separate handling. A &lt;code&gt;404&lt;/code&gt; or &lt;code&gt;500&lt;/code&gt; response does not automatically reject the fetch promise. This example checks &lt;code&gt;response.ok&lt;/code&gt;, which is true for statuses 200 through 299, and throws for unsuccessful responses. See &lt;a rel=&quot;nofollow&quot; href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Response/ok&quot;&gt;MDN’s Response.ok reference&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The response shape is validated before rendering, and titles are assigned through &lt;code&gt;textContent&lt;/code&gt;. That keeps API-provided titles in the text path instead of interpreting them as HTML. See &lt;a rel=&quot;nofollow&quot; href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Node/textContent&quot;&gt;MDN’s textContent reference&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;e-test-the-order-of-events-deliberately&quot;&gt;E. Test the order of events deliberately&lt;/h3&gt;
&lt;p&gt;Fast local responses can hide this bug. Use controlled promises and fake timers so tests choose exactly when each request completes.&lt;/p&gt;
&lt;p&gt;Cover these scenarios:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Rapid typing:&lt;/strong&gt; several edits within the debounce window produce one request for the latest value.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The debounce gap:&lt;/strong&gt; request A starts, the input changes, and A resolves before request B starts. A must not render.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Reversed completion:&lt;/strong&gt; B resolves, then A resolves. B's results remain visible.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cleared input:&lt;/strong&gt; an old response arrives after the field is emptied. The empty state remains.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;A stale failure:&lt;/strong&gt; A rejects while B is loading. B keeps its loading state and controller.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Delayed body consumption:&lt;/strong&gt; A's response headers arrive, but its JSON body completes after a newer search. A still cannot render.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Current failures:&lt;/strong&gt; HTTP errors, network errors, malformed JSON, and an unexpected response shape produce an error state.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Zero matches:&lt;/strong&gt; an empty items array produces the no-results state rather than an error.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;For the ownership tests, deliberately let the fake fetch resolve even after its signal is aborted. That exercises the guard independently from cancellation. It does not represent normal aborted-fetch behavior.&lt;/p&gt;
&lt;p&gt;A deterministic harness for this example passed 12 checks covering those cases, encoding, and text assignment. It used fake DOM objects and fetch promises; it did not verify browser rendering, accessibility announcements, or a real server.&lt;/p&gt;
&lt;h3 id=&quot;f-carry-the-rule-into-production&quot;&gt;F. Carry the rule into production&lt;/h3&gt;
&lt;p&gt;If the component can be removed, add cleanup that advances the version, clears the timer, aborts active work, and removes its listeners.&lt;/p&gt;
&lt;p&gt;Also check input methods used by your audience. An IME-aware version can defer requests during composition while still invalidating obsolete work immediately. If application code sets the input value directly, call the scheduling function explicitly: programmatic value changes do not trigger the normal input event. See &lt;a rel=&quot;nofollow&quot; href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Element/input_event&quot;&gt;MDN’s input event reference&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The durable rule is simple: give each search a version, and let only the current version publish an outcome. Keep that rule intact when adding caching, retries, pagination, or other asynchronous steps.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/31595/build-a-search-box-that-wont-show-stale-results</guid>
<pubDate>Sun, 18 Oct 2026 10:08:35 +0000</pubDate>
</item>
<item>
<title>What evidence should an AI coding assistant provide before saying a bug is fixed?</title>
<link>https://coderlegion.com/31594/what-evidence-should-an-ai-coding-assistant-provide-before-saying-a-bug-is-fixed</link>
<description>&lt;p&gt;An AI coding assistant can produce a plausible patch and a confident explanation in the same response. What evidence should accompany that explanation before a team treats the issue as resolved?&lt;/p&gt;
&lt;p&gt;A useful baseline might include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A reproduction that fails before the change and passes afterward&lt;/li&gt;
&lt;li&gt;The exact tests or commands run, with their outcomes&lt;/li&gt;
&lt;li&gt;An explanation connecting the change to the reported failure&lt;/li&gt;
&lt;li&gt;A clear statement of anything that could not be checked&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The difficult part is deciding how much evidence is enough. A typo in a static label and a change to payment processing deserve different levels of verification. Passing tests also leave an important question: do those tests cover the behavior the user actually reported?&lt;/p&gt;
&lt;p&gt;For a timing bug, a happy-path test seems especially weak. A stronger check would deliberately reverse the order of events, delay a response, or force a failure at the point where the bug appears.&lt;/p&gt;
&lt;p&gt;Would you require a regression test for every AI-generated bug fix, or allow a documented manual check for smaller changes? And should assistants distinguish between “patch prepared,” “automated checks passed,” and “verified in the target environment” instead of compressing all three into “fixed”?&lt;/p&gt;
&lt;p&gt;Curious where you would set the minimum bar, especially when the assistant cannot access the production environment.&lt;/p&gt;
</description>
<category>Discussions</category>
<guid isPermaLink="true">https://coderlegion.com/31594/what-evidence-should-an-ai-coding-assistant-provide-before-saying-a-bug-is-fixed</guid>
<pubDate>Sun, 18 Oct 2026 10:06:10 +0000</pubDate>
</item>
<item>
<title>OAuth Is Not “Login.” I Found That Out the Hard Way in Production</title>
<link>https://coderlegion.com/31422/oauth-is-not-login-i-found-that-out-the-hard-way-in-production</link>
<description>&lt;p&gt;Most people, including a lot of engineers, think OAuth means “the button that says Sign in with Google.” That’s not wrong exactly, but it’s like saying a car is “the thing with a steering wheel.” Technically true, misses basically everything that matters.&lt;/p&gt;
&lt;p&gt;I want to actually walk through what OAuth is, why it exists, what it costs you, and the specific way it bit me in production, because the bug taught me more about OAuth than any tutorial did.&lt;/p&gt;
&lt;p&gt;If I use a term that sounds like engineer-speak, I’ll flag it like this:&lt;/p&gt;
&lt;p&gt;🚨 Jaggy jaggy alert: here’s the jargon, here’s what it actually means in plain English.&lt;/p&gt;
&lt;p&gt;Let’s go.&lt;/p&gt;
&lt;p&gt;What OAuth actually is&lt;br&gt;
OAuth is not a login system. OAuth is a permission system. It answers one question: “can this app do this specific thing on my behalf, without me handing it my password?”&lt;/p&gt;
&lt;p&gt;That’s it. That’s the whole idea.&lt;/p&gt;
&lt;p&gt;Before OAuth, if you wanted some third party app to, say, post to your Twitter or read your Gmail contacts, you had exactly one option: give that app your actual Twitter or Gmail password. The app would log in as you, directly, using your real credentials. Which means that random app now has full access to your account forever, until you go change your password, and even then you’re trusting a third party company stored your password responsibly in the first place.&lt;/p&gt;
&lt;p&gt;OAuth replaces that with a system where you never hand your password to the third party app at all. Instead, you briefly go to Google (or whoever), log in there, and Google hands the app a token, a kind of limited, revocable permission slip, instead of your actual credentials.&lt;/p&gt;
&lt;p&gt;🚨 Jaggy jaggy alert: token. Think of it as a hotel key card instead of your house key. It opens specific doors, for a specific amount of time, and the hotel can deactivate it instantly without you having to change your actual house locks.&lt;/p&gt;
&lt;p&gt;Could the internet work without OAuth&lt;br&gt;
Technically, yes. Plenty of early 2000s apps worked exactly how I described above, you just gave them your password. It’s not that the internet collapses without OAuth.&lt;/p&gt;
&lt;p&gt;What actually happens without it is worse in a slower, quieter way. Every app you connect to anything ends up holding a copy of your real password. A breach at any one of those apps means your actual account password leaks, not just some limited token. You can’t selectively revoke access (“let this app see my calendar but not send emails as me”) because the app either has your full password or it has nothing. And you can’t tell your bank, your email provider, or your social accounts “trust these five apps, not that one,” because from their point of view every connected app looks identical: someone logged in with your password.&lt;/p&gt;
&lt;p&gt;So yes, the world could work without OAuth. It would just be a world where a breach anywhere is a breach everywhere, and “revoke access” isn’t really a button that exists.&lt;/p&gt;
&lt;p&gt;A quick word on OAuth 1.0, the version before this one&lt;br&gt;
Before the OAuth everyone actually uses today, there was OAuth 1.0, back around 2007. Same basic idea, token instead of password, but the mechanics were painful. Every single request had to be cryptographically signed by the app using a shared secret, a process involving HMAC-SHA1 and exact timestamps, before the server would trust it.&lt;/p&gt;
&lt;p&gt;🚨 Jaggy jaggy alert: cryptographic. Just means “based on math that’s specifically designed to be really, really hard to fake or reverse without the right secret ingredient.” When something is described as cryptographically secure, breaking it isn’t impossible, it’s just so expensive and slow that nobody sane bothers trying.&lt;/p&gt;
&lt;p&gt;🚨 Jaggy jaggy alert: signature (signing). Not a handwritten signature. A short code generated from the actual contents of a message plus a secret value, proving two things at once: this message wasn’t altered in transit, and it genuinely came from whoever holds that secret. Change one character of the message and the signature stops matching, which is exactly how tampering gets caught.&lt;/p&gt;
&lt;p&gt;🚨 Jaggy jaggy alert: secret. A private value, basically a password, known only to the two parties who are supposed to trust each other. Whoever holds the secret can prove they are who they claim to be. Whoever doesn’t, can’t fake it convincingly. The whole system falls apart the moment that “secret” stops actually being secret.&lt;/p&gt;
&lt;p&gt;🚨 Jaggy jaggy alert: HMAC-SHA1 signing. A way of mathematically stamping each request so the server can verify nothing was tampered with in transit, instead of just trusting an encrypted connection to do that job. You don’t need the math, just know it meant extra, fiddly cryptography homework on every single request.&lt;/p&gt;
&lt;p&gt;In practice this meant developers spent real time fighting signature mismatches and clock sync issues instead of building their actual app. It worked, but it was miserable, and it still assumed every app had somewhere safe to keep a permanent secret. OAuth 2.0 showed up in 2012 and basically said, let’s just lean on HTTPS to protect the data in transit like everyone already does for everything else, and drop the per-request signing entirely.&lt;/p&gt;
&lt;p&gt;🚨 Jaggy jaggy alert: HTTPS. The padlock in your browser’s address bar. It encrypts everything traveling between your browser and the server, so anyone snooping on the connection, a nosy coffee shop WiFi for instance, sees scrambled nonsense instead of your actual data. OAuth 2.0 leans on this instead of signing every request itself, which is most of why it’s so much simpler than OAuth 1.0.&lt;/p&gt;
&lt;p&gt;Simpler for developers, which is exactly why OAuth 2.0 is the one that actually won and is what the rest of this article has been describing the whole time.&lt;/p&gt;
&lt;p&gt;But that “every app has somewhere safe to keep a secret” assumption didn’t go away, it just got quietly inherited by OAuth 2.0. And a few years later, that assumption stopped being true for a huge chunk of apps. More on that in a minute.&lt;/p&gt;
&lt;p&gt;How it actually works, the short version&lt;br&gt;
Here’s the flow in plain English, using “Sign in with Google” as the example:&lt;/p&gt;
&lt;p&gt;You click the button. The app sends you over to Google, not the other way around, you never type your Google password into the third party app’s own form. At Google, you log in (or you’re already logged in) and Google shows you a screen saying “This app wants to see your email and profile picture, allow it?” You say yes. Google then redirects you back to the app with a short-lived code. The app takes that code and, behind the scenes, trades it with Google for an actual token. From then on, the app uses that token to ask Google for the specific things you approved, nothing more.&lt;/p&gt;
&lt;p&gt;🚨 Jaggy jaggy alert: scope. A scope is the specific permission being requested, like “read your email address” or “post on your behalf.” Apps are supposed to ask for the narrowest scope that does the job, though in practice a lot of apps ask for way more than they need, because broader scopes are easier to code against and nobody’s really checking.&lt;/p&gt;
&lt;p&gt;Download the Medium app&lt;br&gt;
🚨 Jaggy jaggy alert: redirect URI. This is the exact web address Google is allowed to send you back to after login. It’s locked in advance when the app registers with Google, specifically so an attacker can’t trick Google into sending your freshly issued code to some address they control instead.&lt;/p&gt;
&lt;p&gt;That whole flow above quietly assumes one more thing: that the app trading the code for a token is a proper backend server, one that can hold onto a permanent, secret password of its own (a “client secret”) to prove to Google “yes, this request really is from the real app.” Which brings us to a problem nobody really had to think about back in 2012.&lt;/p&gt;
&lt;p&gt;The problem nobody saw coming, apps that can’t keep a secret&lt;br&gt;
Fast forward a few years and suddenly half the apps in the world are single-page apps running entirely in the browser, or mobile apps sitting on someone’s phone. A SPA’s entire codebase gets shipped straight to the user, anyone can pop open dev tools and read it line by line. A mobile app’s installer can be decompiled by anyone with a weekend and mild curiosity. There is nowhere to actually hide a secret in either case. Whatever you bake in, someone can pull back out, which is a uniquely bad punchline for something literally called a “client secret.”&lt;/p&gt;
&lt;p&gt;🚨 Jaggy jaggy alert: client secret. A password-like value, known only to the real backend and Google, used to prove a token request is genuinely coming from the real app and not an imposter. Fine when there’s a server to keep it locked up. Useless the moment the “app” is just JavaScript running on a stranger’s laptop.&lt;/p&gt;
&lt;p&gt;The fix is called PKCE, Proof Key for Code Exchange, and it’s pronounced “pixie,” which might be the single most delightful name in all of security engineering.&lt;/p&gt;
&lt;p&gt;🚨 Jaggy jaggy alert: PKCE. Instead of a long-lived secret baked into the app, the app invents a random, one-time value right before login starts, the code verifier, and sends over a hashed version of it, the code challenge, when kicking off the flow. At the very end, when trading the code for a token, it has to produce that original, un-hashed value again. If someone intercepts the authorization code mid-flow, it’s useless to them, they don’t have the random value that generated it, and they can’t fake having it either.&lt;/p&gt;
&lt;p&gt;🚨 Jaggy jaggy alert: hashing. A one-way math function that turns any input into a fixed-length scrambled output, and crucially, you can’t run it backwards to get the original input out again. Same input always produces the same output, which is exactly what makes it useful here, the app can prove “I know the original value” without ever saying the original value out loud until the very last step.&lt;/p&gt;
&lt;p&gt;No permanent secret to leak, because there was never a permanent secret to begin with, just a fresh, disposable value that self-destructs after one use. Genuinely a cleaner idea than the thing it replaced, and these days it’s recommended for basically every app, server backed or not.&lt;/p&gt;
&lt;p&gt;The trade-off nobody puts on the slide&lt;br&gt;
Here’s what the “just add OAuth” tutorials gloss over: you’re trading simplicity for control, and that trade costs real engineering effort.&lt;/p&gt;
&lt;p&gt;With a plain username and password system, you own the entire flow. One table, one hashing function, done. With OAuth, you now depend on a third party’s uptime, their API changing underneath you, tokens that expire and need refreshing, and an entire second system, your own backend, that has to track who’s logged in even though the actual login happened somewhere else entirely.&lt;/p&gt;
&lt;p&gt;🚨 Jaggy jaggy alert: refresh token. Access tokens are deliberately short-lived, often an hour or less, so if one leaks, the damage window is small. A refresh token is a longer-lived, more sensitive token your backend holds onto quietly, so it can go get a fresh access token without making the user log in again every hour. Lose control of a refresh token and you’ve basically lost control of the session, so these need to be stored far more carefully than access tokens.&lt;/p&gt;
&lt;p&gt;That’s the actual trade-off. You get: no passwords for you to store or leak, users can revoke access with one click from Google’s side, scoped and limited permissions, and single sign-on convenience. You pay for it with: more moving parts, a dependency on an external provider, token lifecycle management, and a genuinely larger surface area for things to quietly go wrong.&lt;/p&gt;
&lt;p&gt;How I actually use this in production&lt;br&gt;
On one platform I worked on, we had Google OAuth as a social login option sitting alongside our own email and password system, with our own JWT issued after either path succeeded.&lt;/p&gt;
&lt;p&gt;🚨 Jaggy jaggy alert: JWT. Short for JSON Web Token. This is different from an OAuth access token, even though people use the words interchangeably all the time. A JWT is a signed, self-contained token your own backend creates, that your own backend can verify without a database lookup, basically a sealed, tamper-proof note saying “this is user 482, and I, the server, vouch for that.” OAuth tokens come from Google. JWTs, in this setup, come from us.&lt;/p&gt;
&lt;p&gt;That distinction, two different kinds of token doing two different jobs, is exactly where things went wrong for me once.&lt;/p&gt;
&lt;p&gt;What actually goes wrong: my real production bug&lt;br&gt;
We had a production authentication bug where users were randomly getting logged out, or logged in as the wrong state, with no clear pattern. Intermittent, hard to reproduce locally, the worst kind of bug.&lt;/p&gt;
&lt;p&gt;The root cause, once I actually traced it through production logs, was almost embarrassingly simple: the OAuth token coming back from the social login flow and our own JWT were both being stored under the same cookie key. Whichever one got written last silently overwrote the other. So depending on timing, a user’s session cookie might end up holding Google’s OAuth token instead of our own JWT, or vice versa, and the backend would try to verify it as the wrong type of token entirely, fail, and drop the session.&lt;/p&gt;
&lt;p&gt;Nobody designed it that way on purpose. It happened because OAuth tokens and JWTs look similar enough, both are just strings you shove in a cookie, that it’s easy to treat them as interchangeable “auth stuff” instead of two distinct tokens with two distinct lifecycles and two distinct purposes. The fix was straightforward once I actually understood the distinction: give each token its own, clearly named cookie key, so they could never collide again.&lt;/p&gt;
&lt;p&gt;The real lesson wasn’t the one line fix. It was that OAuth tokens and your own session tokens are not the same thing wearing a different hat, and treating them as the same thing is exactly the kind of assumption that only breaks in production, under real timing and real concurrent requests, never in your local dev environment where everything happens conveniently one at a time.&lt;/p&gt;
&lt;p&gt;Other pitfalls worth knowing before you hit them&lt;br&gt;
A few more ways this goes sideways, things I’ve seen or would actively watch for:&lt;/p&gt;
&lt;p&gt;Storing tokens in localStorage instead of an httpOnly cookie, which means any cross-site scripting vulnerability on your site can just read the token straight out and walk away with the user's session.&lt;/p&gt;
&lt;p&gt;🚨 Jaggy jaggy alert: httpOnly cookie. A cookie flagged so that JavaScript running in the browser can’t read it at all, only the browser itself can send it along with requests. It’s a deliberate wall against exactly the kind of theft I just described.&lt;/p&gt;
&lt;p&gt;Mismatched redirect URIs between what’s registered and what the app actually sends, which just breaks login outright and is a surprisingly common first deploy bug when you move from a local dev URL to a real production domain and forget to register the new one.&lt;/p&gt;
&lt;p&gt;Asking for way more scopes than you need, which makes users nervous at the consent screen and gives you a bigger blast radius if your token ever does leak.&lt;/p&gt;
&lt;p&gt;Treating “the user has an OAuth token” as “the user is logged in” without actually verifying who they are. OAuth by itself is about authorization, what you’re allowed to do, not identity. Actual login on top of OAuth is a separate layer called OpenID Connect, and conflating the two is a genuinely common source of security bugs.&lt;/p&gt;
&lt;p&gt;So, do you actually need it&lt;br&gt;
If you’re building something small and internal, OAuth is probably overkill, a simple username and password system with properly hashed passwords is less to maintain and less to get wrong. OAuth earns its complexity the moment you want users to log in with an identity they already have elsewhere, or you want to act on a third party service on their behalf without ever touching their password. At that point, yes, deal with the token juggling, because the alternative, a world where “give the app your password” is normal, is worse in ways that only show up after something’s already gone wrong.&lt;/p&gt;
&lt;p&gt;That’s the real shape of it. Not a login button. A permission system, with real trade-offs, that will absolutely find the one gap in your assumptions if you let two different tokens share a cookie key and call it a day.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/31422/oauth-is-not-login-i-found-that-out-the-hard-way-in-production</guid>
<pubDate>Fri, 16 Oct 2026 17:09:26 +0000</pubDate>
</item>
<item>
<title>What habits daily make you a really great software engineer?</title>
<link>https://coderlegion.com/31423/what-habits-daily-make-you-a-really-great-software-engineer</link>
<description>&lt;p&gt;Well, what do you think what are some top notch habits that makes you in top zone of engineering&lt;/p&gt;
</description>
<category>Discussions</category>
<guid isPermaLink="true">https://coderlegion.com/31423/what-habits-daily-make-you-a-really-great-software-engineer</guid>
<pubDate>Fri, 16 Oct 2026 17:09:05 +0000</pubDate>
</item>
<item>
<title>Is Linux an Operating System or Just a Kernel?</title>
<link>https://coderlegion.com/31033/is-linux-an-operating-system-or-just-a-kernel</link>
<description>&lt;p&gt;When I first started learning Linux, I constantly heard two different statements:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&quot;Linux is an operating system.&quot;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&quot;Linux is just a kernel.&quot;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;At first, I thought one of them had to be wrong. After learning how Linux actually works, I realized both statements make sense depending on the context.&lt;/p&gt;
&lt;p&gt;Let's understand why.&lt;/p&gt;
&lt;h3 id=&quot;what-is-an-operating-system-&quot;&gt;What Is an Operating System?&lt;/h3&gt;
&lt;p&gt;An operating system (OS) is the software that acts as a bridge between users, applications, and the computer's hardware.&lt;/p&gt;
&lt;p&gt;Without an operating system, applications wouldn't know how to communicate with the CPU, memory, keyboard, display, storage devices, or network hardware.&lt;/p&gt;
&lt;p&gt;An operating system provides services such as:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Process management&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Memory management&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;File management&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Device management&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Security and permissions&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Networking&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These services allow applications to run without worrying about the underlying hardware.&lt;/p&gt;
&lt;h3 id=&quot;what-is-a-kernel-&quot;&gt;What Is a Kernel?&lt;/h3&gt;
&lt;p&gt;The kernel is the core component of an operating system.&lt;/p&gt;
&lt;p&gt;Think of it as the manager that is always running in the background. Every program that needs hardware access must go through the kernel.&lt;/p&gt;
&lt;p&gt;The kernel is responsible for:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Scheduling CPU time&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Allocating memory&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Managing devices using drivers&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Handling filesystems&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Managing processes&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Enforcing permissions&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Providing system calls&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Applications cannot directly control hardware. Instead, they request services from the kernel.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-plaintext&quot;&gt;Application
      │
System Calls
      │
Linux Kernel
      │
Hardware
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;then-what-is-linux-&quot;&gt;Then What Is Linux?&lt;/h3&gt;
&lt;p&gt;Technically, &lt;strong&gt;Linux&lt;/strong&gt; refers to the Linux kernel created by Linus Torvalds in 1991.&lt;/p&gt;
&lt;p&gt;The kernel alone cannot provide a complete user experience.&lt;/p&gt;
&lt;p&gt;For example, the kernel cannot:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Let you type commands&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Display directories with &lt;code&gt;ls&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Copy files using &lt;code&gt;cp&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Edit files&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Browse the web&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It simply provides the core functionality needed for these programs to work.&lt;/p&gt;
&lt;h3 id=&quot;so-where-do-commands-come-from-&quot;&gt;So Where Do Commands Come From?&lt;/h3&gt;
&lt;p&gt;Commands like:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;ls
cp
mv
mkdir
grep
cat
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;are &lt;strong&gt;not part of the Linux kernel&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Most of these commands come from the &lt;strong&gt;GNU Project&lt;/strong&gt;, which provides many of the utilities we use every day.&lt;/p&gt;
&lt;p&gt;The shell (such as Bash) is also a separate program that lets us interact with the kernel.&lt;/p&gt;
&lt;p&gt;When you type:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;ls
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;the shell starts the &lt;code&gt;ls&lt;/code&gt; program, which eventually makes system calls to the Linux kernel. The kernel retrieves the directory information from the filesystem and returns it to the program, which displays it on your screen.&lt;/p&gt;
&lt;h3 id=&quot;what-is-a-linux-distribution-&quot;&gt;What Is a Linux Distribution?&lt;/h3&gt;
&lt;p&gt;A Linux distribution combines everything needed to create a usable operating system.&lt;/p&gt;
&lt;p&gt;A typical distribution includes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Linux kernel&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;GNU utilities&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Shell (Bash, Zsh, etc.)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Package manager&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;System libraries&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Desktop environment (optional)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Applications&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Examples include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Ubuntu&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Debian&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Fedora&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Arch Linux&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Linux Mint&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Each distribution uses the Linux kernel but packages it with different software and tools.&lt;/p&gt;
&lt;h3 id=&quot;why-do-some-people-say-gnu-linux-&quot;&gt;Why Do Some People Say GNU/Linux?&lt;/h3&gt;
&lt;p&gt;Richard Stallman and the GNU Project argue that most of the user-space tools come from GNU, while Linux is only the kernel.&lt;/p&gt;
&lt;p&gt;Because of this, some people prefer calling the complete operating system &lt;strong&gt;GNU/Linux&lt;/strong&gt; instead of simply Linux.&lt;/p&gt;
&lt;p&gt;Others use the shorter name &quot;Linux&quot; because it is more common.&lt;/p&gt;
&lt;p&gt;Both terms are widely used today, depending on the context.&lt;/p&gt;
&lt;h3 id=&quot;how-everything-fits-together&quot;&gt;How Everything Fits Together&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-plaintext&quot;&gt;+-----------------------------+
| Applications                |
+-----------------------------+
| Shell (Bash)                |
+-----------------------------+
| GNU Utilities               |
+-----------------------------+
| System Libraries            |
+-----------------------------+
| Linux Kernel                |
+-----------------------------+
| Hardware                    |
+-----------------------------+
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;what-i-learned-1&quot;&gt;What I Learned&lt;/h3&gt;
&lt;p&gt;Before learning Linux, I assumed Linux itself contained every command I used.&lt;/p&gt;
&lt;p&gt;Now I understand that the Linux kernel is only the core of the operating system. The commands, shell, libraries, and applications are separate software that work together with the kernel to create the complete Linux experience.&lt;/p&gt;
&lt;p&gt;This also explains why different Linux distributions look and behave differently while sharing the same Linux kernel.&lt;/p&gt;
&lt;h3 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;Linux is technically a &lt;strong&gt;kernel&lt;/strong&gt;, not a complete operating system by itself. However, in everyday conversation, people often use the word &lt;strong&gt;Linux&lt;/strong&gt; to refer to the complete operating system provided by Linux distributions.&lt;/p&gt;
&lt;p&gt;Understanding this distinction helped me better understand how Linux works internally and why different distributions can share the same kernel while offering different user experiences.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/31033/is-linux-an-operating-system-or-just-a-kernel</guid>
<pubDate>Wed, 14 Oct 2026 08:34:17 +0000</pubDate>
</item>
<item>
<title>Understanding Before Implementation: My Philosophy of Engineering in the AI Era</title>
<link>https://coderlegion.com/30902/understanding-before-implementation-my-philosophy-of-engineering-in-the-ai-era</link>
<description>&lt;p&gt;Today, an AI assistant can generate a working API endpoint, write a complex SQL query, or configure a multi-stage CI/CD pipeline in seconds. When code generation is nearly frictionless, it is easy to mistake &lt;em&gt;generating an implementation&lt;/em&gt; for &lt;em&gt;understanding a system&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;AI coding assistants excel at boilerplate, syntax, API orchestration, and fast prototyping. But syntax was never the hardest part of software engineering. The harder part has always been framing the real problem, understanding how components interact, identifying constraints, evaluating trade-offs, and knowing how to verify that a system actually does what it is supposed to do.&lt;/p&gt;
&lt;p&gt;Now that AI can execute code generation on command, conceptual understanding is not less important—it is the very foundation that allows us to command AI effectively.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;the-difference-between-framework-proficiency-and-system-understanding&quot;&gt;The Difference Between Framework Proficiency and System Understanding&lt;/h3&gt;
&lt;p&gt;When picking up a new technology, there are two distinct ways to approach learning:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Learning to reproduce an implementation:&lt;/strong&gt; Memorizing specific APIs, following step-by-step tutorials, or asking AI to generate code for a framework without understanding its underlying mechanics.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Learning to build a mental model:&lt;/strong&gt; Understanding what problem the technology was created to solve, why its abstractions exist, where it fits into a larger architecture, and what trade-offs it introduces.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Frameworks and SDKs teach us &lt;em&gt;how&lt;/em&gt; to interact with specific abstractions. Conceptual understanding helps us decide &lt;em&gt;which&lt;/em&gt; abstractions we need, whether they are appropriate, and when a different approach is necessary.&lt;/p&gt;
&lt;p&gt;This does not mean hands-on experimentation is secondary. Theory without practice can lead to rigid, over-engineered designs that fail in production. Rather, conceptual reasoning and hands-on implementation should reinforce each other in a continuous loop:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;+-------------------------------------------------------------+
|                                                             |
|   1. Build a Mental Model ---&amp;gt; 2. Define Architecture      |
|              ^                             |                |
|              |                             v                |
|   4. Refine Architecture &amp;lt;--- 3. Implement &amp;amp; Test           |
|                               (via AI, SDKs, Experiments)   |
|                                                             |
+-------------------------------------------------------------+
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When you understand the underlying mechanics, you learn a framework with clear intent rather than copying code blindly.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;from-devops-to-agentic-finops-a-personal-journey&quot;&gt;From DevOps to Agentic FinOps: A Personal Journey&lt;/h3&gt;
&lt;p&gt;I experienced the power of this philosophy firsthand when I transitioned into the world of AI agents.&lt;/p&gt;
&lt;p&gt;My professional background was firmly rooted in DevOps—managing cloud infrastructure, automated pipelines, and operational reliability. I had minimal formal experience with AI or agentic frameworks when an opportunity arose to design an agentic solution for a complex DevOps problem: cloud financial operations (FinOps).&lt;/p&gt;
&lt;p&gt;Faced with an unfamiliar domain, I had two choices:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Approach A:&lt;/strong&gt; Spend weeks memorizing popular agentic framework APIs, copying example agent scripts, and trying to force-fit my problem into pre-built templates.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Approach B:&lt;/strong&gt; Step back and build a mental model of agentic systems first. What makes an agentic system different from a script or a deterministic workflow? What components are required—reasoning, memory, tool access, state management? How should data and decisions flow?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I chose &lt;strong&gt;Approach B&lt;/strong&gt;. &lt;/p&gt;
&lt;p&gt;Before diving deep into specific agent frameworks or SDKs, I focused on understanding the system boundaries and architectural capabilities required for an Agentic FinOps solution:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;What exact operational problem were we solving in cloud cost governance?&lt;/li&gt;
&lt;li&gt;Which tasks required autonomous decision-making versus deterministic automation?&lt;/li&gt;
&lt;li&gt;How would an agent reason about cloud resource data, evaluate policies, and execute actions?&lt;/li&gt;
&lt;li&gt;What state and context would need to persist across executions?&lt;/li&gt;
&lt;li&gt;Where were the potential security, cost, and reliability failure modes?&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;+-----------------------------------------------------------------+
|                   Agentic FinOps Conceptual Model              |
+-----------------------------------------------------------------+
|                                                                 |
|  [ Data Sources ]       [ Reasoning Engine ]    [ Action Layer ]|
|  - Cloud Metrics  ---&amp;gt;   - Policy Rules    ---&amp;gt; - Remediation   |
|  - Usage Data           - Context/Memory        - Alerts        |
|                                                                 |
+-----------------------------------------------------------------+
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;By first mapping out the system's core requirements, I could evaluate frameworks objectively. Instead of letting a framework dictate what my system should look like, I used frameworks and SDKs—along with AI assistants—to implement specific, well-defined components of the architecture I had designed.&lt;/p&gt;
&lt;p&gt;I began building and testing core components individually to validate feasibility. Did I have every answer upfront? No. Was every part of the system immediately production-ready? No. The architecture continues to evolve as I learn more. But starting with conceptual clarity allowed me to make meaningful progress in an unfamiliar domain without getting lost in implementation details.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;engineering-in-the-ai-era-taking-the-lead&quot;&gt;Engineering in the AI Era: Taking the Lead&lt;/h3&gt;
&lt;p&gt;AI tools have transformed how quickly we can move from an idea to a working prototype. But generated code is not inherently secure, performant, cost-effective, or architecturally sound.&lt;/p&gt;
&lt;p&gt;To &lt;strong&gt;&quot;take the lead to command AI&quot;&lt;/strong&gt; does not mean typing every line of code manually. It means taking responsibility for:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Problem Definition:&lt;/strong&gt; Articulating clear, context-rich goals and constraints.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;System Architecture:&lt;/strong&gt; Deciding how components decouple, communicate, and fail.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Critique and Evaluation:&lt;/strong&gt; Interrogating AI-generated code for security flaws, edge cases, and hidden performance costs.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Validation:&lt;/strong&gt; Designing small experiments to verify that a proposed solution works under real conditions.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you don't understand what you are asking AI to build, you cannot effectively evaluate what it returns.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;a-transferable-framework-for-learning-new-technologies&quot;&gt;A Transferable Framework for Learning New Technologies&lt;/h3&gt;
&lt;p&gt;Whether you are exploring agentic AI, Kubernetes, distributed databases, or service meshes, this iterative approach can guide your learning:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Define the Problem:&lt;/strong&gt; Understand what pain point the technology addresses before choosing tools.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Build a Mental Model:&lt;/strong&gt; Map out the core concepts, data flows, and state management mechanisms.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Identify Component Responsibilities:&lt;/strong&gt; Determine what each part of the system does and why it exists.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Evaluate Trade-offs:&lt;/strong&gt; Analyze cost, latency, security, and operational complexity across different options.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Accelerate with AI and Frameworks:&lt;/strong&gt; Use AI tools, documentation, and SDKs to rapidly write and prototype code.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Test Core Assumptions:&lt;/strong&gt; Run small, targeted experiments to validate critical capabilities.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Inspect and Verify:&lt;/strong&gt; Audit generated code—do not assume &quot;working code&quot; is correct code.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Refine Iteratively:&lt;/strong&gt; Adjust your architecture as practical findings reveal gaps in your mental model.&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h3 id=&quot;why-i-write-technical-content-this-way&quot;&gt;Why I Write Technical Content This Way&lt;/h3&gt;
&lt;p&gt;This philosophy directly shapes how I write technical articles. My goal is not to publish syntax cheat sheets or step-by-step code tutorials that become outdated with the next SDK release.&lt;/p&gt;
&lt;p&gt;Instead, I aim to help readers understand &lt;strong&gt;why&lt;/strong&gt; systems are built the way they are—their underlying components, decision points, and trade-offs. Once you grasp the foundational architecture, applying any specific SDK, API, or AI tool becomes straightforward.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;final-thoughts-1&quot;&gt;Final Thoughts&lt;/h3&gt;
&lt;p&gt;The ability to generate code quickly is more accessible than ever. But true engineering value lies in judgment, critical thinking, and system architecture.&lt;/p&gt;
&lt;p&gt;Before diving straight into implementation or asking AI to generate a solution, take time to understand the problem, the system, and the trade-offs. Use AI and modern frameworks to build faster, but keep your hands on the architectural wheel.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/30902/understanding-before-implementation-my-philosophy-of-engineering-in-the-ai-era</guid>
<pubDate>Tue, 13 Oct 2026 08:02:50 +0000</pubDate>
</item>
<item>
<title>The Death of SaaS  - What Happens When People Can Make Software Fit Their Lives?</title>
<link>https://coderlegion.com/30898/the-death-of-saas-what-happens-when-people-can-make-software-fit-their-lives</link>
<description>&lt;p&gt;&lt;img src=&quot;https://cdn-images-1.medium.com/max/1512/1*75d-uWEv-Z-gopXH0gLRCw.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;In February 2026, investors wiped approximately $830 billion from the market value of software and services companies over six trading sessions, frightened by the possibility that AI could erode businesses built around selling access to software. By October, US software stocks had recovered much of that ground, and Wall Street appeared considerably less alarmed. [1][2]&lt;/p&gt;
&lt;p&gt;Outside the financial markets, however, customers had begun doing something that stock prices could scarcely capture. They were making their own software.&lt;/p&gt;
&lt;p&gt;Some wanted to replace expensive subscriptions. Others were tired of squeezing their work into applications designed around somebody else’s habits.&lt;/p&gt;
&lt;p&gt;With AI coding tools and existing open-source components, they could begin assembling applications around what they needed, however peculiar the requirements. An application serving five colleagues might be quite sufficient.&lt;/p&gt;
&lt;p&gt;It didn’t need a market.&lt;/p&gt;
&lt;p&gt;That is the change beneath the argument about &lt;strong&gt;&lt;em&gt;The Death of SaaS&lt;/em&gt;:&lt;/strong&gt; people are gaining the ability to decide how their software should work, rather than accepting those decisions as part of the price of admission.&lt;/p&gt;
&lt;p&gt;In October, an ambitious attempt to recreate Adobe’s professional applications brought that change in expectations into public view.&lt;/p&gt;
&lt;hr&gt;
&lt;h4 id=&quot;1-adobe-and-the-cost-of-learning-someone-else-s-software&quot;&gt;1. Adobe and the Cost of Learning Someone Else’s Software&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn-images-1.medium.com/max/1008/1*XFdnKUGUBV-PPd_2OYQJzw.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;The developer behind &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.reuters.com/business/us-software-stocks-scale-fresh-2026-highs-ai-disruption-worries-fade-2026-10-06/&quot;&gt;ArtCraft&lt;/a&gt; began publishing &lt;a rel=&quot;nofollow&quot; href=&quot;https://getartcraft.com/apps&quot;&gt;seven open-source&lt;/a&gt; applications in Rust, with substantial AI assistance. Their targets were familiar: Photoshop, Illustrator, Premiere Pro, Lightroom, Acrobat, After Effects, and InDesign. The repositories were public, and other developers could inspect the code and contribute.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn-images-1.medium.com/max/1008/1*bkJx4Oy8afLUsRM0L3gQQg.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Under the Reddit announcement, somebody asked for another program.&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;“Now do AutoCAD.”&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The developer responded with a link to &lt;a rel=&quot;nofollow&quot; href=&quot;https://getartcraft.com/apps/cadcraft&quot;&gt;CADCraft&lt;/a&gt;, an additional project already underway. [3]&lt;/p&gt;
&lt;p&gt;It’s easy to understand the excitement. Adobe’s software has become part of the working environment of designers and photographers, much as Autodesk’s products have shaped established CAD workflows.&lt;/p&gt;
&lt;p&gt;People spend years learning these systems. Training courses teach their interfaces, clients request particular file formats, and businesses accumulate work that becomes difficult to move elsewhere.&lt;/p&gt;
&lt;p&gt;The subscription is often the most visible expense. The time already invested in learning the software is harder to calculate.&lt;/p&gt;
&lt;p&gt;ArtCraft’s early applications, however, were a long way from matching the products they imitated. In an October 10 review, Steve Paris found that PhotoCraft couldn’t open his HEIC photographs and saved edited work only as JPG. FilmCraft struggled with basic trimming and produced unusable audio. [4]&lt;/p&gt;
&lt;p&gt;These are understandable limitations for an early experimental project attempting software that large engineering teams have spent decades refining.&lt;/p&gt;
&lt;p&gt;A hobbyist can afford to experiment with an unfinished editor; a professional has a client waiting for tomorrow’s revisions. Try explaining that the editable layers are gone, but hey, the toolbar looks just like Photoshop.&lt;/p&gt;
&lt;p&gt;The distinction matters in practical work, where an application has to preserve what people create. A designer leaving Adobe may still need to exchange files with clients who haven’t left.&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://www.techradar.com/pro/one-dev-is-recreating-7-adobe-apps-in-rust-for-free-so-i-tested-it-out&quot;&gt;In a July 2025 Reddit discussion&lt;/a&gt;, a designer described moving their small company away from Adobe after nearly twenty years of professional use. The responses showed how much previous work and established production habits could complicate a transition, even when people were unhappy with the subscription. [5]&lt;/p&gt;
&lt;p&gt;ArtCraft won’t make those dependencies disappear overnight. The developer’s description of the project as a clean-room recreation also remains a claim about the development approach rather than an independently established legal finding.&lt;/p&gt;
&lt;p&gt;But there is something peculiar about the assumption that an alternative must reproduce the original product in its entirety.&lt;/p&gt;
&lt;p&gt;A photographer may need a reliable RAW-processing workflow without the surrounding application ecosystem. A small business might require a handful of image operations performed consistently. Someone producing illustrations may care deeply about brush behavior while having no use for dozens of other features.&lt;/p&gt;
&lt;p&gt;An application developed for a vast customer base necessarily contains decisions made for people whose work bears little resemblance to yours.&lt;/p&gt;
&lt;p&gt;A faithful clone would inherit many of those choices, including the ones that were inconvenient in the first place.&lt;/p&gt;
&lt;p&gt;We have spent so many years adjusting ourselves to the shape of software that changing the tool itself sounds almost unreasonable, although people who work with physical tools would recognize the impulse immediately.&lt;/p&gt;
&lt;hr&gt;
&lt;h4 id=&quot;2-the-hammer-and-the-way-we-work&quot;&gt;2. The Hammer and the Way We Work&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn-images-1.medium.com/max/1008/1*P_-56ssFgTP6_Jqm9U9GSw.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;A carpenter can replace a worn hammer handle or choose a different weight for a particular job. Years of practice still matter, and the tool’s design has a considerable influence on the work. But modifying equipment to suit a particular task has always been part of the craft.&lt;/p&gt;
&lt;p&gt;Computer applications trained us into another relationship.&lt;/p&gt;
&lt;p&gt;We learned their terminology and remembered where their designers had placed the controls. A business might change its procedures because the software handled one sequence more conveniently than another.&lt;/p&gt;
&lt;p&gt;Employees became proficient at navigating features they seldom needed while finding elaborate workarounds for the few that were missing.&lt;/p&gt;
&lt;p&gt;Once everybody knew the procedure, the inconvenience became routine. Nobody wanted to disturb a system that had taken so long to learn.&lt;/p&gt;
&lt;p&gt;In July 2026, a construction-business operator described this frustration in a Reddit discussion. They had tried Buildertrend, a commercial construction-management platform, and found it bloated and cumbersome for the way their company operated. Rather than return to a costly subscription, they had spent about a month intermittently developing their own management software.&lt;/p&gt;
&lt;p&gt;The application was still taking shape. What they wanted from it was unmistakable:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;“It works how our business runs instead of us trying to adapt to the way they think we should do things.” [6]&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The parent Reddit post was removed, although the relevant comment remains visible.&lt;/p&gt;
&lt;p&gt;That is the experience I find more revealing than an ambitious claim about replacing an entire software company. The construction operator already knew how their business ran. Their problem was finding software that respected those practices.&lt;/p&gt;
&lt;p&gt;Other contributors in the discussion described similarly narrow approaches to construction administration, including scheduling through SMS so subcontractors could confirm appointments without adopting another application.&lt;/p&gt;
&lt;p&gt;Consider what disappears from the usual software-selection process. The business no longer needs to evaluate hundreds of capabilities it never requested. It can start from the tasks employees actually perform and add functions as those tasks require them.&lt;/p&gt;
&lt;p&gt;The resulting program could be unsuitable for almost every other construction company. Its owner has little reason to care.&lt;/p&gt;
&lt;p&gt;The ambition here is surprisingly modest: to have the right tool for the work already being done.&lt;/p&gt;
&lt;p&gt;And a great deal of the technical foundation for such tools has been available for years.&lt;/p&gt;
&lt;hr&gt;
&lt;h4 id=&quot;3-we-already-had-the-building-materials&quot;&gt;3. We Already Had the Building Materials&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn-images-1.medium.com/max/1008/1*ruOUka8Z_N1nKxgP4sXIQg.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Open-source software had been expanding access to advanced technical capabilities long before the present AI boom. People often discuss these projects as inexpensive substitutes for commercial applications, overlooking the substantial engineering they contain and the professional work already produced with them.&lt;/p&gt;
&lt;p&gt;The alternatives cover familiar software categories:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Microsoft 365 → LibreOffice / ONLYOFFICE.&lt;/strong&gt; Document editing, spreadsheets, and presentations.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Autodesk Maya → Blender.&lt;/strong&gt; 3D modeling, animation, rendering, and visual production.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;AutoCAD / Fusion → FreeCAD.&lt;/strong&gt; Parametric CAD and mechanical design.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Commercial antivirus suites → Windows Security / Bitdefender Free&lt;/strong&gt; Free baseline malware protection.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;TechSmith Snagit → ShareX/ OBS Studio.&lt;/strong&gt; Screen capture, desktop recording, and workflow automation.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Wondershare UniConverter → HandBrake/ FFmpeg.&lt;/strong&gt; Video conversion, compression, and media processing.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Adobe Premiere Pro / After Effects → DaVinci Resolve / Kdenlive&lt;/strong&gt; Video editing and production tools.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Adobe Lightroom → darktable/ RawTherapee.&lt;/strong&gt; RAW photography and image development.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Adobe Photoshop → GIMP/ Krita/ PhotoCraft&lt;/strong&gt; Image editing and digital painting; PhotoCraft remains experimental.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Adobe Illustrator → Inkscape/ VectorCraft.&lt;/strong&gt; Vector illustration and scalable graphics; VectorCraft remains experimental.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Some products in this list are free proprietary software rather than open source, and the applications vary in scope. They are alternatives for particular kinds of work, with different features and degrees of compatibility.&lt;/p&gt;
&lt;p&gt;But consider &lt;strong&gt;Blender&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn-images-1.medium.com/max/1008/1*B1eDDWzKkzPuk7F-vD7TmQ.jpeg&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;The animated feature &lt;em&gt;Flow&lt;/em&gt;, directed by Gints Zilbalodis, was produced using Blender and EEVEE.&lt;/p&gt;
&lt;p&gt;The film won the Academy Award for Best Animated Feature in 2025. Zilbalodis, who had previously worked with Maya, described adopting Blender because its rendering workflow gave him the control and speed he wanted for his production. [7]&lt;/p&gt;
&lt;p&gt;This is hardly an example of professionals settling for inadequate software because they couldn’t afford the real thing. The director chose a tool that suited the way he wanted to make a film.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;FFmpeg&lt;/strong&gt; provides a different illustration. Its media-processing capabilities can sit underneath a small application that handles a production team’s particular conversion requirements. The person constructing that application doesn’t need to develop a video codec. Much of the difficult engineering already exists.&lt;/p&gt;
&lt;p&gt;AI coding assistants can help people create the surrounding application: a small interface, a sequence of processing steps, perhaps an integration with another tool used in the same workflow.&lt;/p&gt;
&lt;p&gt;Open-source projects such as &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/abi/screenshot-to-code&quot;&gt;&lt;strong&gt;Screenshot-to-Code&lt;/strong&gt;&lt;/a&gt; push the starting point even closer to the user by generating interface code from visual examples. A screenshot is only part of an application, but it can give someone an initial structure to modify. [8]&lt;/p&gt;
&lt;p&gt;There is a long history behind this kind of customization. People built intricate applications in Excel, and platforms such as Airtable enabled teams to organize databases around their own activities well before AI coding assistants arrived.&lt;/p&gt;
&lt;p&gt;What is expanding now is the range of changes people can attempt through natural-language interaction with code. They can start with components created by skilled developers and shape their own application around those foundations.&lt;/p&gt;
&lt;p&gt;The benefit becomes especially clear when the intended audience is very small.&lt;/p&gt;
&lt;hr&gt;
&lt;h4 id=&quot;4-software-for-one&quot;&gt;4. Software for One&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn-images-1.medium.com/max/1008/1*YnFt-SOoXjNdTlmgLECptQ.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;In June 2026, a Reddit user asked which paid applications people had replaced by building their own with AI.&lt;/p&gt;
&lt;p&gt;The replies brought that possibility down to the scale of everyday work. People had grown tired of accommodating software that never quite fitted their needs, and some had begun changing the tools themselves.&lt;/p&gt;
&lt;p&gt;One reply described taking a screenshot of a Monday.com board and asking Bolt to build something similar. Within hours, the user reported having a working application they named Tuesday.&lt;/p&gt;
&lt;p&gt;They subsequently tailored it to their team’s habits, including synchronization with a shared Google Calendar, and claimed savings of approximately $800 per month in Monday.com licenses. Those figures come from the user’s account rather than an independent audit. [9]&lt;/p&gt;
&lt;p&gt;The name Tuesday has a kind of accidental charm. The user wasn’t trying to launch a rival to Monday.com. Their team needed a particular way to manage work, and the person building the application could decide which functions deserved attention.&lt;/p&gt;
&lt;p&gt;Elsewhere in the same discussion, a user described making a budgeting application that relied on manually entered transactions. Another participant explained why manual entry could be desirable: recording expenses deliberately was part of how they kept track of their spending. An automatic bank import, usually treated as an obvious improvement, would remove part of the experience they valued. [9]&lt;/p&gt;
&lt;p&gt;This is the sort of disagreement commercial software designers must routinely reconcile. A feature can make the application better for one customer while making it less suitable for another.&lt;/p&gt;
&lt;p&gt;Software developed for a single person can accept peculiar requirements without taking a vote.&lt;/p&gt;
&lt;p&gt;A further Reddit contributor described creating their own document-management system after a product they had used for fifteen years was discontinued following an acquisition. The purchaser was attempting to move customers onto a more expensive subscription service.&lt;/p&gt;
&lt;p&gt;The user reported building an alternative that could work with a collection approaching 100,000 documents, despite having little recent programming experience. This remains a self-reported account, but the motive is hard to miss: they wanted to retain control over work that another company’s acquisition had disrupted. [9]&lt;/p&gt;
&lt;p&gt;Similar cases have appeared outside developer communities. In January, TechCrunch reported on people making &lt;em&gt;micro-apps&lt;/em&gt; for themselves and small groups.&lt;/p&gt;
&lt;p&gt;Rebecca Yu created &lt;a rel=&quot;nofollow&quot; href=&quot;https://techcrunch.com/2026/01/16/the-rise-of-micro-apps-non-developers-are-writing-apps-instead-of-buying-them/&quot;&gt;Where2Eat&lt;/a&gt; to help her friends choose restaurants. Other applications were built for household tasks and temporary needs that would never have justified a conventional commercial product. [10]&lt;/p&gt;
&lt;p&gt;A conventional software company must consider market size. The owner of a personal application can ask whether the software serves the people who will actually use it.&lt;/p&gt;
&lt;p&gt;That difference changes what counts as a successful product.&lt;/p&gt;
&lt;p&gt;If a useful tool lasts six months and is then replaced because its owner’s requirements have changed, it may have fulfilled its purpose. If it contains only four features, those four can receive attention that a large general-purpose application would have distributed across far more capabilities.&lt;/p&gt;
&lt;p&gt;More importantly, the user can participate in deciding how those features work.&lt;/p&gt;
&lt;p&gt;Research into generative and malleable interfaces has begun exploring this relationship more formally. &lt;a rel=&quot;nofollow&quot; href=&quot;https://doi.org/10.1145/3706598.3713285&quot;&gt;Yining Cao, Peiling Jiang, and Haijun Xia&lt;/a&gt; examined interfaces organized around task-driven data models, allowing users to modify generated applications through natural language and direct interaction. Their CHI 2025 work explores how applications can evolve alongside the requirements of the people using them. [11]&lt;/p&gt;
&lt;p&gt;An application can therefore become something less fixed than the product delivered at the moment of purchase. Its structure can reflect the work as the work changes.&lt;/p&gt;
&lt;p&gt;This experience can seem unfamiliar because professional software has been distributed through standardized applications for so long.&lt;/p&gt;
&lt;p&gt;Historically, however, the movement of technical knowledge toward a wider population is hardly unprecedented.&lt;/p&gt;
&lt;hr&gt;
&lt;h4 id=&quot;5-gutenberg-the-encyclop-die-and-the-open-web&quot;&gt;5. Gutenberg, the Encyclopédie, and the Open Web&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn-images-1.medium.com/max/1008/1*eDvx6NgwDlbN63ADC4u9Ew.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Around the middle of the fifteenth century, Johannes Gutenberg helped transform book production in Europe.&lt;/p&gt;
&lt;p&gt;Printing with movable metal type was already known in Korea, as demonstrated by the surviving &lt;em&gt;Jikji&lt;/em&gt; of 1377, but Gutenberg’s European printing system made the repeated production of books more efficient within its own historical environment.&lt;/p&gt;
&lt;p&gt;The Gutenberg Bible, printed around 1454–1455, remains one of the defining artifacts of that change. [12][13]&lt;/p&gt;
&lt;p&gt;Books still required literacy, and access was far from universal. Yet the economics of making and circulating written material had changed. Knowledge that depended heavily on laborious manuscript copying could now travel through a growing printing industry.&lt;/p&gt;
&lt;p&gt;Three centuries later, Denis Diderot and Jean le Rond d’Alembert undertook another revealing project.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://coderlegion.com/?qa=blob&amp;amp;qa_blobid=15969963876337083811&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Published between 1751 and 1772, the &lt;em&gt;Encyclopédie&lt;/em&gt; attempted to gather knowledge across the sciences and the mechanical arts. Its enormous collection of illustrations documented machinery and working processes in remarkable detail.&lt;/p&gt;
&lt;p&gt;The University of Chicago’s ARTFL project records seventeen volumes of text and eleven volumes of plates in the original publication. [14]&lt;/p&gt;
&lt;p&gt;The decision to document practical trades was particularly significant.&lt;/p&gt;
&lt;p&gt;At the time, much technical knowledge belonged to the working traditions of individual craftspeople and workshops. Some manufacturing practices were guarded closely. Diderot wanted to put these processes into words and pictures so that people outside the original workshops could study them.&lt;/p&gt;
&lt;p&gt;John R. Pannabecker’s historical research describes how the &lt;em&gt;Encyclopédie&lt;/em&gt; helped shift the mechanical arts from knowledge embedded largely in working communities toward knowledge represented systematically in published materials. [15]&lt;/p&gt;
&lt;p&gt;An engraving couldn’t make its reader a master craftsperson. There are forms of judgment that cannot be transferred completely through a diagram. But someone who had never entered a particular workshop could begin to understand machinery and production methods previously difficult to examine.&lt;/p&gt;
&lt;p&gt;That is a striking antecedent to the open-source idea: useful technical knowledge becomes available for study beyond the people and institutions that originally developed it.&lt;/p&gt;
&lt;p&gt;Then came the World Wide Web.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn-images-1.medium.com/max/1008/1*gL1c2qh2jvHT0kRat3zr_Q.jpeg&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Tim Berners-Lee developed the Web at CERN beginning in 1989 to help researchers exchange information. On April 30, 1993, CERN released core Web software into the public domain. That decision made the basic technology available for others to use and improve without paying CERN for permission. [16]&lt;/p&gt;
&lt;p&gt;The Web grew on the foundation of an internet that already existed, and its expansion depended on browsers and technical contributions from many organizations. CERN’s own history emphasizes how openly available software and accessible browsers helped the Web spread beyond its original scientific setting.&lt;/p&gt;
&lt;p&gt;One detail is particularly relevant today: Berners-Lee’s original WorldWideWeb browser was also an editor. The early vision included the ability to create and change pages, even though most people eventually came to know the Web primarily as something they browsed. [16]&lt;/p&gt;
&lt;p&gt;For ordinary users, the arrival of accessible publishing tools changed who could put information online. A person no longer needed to own a newspaper or a broadcasting network to publish something others could read.&lt;/p&gt;
&lt;p&gt;There is a thread running through these episodes, though each belongs to a different technological world.&lt;/p&gt;
&lt;p&gt;Printing altered the reproduction of written material. The &lt;em&gt;Encyclopédie&lt;/em&gt; tried to bring practical technical knowledge beyond the workshops where it had accumulated. The open Web expanded opportunities to publish and exchange information across institutional boundaries.&lt;/p&gt;
&lt;p&gt;The consequences extended well beyond the industries that created the original technologies.&lt;/p&gt;
&lt;p&gt;AI-assisted software development is opening another part of that history. A person who understands a particular job can increasingly participate in making the software used to perform it.&lt;/p&gt;
&lt;p&gt;The technical foundations are often already available through open-source projects. AI helps bridge some of the distance between understanding a problem and constructing a working tool.&lt;/p&gt;
&lt;p&gt;The possibilities remain uneven. Some applications are easy to modify, while others embody extraordinary engineering complexity. A sophisticated CAD system cannot be conjured into professional reliability merely by reproducing its interface.&lt;/p&gt;
&lt;p&gt;Yet people are gaining practical ways to influence software’s design without first becoming specialists in every underlying technology.&lt;/p&gt;
&lt;p&gt;That changes the relationship between a person and the tools they depend on.&lt;/p&gt;
&lt;hr&gt;
&lt;h4 id=&quot;final-thoughts-the-choice-we-didn-t-have&quot;&gt;Final Thoughts — The Choice We Didn’t Have&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn-images-1.medium.com/max/1008/1*3v131LrU-TaWnpSExZZs8w.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;The traditional software business was built around producing applications that large numbers of people could use. Standardization brought real benefits, but it also required customers to accept decisions made for a much wider population.&lt;/p&gt;
&lt;p&gt;If a function was missing, users searched for another product or waited. If a workflow didn’t fit, they learned a workaround. Years of practice made those compromises familiar, while subscriptions kept access to the software tied to decisions made elsewhere.&lt;/p&gt;
&lt;p&gt;Now people are beginning to make different choices. A construction operator is developing software around the way his company already works. A small team has built Tuesday because Monday.com carried more than it needed. Someone with years of documents has attempted to replace a system whose owner wanted to move them onto another subscription.&lt;/p&gt;
&lt;p&gt;Meanwhile, open-source projects continue to make advanced technology available for reuse, and AI coding tools give more people a way to assemble that technology around their own requirements.&lt;/p&gt;
&lt;p&gt;Services that genuinely help people will continue to find customers. A user can choose to pay for infrastructure or collaboration without surrendering every decision about the application that sits above them.&lt;/p&gt;
&lt;p&gt;The part of SaaS losing its hold is the assumption that useful software must arrive as a standardized package whose structure the customer has little power to change.&lt;/p&gt;
&lt;p&gt;That brings me back to the ArtCraft announcement.&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;“Now do AutoCAD.”&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The request was for another familiar professional application. After reading the accounts of people making software for their own work, though, I wonder what that person actually needed AutoCAD to do.&lt;/p&gt;
&lt;p&gt;Perhaps the answer wouldn’t require another enormous application. Perhaps it would require something smaller, built around the particular design problem sitting on their desk.&lt;/p&gt;
&lt;p&gt;For a long time, that distinction hardly mattered. Most users had to choose among the applications somebody else was willing to build.&lt;/p&gt;
&lt;p&gt;They are beginning to have a say in the tools themselves.&lt;/p&gt;
&lt;hr&gt;
&lt;h4 id=&quot;references&quot;&gt;References&lt;/h4&gt;
&lt;p&gt;[1] Reuters, “Selloff wipes out nearly $1 trillion from software and services stocks as investors debate AI’s existential threat,” &lt;em&gt;Reuters&lt;/em&gt;, February 4, 2026.&lt;/p&gt;
&lt;p&gt;[2] Reuters, “US software stocks scale fresh 2026 highs as AI disruption worries fade,” &lt;em&gt;Reuters&lt;/em&gt;, October 6, 2026.&lt;/p&gt;
&lt;p&gt;[3] ArtCraft developer, “I’ve created Open Source 100% Rust clean-room replacements for 7 of Adobe’s apps,” &lt;em&gt;Reddit, r/SaaS&lt;/em&gt;, October 2026.&lt;/p&gt;
&lt;p&gt;[4] Steve Paris, “One dev is recreating 7 Adobe apps in Rust for free — so I tested it out,” &lt;em&gt;TechRadar&lt;/em&gt;, October 10, 2026.&lt;/p&gt;
&lt;p&gt;[5] r/graphic_design contributor, “Why I’m (we’re) leaving Adobe,” &lt;em&gt;Reddit, r/graphic_design&lt;/em&gt;, July 2025.&lt;/p&gt;
&lt;p&gt;[6] r/ClaudeAI commenter, “Construction-management discussion, including Buildertrend replacement,” &lt;em&gt;Reddit, r/ClaudeAI&lt;/em&gt;, July 12, 2026. Parent post removed; relevant comment remains accessible.&lt;/p&gt;
&lt;p&gt;[7] Francesco Siddi, “Making Flow — Interview with director Gints Zilbalodis,” &lt;em&gt;Blender&lt;/em&gt;, January 22, 2025; updated to reflect subsequent awards.&lt;/p&gt;
&lt;p&gt;[8] Abi Raja and contributors, “screenshot-to-code,” &lt;em&gt;GitHub repository&lt;/em&gt;, accessed October 2026.&lt;/p&gt;
&lt;p&gt;[9] r/ClaudeAI contributors, “What paid apps have you ditched by vibe coding a replacement?,” &lt;em&gt;Reddit, r/ClaudeAI&lt;/em&gt;, June 2026 and subsequent comments.&lt;/p&gt;
&lt;p&gt;[10] Dominic-Madori Davis, “The rise of ‘micro’ apps: non-developers are writing apps instead of buying them,” &lt;em&gt;TechCrunch&lt;/em&gt;, January 16, 2026.&lt;/p&gt;
&lt;p&gt;[11] Yining Cao, Peiling Jiang, and Haijun Xia, “Generative and Malleable User Interfaces with Generative and Evolving Task-Driven Data Model,” &lt;em&gt;Proceedings of the 2025 CHI Conference on Human Factors in Computing Systems&lt;/em&gt;, Article 686, 2025.&lt;/p&gt;
&lt;p&gt;[12] UNESCO, “Baegun hwasang chorok buljo jikji simche yojeol (vol. II),” &lt;em&gt;Memory of the World Register&lt;/em&gt;, 2001.&lt;/p&gt;
&lt;p&gt;[13] Library of Congress, “The Gutenberg Bible at the Library of Congress: Introduction,” &lt;em&gt;Library of Congress Research Guide&lt;/em&gt;, undated.&lt;/p&gt;
&lt;p&gt;[14] University of Chicago, “The ARTFL Encyclopédie,” &lt;em&gt;ARTFL Encyclopédie Project&lt;/em&gt;, institutional digital edition.&lt;/p&gt;
&lt;p&gt;[15] John R. Pannabecker, “Diderot, the Mechanical Arts, and the Encyclopédie: In Search of the Heritage of Technology Education,” &lt;em&gt;Journal of Technology Education&lt;/em&gt;, 6(1), Fall 1994.&lt;/p&gt;
&lt;p&gt;[16] CERN, “A Short History of the Web,” &lt;em&gt;CERN institutional history&lt;/em&gt;, undated.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/30898/the-death-of-saas-what-happens-when-people-can-make-software-fit-their-lives</guid>
<pubDate>Tue, 13 Oct 2026 06:35:19 +0000</pubDate>
</item>
<item>
<title>GraphQL Solved Facebook's Problem. Does It Solve Yours?</title>
<link>https://coderlegion.com/30860/graphql-solved-facebooks-problem-does-it-solve-yours</link>
<description>&lt;p&gt;GraphQL divides teams, and I don't claim the last word on it. This post is my research into my own experience with GraphQL and into decisions I'm still making. I'm not calling anyone out or dismissing what teams have built with it. I'm asking for caution before an organization commits to a tool its product and customers may not need.&lt;/p&gt;
&lt;p&gt;Facebook's 2015 engineering post introducing GraphQL explained where the idea came from: &quot;We don't think of data in terms of resource URLs, secondary keys, or join tables; we think about it in terms of a graph of objects.&quot; The line most people quote from that post is that &quot;the shape of the returned data is determined entirely by the client's query.&quot; That's true, but it's a consequence of the unusually complex and deep graph, not the reason GraphQL exists. No server team can design an endpoint for every path through a graph that size, across every app version reading it, so the client has to write the query.&lt;/p&gt;
&lt;p&gt;GraphQL solved Facebook's problem, and the burden of proof sits with everyone importing it to show that it solves theirs. I don't think most teams can meet it.&lt;/p&gt;
&lt;p&gt;Most of what teams adopt GraphQL for is available without it. What's left is a client composing its own traversal of a graph the server can't predict. GraphQL's own best practices steer private APIs away from even that, by registering every query ahead of time. Client teams can still write their own queries before release, but planning across teams, or a backend for their screens, usually gives them the same control. That stops being true only when a product's screens take so many paths through its graph, and change them so often, that keeping an endpoint for each costs more than securing a query language. Facebook's did.&lt;/p&gt;
&lt;h3 id=&quot;most-of-graphql-s-benefits-aren-t-graphql-s&quot;&gt;Most of GraphQL's Benefits Aren't GraphQL's&lt;/h3&gt;
&lt;p&gt;The case for adopting GraphQL usually arrives as a bundle: typed contracts, payloads shaped to each screen, flexible filtering, declarative authorization, one endpoint over many services, and frontends that don't wait on backends. For each, the test is whether the client must compose the operation. Most don't.&lt;/p&gt;
&lt;h4 id=&quot;typed-contracts-and-codegen-come-from-contract-first-apis&quot;&gt;Typed Contracts and Codegen Come From Contract-First APIs&lt;/h4&gt;
&lt;p&gt;A GraphQL schema gives clients types and generated code, as an OpenAPI document does for an HTTP API and a Protocol Buffers definition does for gRPC. What differs is what the contract commits the service to.&lt;/p&gt;
&lt;p&gt;In contract-first development, the team designs and publishes each operation on purpose, so the contract states what the service supports. A GraphQL schema publishes types and entry points instead. The operations that matter, meaning which fields combine at what depth, get written later by whoever calls it. Unless every query is registered in advance, the schema commits the service to every operation its types and limits allow.&lt;/p&gt;
&lt;h4 id=&quot;filtering-and-sorting-are-the-server-s-choice-not-a-query-language&quot;&gt;Filtering and Sorting Are the Server's Choice, Not a Query Language&lt;/h4&gt;
&lt;p&gt;Hot Chocolate, a GraphQL server for .NET and the best-designed one I've used, adds filtering, sorting, and paging to a list with an attribute or two. But each attribute is the server choosing which lists callers may filter, on which fields, and how far they may page.&lt;/p&gt;
&lt;p&gt;An endpoint makes the same choices about its query parameters. OData, an OASIS standard for REST APIs, standardizes those parameters as &lt;code&gt;$filter&lt;/code&gt;, &lt;code&gt;$orderby&lt;/code&gt;, &lt;code&gt;$top&lt;/code&gt;, and &lt;code&gt;$skip&lt;/code&gt;, and ASP.NET Core lets each endpoint allow only the ones it chooses.&lt;/p&gt;
&lt;h4 id=&quot;field-level-authorization-is-a-cost-of-client-written-operations&quot;&gt;Field-Level Authorization Is a Cost of Client-Written Operations&lt;/h4&gt;
&lt;p&gt;The policy engine behind GraphQL authorization isn't GraphQL's, and the GraphQL Foundation tells production codebases to &quot;delegate authorization logic to the business logic layer.&quot; Hot Chocolate's &lt;code&gt;[Authorize]&lt;/code&gt; attribute, for one, runs the same roles and policies ASP.NET Core applies to an endpoint. What GraphQL changes is where the check has to sit.&lt;/p&gt;
&lt;p&gt;An endpoint is a designed operation, so its code knows what the caller is doing and can apply the rule that fits. In GraphQL, a read can reach a field through any path the schema's types allow. Only root mutations are designed like endpoints.&lt;/p&gt;
&lt;p&gt;Say a support agent may see a customer's email while working that customer's ticket, but not in a sales report the agent can also open. In GraphQL, the same &lt;code&gt;Customer.email&lt;/code&gt; field sits under both. A check on the field alone can confirm the agent has an open ticket with that customer, but it can't tell whether the request came through the ticket or the report, so the email shows in both.&lt;/p&gt;
&lt;p&gt;A ticket-specific customer type would fix that, as a ticket DTO would in REST. Either fix works, but an endpoint exposes only the paths its designer wrote, while a schema must be right for every path its types allow.&lt;/p&gt;
&lt;h4 id=&quot;one-endpoint-over-many-services-fights-the-teams-behind-it&quot;&gt;One Endpoint Over Many Services Fights the Teams Behind It&lt;/h4&gt;
&lt;p&gt;Apollo Federation and Hot Chocolate's Fusion put one GraphQL endpoint in front of several services, often for teams with predictable queries. Each service publishes a subgraph, and a gateway composes them into one schema and splits each query into requests to the services that own the fields.&lt;/p&gt;
&lt;p&gt;The stitching a client would have done still happens, in a query planner where the fetches are harder to see. Apollo's query-plan documentation says the router runs subgraph fetches in sequence &quot;whenever one subgraph's response depends on data that first must be returned by another subgraph.&quot;&lt;/p&gt;
&lt;p&gt;A product built around bounded contexts gives each team authority over its own model. A federated schema asks for the opposite. Every team contributes types to one shared graph, extends other teams' entities, and passes a composition check that another team's conflicting change can fail. The bounded contexts are still there, but the schema pretends they aren't.&lt;/p&gt;
&lt;p&gt;A backend-for-frontend looks like the same facade but points the other way. Sam Newman describes it as a backend dedicated to one user experience and owned by the team building that interface. It calls each domain's published contract like any client, so each domain keeps its own schedule and authority. Its fan-out sits in code its team owns and can tune, and it shapes each response to its screens, so clients don't over-fetch.&lt;/p&gt;
&lt;p&gt;Members of my team once proposed a single query endpoint over our services, and I rejected the prototype we built. It added too much latency for our data-heavy, highly responsive web app, and it fought our caching. Each domain, and often each kind of record, had its own TTL. One response mixing those lifetimes defeated a request-level cache, and caching per source meant streaming every domain's invalidations into a gateway no domain team owned. What worked was each layer caching the data it owned.&lt;/p&gt;
&lt;p&gt;Latency isn't the lasting case against federation, since a backend-for-frontend's fan-out pays the same network hops. Ownership is. One product doesn't prove a rule, and a product without our volatile data, varied cache lifetimes, and team per bounded context might find a federated graph harmless.&lt;/p&gt;
&lt;h4 id=&quot;fragment-colocation-needs-graphql-but-solves-a-team-problem&quot;&gt;Fragment Colocation Needs GraphQL but Solves a Team Problem&lt;/h4&gt;
&lt;p&gt;Relay, Meta's GraphQL client for React, is built around colocated fragments. Each component declares the data it needs, and the compiler nests those fragments into the queries a screen sends.&lt;/p&gt;
&lt;p&gt;GraphQL's defenders build on this, arguing that even when every query is registered in advance, the unpredictability hasn't gone away. It has moved from runtime to development time, because the server team can't predict what the frontend will need next month.&lt;/p&gt;
&lt;p&gt;Colocation buys frontend convenience, and it pays for it by giving up intentional contracts. The need it serves, a frontend team that can't wait on a backend team, is a planning failure before it's a technical one. Most of a screen's data needs show in its design before it's built, and leads who set priorities across teams can schedule its endpoint in the same release.&lt;/p&gt;
&lt;p&gt;So this case for the technology rests on one premise, that the teams can't plan together, and that premise is a needle holding up a foundation.&lt;/p&gt;
&lt;p&gt;Even good planning can't always keep pace, as when a screen changes mid-build or an experiment varies it. Then the frontend team needs to own its operations, and a backend-for-frontend can give it that as well as colocation can.&lt;/p&gt;
&lt;p&gt;Say a listing card on web, iOS, and Android gains its seller's rating. With colocation, each client edits a fragment and its build registers a new document. With a backend-for-frontend, each client team adds the field to its own backend's response in the same release. Neither waits on a domain team unless the rating doesn't exist yet, and then both do.&lt;/p&gt;
&lt;p&gt;Each fix has a price. A backend-for-frontend needs a backend per platform, each with its own pipeline and on-call. Colocation avoids that and saves an endpoint edit per backend. But saved time isn't a benefit until it's weighed against what it leaves behind. Colocation leaves an operation no server code was written for, even when a frontend team runs GraphQL as its own backend-for-frontend. That operation carries the authorization problem above. Only registered paths run, but no server team designed them. A designed endpoint settles that check in code its team owns.&lt;/p&gt;
&lt;p&gt;Adopting GraphQL because it removes the wait between teams, and then collecting reasons it fits, invites confirmation bias. Choosing it on purpose means proving its security, performance, stability, and testability against the problem at hand. Slowing down to plan together is how a product speeds up.&lt;/p&gt;
&lt;h3 id=&quot;what-s-left-is-a-graph-nobody-can-predict&quot;&gt;What's Left Is a Graph Nobody Can Predict&lt;/h3&gt;
&lt;p&gt;GraphQL started in 2012, when Facebook began rebuilding its iOS and Android apps. Native apps needed &quot;an API data version of News Feed,&quot; and where a REST service &quot;may require multiple round-trips,&quot; &quot;GraphQL naturally follows relationships between objects.&quot;&lt;/p&gt;
&lt;p&gt;Every item in a feed links to its author, its comments, the people who commented, and the groups and pages it came from. A reader can follow any of those links to the next view. That data also changes constantly. A designed feed endpoint would have to return every relationship any screen on any client version might open next, or come in a variant per screen and version. Facebook meets the burden of proof, and I agree with its conclusion.&lt;/p&gt;
&lt;h4 id=&quot;rest-follows-one-link-at-a-time-graphql-follows-many&quot;&gt;REST Follows One Link at a Time, GraphQL Follows Many&lt;/h4&gt;
&lt;p&gt;The round trips Facebook described are what REST does by design. Roy Fielding's dissertation defined REST around hypermedia, where each response carries links to the client's next steps and the client follows one per request. Fielding said the REST interface is &quot;optimizing for the common case of the Web, but resulting in an interface that is not optimal for other forms of architectural interaction.&quot; A feed is one of those other forms.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;Screen shape &lt;/th&gt;&lt;th&gt;What fits &lt;/th&gt;&lt;th&gt;Why&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;One view at a time, with links to the next &lt;/td&gt;&lt;td&gt;Hypermedia REST, as Fielding defined it &lt;/td&gt;&lt;td&gt;One request per link&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Many items per view, each linked to many relationships, in a large graph that changes constantly &lt;/td&gt;&lt;td&gt;GraphQL &lt;/td&gt;&lt;td&gt;One request states the whole traversal&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Known views taking a few planned paths through data that changes slowly enough to cache &lt;/td&gt;&lt;td&gt;Designed endpoints &lt;/td&gt;&lt;td&gt;The server ships each view's shape and caches what it can&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Most APIs called REST today sit in the third row. They're RPC, designed operations over HTTP.&lt;/p&gt;
&lt;p&gt;Each relationship a list item links into needs its own designed operation. For a first-party product, two questions test, screen by screen, whether its operations outrun hand-written endpoints:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Does each item in a list summarize itself and open one detail view, rather than link into many of its relationships?&lt;/li&gt;
&lt;li&gt;Does the data behind those views change slowly enough for a designed endpoint to serve and cache it?&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;If both answers are yes for most screens, hand-written endpoints can keep pace. A marketplace's listing and detail screens pass both, since each card opens one listing whose text and photos are read far more often than they change.&lt;/p&gt;
&lt;p&gt;Volatile data hurts a designed endpoint only when items also link into many relationships, because everything returned just in case must then be fetched fresh.&lt;/p&gt;
&lt;h4 id=&quot;github-agents-and-graph-databases-face-unpredictable-queries&quot;&gt;GitHub, Agents, and Graph Databases Face Unpredictable Queries&lt;/h4&gt;
&lt;p&gt;GitHub's repositories, issues, and pull requests form a graph that third-party callers traverse in ways GitHub can't predict, so no screen test applies. Over a graph database, GraphQL is a thin layer, and Neo4j's GraphQL Library compiles each operation into &quot;a single Cypher query which is executed against the database.&quot;&lt;/p&gt;
&lt;p&gt;AI agents compose queries as they go, which should make them GraphQL's best fit, yet Anthropic and Apollo both point them toward designed tools. Anthropic's agent-tool guidance recommends &quot;a few thoughtful tools targeting specific high-impact workflows.&quot; Apollo's Model Context Protocol server, which exposes GraphQL as agent tools, offers an open &lt;code&gt;execute&lt;/code&gt; tool but steers developers toward governing what agents reach &quot;by adopting predefined persisted queries.&quot; Predefining an agent's queries gives up the reason to expose a graph to it, and those operations could just as well be RPC endpoints.&lt;/p&gt;
&lt;h4 id=&quot;open-queries-can-be-secured-without-a-registry&quot;&gt;Open Queries Can Be Secured Without a Registry&lt;/h4&gt;
&lt;p&gt;These cases need open queries, and a server that accepts them accepts them from anyone, because the endpoint shows in the browser's network tab and the schema is often one introspection query away. A query like &lt;code&gt;users(first: 100) { friends(first: 100) { friends(first: 100) { name } } }&lt;/code&gt; asks for a million records. Aliases let one request call a login mutation a hundred times, and a per-request rate limiter counts that as one attempt, as a PortSwigger lab shows.&lt;/p&gt;
&lt;p&gt;The GraphQL Foundation's security guidance recommends limiting query depth, applying &quot;a separate smaller limit to how deeply lists can be nested,&quot; capping operations per batch, and restricting aliases. Cost analysis prices the rest. It weights each field and charges for what a query could return rather than for the request that carried it, so a hundred aliased logins cost a hundred mutations.&lt;/p&gt;
&lt;p&gt;GitHub caps a call at 500,000 nodes and meters each user at 5,000 points an hour, scored by the connections a query could traverse. The price is a cost model the server tunes field by field as the schema grows.&lt;/p&gt;
&lt;h3 id=&quot;graphql-s-own-best-practices-steer-private-apis-away-from-the-point&quot;&gt;GraphQL's Own Best Practices Steer Private APIs Away From the Point&lt;/h3&gt;
&lt;h4 id=&quot;trusted-documents-are-for-first-party-apis-only&quot;&gt;Trusted Documents Are for First-Party APIs Only&lt;/h4&gt;
&lt;p&gt;For private APIs, GraphQL's own guidance avoids tuning a cost model. It refuses any query the server hasn't seen. With persisted queries, the client's operations are hashed and registered at build time, and in production the client sends only the hash. Relay, Hot Chocolate's &lt;code&gt;OnlyAllowPersistedDocuments&lt;/code&gt; option, Apollo's safelisting, and the GraphQL Foundation's &quot;trusted documents&quot; describe the same arrangement.&lt;/p&gt;
&lt;p&gt;Benjie Gillam, a GraphQL Working Group contributor, calls an allowlist &quot;very much a best practice&quot; for the &quot;vast majority of GraphQL users.&quot; The GraphQL Foundation adds that &quot;trusted documents can't be used for public APIs.&quot; So for a private API, the recommended practice gives up open queries and leaves client teams composing operations at build time.&lt;/p&gt;
&lt;h4 id=&quot;serving-partners-brings-back-the-cost-model&quot;&gt;Serving Partners Brings Back the Cost Model&lt;/h4&gt;
&lt;p&gt;In my experience, what starts private often has to interoperate later, with a partner integration, another department, or a customer who wants their data. Some of those consumers expect HTTP conventions, such as per-resource URLs and HTTP caching. For every GraphQL endpoint I built, I ended up needing a more RESTful one beside it.&lt;/p&gt;
&lt;p&gt;A designed contract can often open to outsiders behind auth and rate limits, because the server chose each operation and parameter, bounding its cost. The locked-down GraphQL API can't serve them as built. Registering operations for partners means the server team writes each one anyway. A gateway that translates for partners only moves those endpoints into infrastructure no domain team owns. Opening the API, or adding an open surface beside it, brings back the cost model the registry avoided.&lt;/p&gt;
&lt;h4 id=&quot;persisted-queries-carry-the-costs-of-both-models&quot;&gt;Persisted Queries Carry the Costs of Both Models&lt;/h4&gt;
&lt;p&gt;Under persisted queries, the server stores and can refuse each document. It doesn't choose which fields combine in a request. The client gives up runtime freedom, and the server gives up design.&lt;/p&gt;
&lt;p&gt;Both teams run the registry. Its clearest advantage is that it ties each field to the builds that read it, which makes deprecation safe, especially with long-lived mobile apps. A backend-for-frontend matches that only by keeping each release's response version until its last app build retires.&lt;/p&gt;
&lt;p&gt;Registration in CI keeps attackers' queries out, but not a teammate's costly one. No server author designed that document's field combination, so catching its cost means reviewing or scoring each document at registration.&lt;/p&gt;
&lt;h4 id=&quot;a-monorepo-makes-the-registry-cheaper&quot;&gt;A Monorepo Makes the Registry Cheaper&lt;/h4&gt;
&lt;p&gt;Gillam's write-up says the allowlist practice &quot;has been used within Facebook since before GraphQL was open sourced.&quot; So Facebook did know its queries before they ran, but only because its client build generated the list rather than a team writing it.&lt;/p&gt;
&lt;p&gt;Facebook's code also lives largely in one repository, which Meta's post on its Sapling source control system puts at &quot;tens of millions of files.&quot; Meta hasn't said its persisted queries depend on that, but I read them as benefiting from it. When client, compiler, and registry change in one commit, the hash is a detail of one build rather than a contract between two teams.&lt;/p&gt;
&lt;p&gt;Where client and server ship from separate repositories, the registry becomes that contract. Without a graph like Facebook's, keeping a query language only to lock it down is forcing a square block into a round hole.&lt;/p&gt;
&lt;h3 id=&quot;most-teams-don-t-need-graphql&quot;&gt;Most Teams Don't Need GraphQL&lt;/h3&gt;
&lt;p&gt;Most teams adopting GraphQL don't have a graph nobody can predict. Their screens follow known paths, and designed endpoints give them what they wanted from GraphQL. For those teams, the server should own each operation, and the teams should plan together instead of building around each other.&lt;/p&gt;
&lt;p&gt;GraphQL earns its place where nobody can predict the queries. That covers public graph APIs like GitHub's, agents whose tasks can't be listed in advance, graph databases, and graphs like Facebook's, whose screens take too many changing paths to keep an endpoint for each. Persisted queries fit only that last case. Anywhere else, a team that locks GraphQL down has already listed its queries and keeps a query language only to compose them at build time.&lt;/p&gt;
&lt;p&gt;None of this says you can't use GraphQL, or that trying it was a mistake. It says to prove you have Facebook's problem before you take on Facebook's solution.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/30860/graphql-solved-facebooks-problem-does-it-solve-yours</guid>
<pubDate>Mon, 12 Oct 2026 21:00:21 +0000</pubDate>
</item>
<item>
<title>Jev AI Explained: A Single-Pass &quot;Decision Model&quot; for the Tasks LLMs Are Overkill For</title>
<link>https://coderlegion.com/30834/jev-ai-explained-a-single-pass-decision-model-for-the-tasks-llms-are-overkill-for</link>
<description>&lt;p&gt;Look at where LLM calls actually go in a typical production system and a pattern shows up fast. A big share of them are not conversations or content generation. They are decisions: which queue does this ticket belong to, how risky is this action, is this document relevant. Each one is a pick-from-a-list problem, and each one is being solved with an expensive, slow, text-generating model.&lt;/p&gt;
&lt;p&gt;Jev, released in September 2026 by TypeSafe AI, targets exactly that. It is a decision model, not a language model. It cannot write text, but it can answer structured questions quickly and attach a calibrated confidence score to every answer.&lt;/p&gt;
&lt;h3 id=&quot;why-the-architecture-matters&quot;&gt;Why the Architecture Matters&lt;/h3&gt;
&lt;p&gt;An autoregressive LLM produces output token by token, so even a one-word label requires a chain of sequential forward passes. That chain is the main source of latency and cost.&lt;/p&gt;
&lt;p&gt;Jev is non-autoregressive. It reads the input once and emits a typed result in a single forward pass. That removes a whole class of problems at the source: no malformed JSON, no hallucinated fields, no retry logic for broken output. In practice, responses land in roughly 70 to 500 milliseconds, versus multiple seconds for a generative model on the same job.&lt;/p&gt;
&lt;p&gt;The constraint is obvious: Jev only evaluates structured decisions. No free text, no multi-step reasoning, no arithmetic. &lt;/p&gt;
&lt;h3 id=&quot;the-three-primitives&quot;&gt;The Three Primitives&lt;/h3&gt;
&lt;p&gt;Every request is composed from three question types:&lt;/p&gt;
&lt;p&gt;Choice selects one label from a fixed set, for example routing a ticket to billing, technical support, or sales.&lt;/p&gt;
&lt;p&gt;Score rates something on an ordered scale, for example severity from low to critical.&lt;/p&gt;
&lt;p&gt;Noul answers a yes or no question with a probability between 0 and 1, for example whether an action is destructive.&lt;/p&gt;
&lt;p&gt;Because each result carries a confidence value, your application can set thresholds: act automatically above one level, escalate to a human or a full LLM below it.&lt;/p&gt;
&lt;p&gt;Cost: Read the Fine Print&lt;/p&gt;
&lt;p&gt;TypeSafe's headline claim is 40 to 400 times cheaper than standard LLMs. That holds against a frontier reasoning model. Compared with the small, cheap models most teams already use for classification, the realistic saving is closer to 5 times. At high request volume that still adds up, but it is a different story than the marketing number suggests.&lt;/p&gt;
&lt;h3 id=&quot;where-it-fits-in-a-real-system&quot;&gt;Where It Fits in a Real System&lt;/h3&gt;
&lt;p&gt;The strongest use cases replace an LLM call that was only ever a multiple-choice question.&lt;/p&gt;
&lt;p&gt;The first is routing in front of expensive steps. In a multi-step agent pipeline, a fast classifier can decide whether a request needs a full agent run, a simple lookup, or no model call at all. This is the same principle behind well-designed &lt;a rel=&quot;nofollow&quot; href=&quot;http://intercode.com/services/ai-agent-orchestration&quot;&gt;AI agent orchestration&lt;/a&gt;, where cheap, deterministic decisions gate the costly ones.&lt;/p&gt;
&lt;p&gt;The second is safety gating. Before an agent executes a delete, a schema change, or a write to production data, a Noul check can block it or require approval, with a probability you can threshold.&lt;/p&gt;
&lt;p&gt;The third is retrieval filtering. In a RAG pipeline, scoring each retrieved chunk for relevance before it reaches the main model cuts both noise and token spend, which is a practical optimization for any &lt;a rel=&quot;nofollow&quot; href=&quot;http://intercode.com/services/generative-ai-development&quot;&gt;generative AI and RAG system&lt;/a&gt; running at scale.&lt;/p&gt;
&lt;p&gt;Support triage and lead scoring follow the same shape: several independent scores from one request, combined by your own business logic instead of a long prompt. &lt;/p&gt;
&lt;h3 id=&quot;known-limitations&quot;&gt;Known Limitations&lt;/h3&gt;
&lt;p&gt;Jev has documented blind spots you should design around:&lt;/p&gt;
&lt;p&gt;It is unreliable at math and numeric comparison, so keep calculations in application code.&lt;/p&gt;
&lt;p&gt;It struggles with counting items in a list. Query per item instead.&lt;/p&gt;
&lt;p&gt;It treats dates as plain text and cannot reason about ordering in time.&lt;/p&gt;
&lt;p&gt;It only sees what is explicitly in the input. Implied context is missed.&lt;/p&gt;
&lt;p&gt;Like any model reading untrusted text, it is exposed to prompt injection, so input still needs sanitizing.&lt;/p&gt;
&lt;p&gt;The practical conclusion is to treat it as a fast classifier inside your own code, not as a reasoning engine.&lt;/p&gt;
&lt;h3 id=&quot;a-category-that-is-filling-up-quickly&quot;&gt;A Category That Is Filling Up Quickly&lt;/h3&gt;
&lt;p&gt;Within about three weeks of launch, alternatives appeared. Cloudflare shipped two open-weight models, Clef and Clef-flash, with a compatible API and image input that Jev lacks. Clef-flash is reported at around 39 milliseconds on Cloudflare's edge network. Laya, an open-source option, runs locally without a GPU, with claimed latencies as low as 9 milliseconds. OpenAI has previewed its own decision-oriented API, though it was not broadly available at the time of writing.&lt;/p&gt;
&lt;p&gt;None of these figures are independently verified, so benchmark on your own traffic before choosing a provider. &lt;/p&gt;
&lt;h3 id=&quot;takeaway&quot;&gt;Takeaway&lt;/h3&gt;
&lt;p&gt;If a meaningful part of your LLM spend goes to routing, scoring, or yes or no gates, decision models are worth testing. They will not replace your main model, but they can take over the decisions that never needed one. Teams that want help deciding where this kind of component belongs and shipping it to production can look at InterCode's &lt;a rel=&quot;nofollow&quot; href=&quot;http://intercode.com/services/ai-development&quot;&gt;custom AI development&lt;/a&gt; work.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/30834/jev-ai-explained-a-single-pass-decision-model-for-the-tasks-llms-are-overkill-for</guid>
<pubDate>Mon, 12 Oct 2026 16:18:29 +0000</pubDate>
</item>
<item>
<title>Raising the bar on software delivery</title>
<link>https://coderlegion.com/30814/raising-the-bar-on-software-delivery</link>
<description>&lt;p&gt;In this episode of Continuous Delivery Office Hours, Tony, Bob, and Steve propose a raising of standards for software delivery.&lt;br&gt;
While most development teams can agree on some basic capabilities, like using version control rather than a network share to store code, as soon as you go beyond the simplest things, the level of agreement drops dramatically.&lt;/p&gt;
&lt;p&gt;But we have decades of high-quality research to guide us toward something better, and we should take inspiration from other industries that continually raise the bar for &quot;what good looks like&quot;.&lt;br&gt;
Watch now to delve into:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The evolution of delivery standards: While high-performing teams worked out new standards as they increased their throughput through Lean and Agile, those who haven't raised the bar are now struggling to improve throughput with AI.&lt;/li&gt;
&lt;li&gt;Feedback is the litmus test: Just as Ron Westrum captured the essence of organization in terms of information flow, we can test our software delivery by paying attention to feedback loops.&lt;/li&gt;
&lt;li&gt;The 23 things: We think there are about 23 interconnected capabilities and practices that set the minimum standard for professional software delivery (but we don't list them all in this episode!&lt;/li&gt;
&lt;li&gt;Shopping, watertight houses, and the Omaha basement: We draw inspiration from buildings to highlight the necessity of interconnected practices.&lt;/li&gt;
&lt;/ul&gt;
</description>
<category>Videos</category>
<guid isPermaLink="true">https://coderlegion.com/30814/raising-the-bar-on-software-delivery</guid>
<pubDate>Mon, 12 Oct 2026 13:24:05 +0000</pubDate>
</item>
<item>
<title>Post-Mortem Analyses of Publicly Known AI Security Incidents: Key Architectural Insights</title>
<link>https://coderlegion.com/30792/post-mortem-analyses-of-publicly-known-ai-security-incidents-key-architectural-insights</link>
<description>&lt;p&gt;As artificial intelligence systems transition from isolated proof-of-concept models to deeply integrated production architectures, their attack surface has undergone a fundamental shift. Conventional software security paradigms—which focus primarily on memory safety, injection vulnerabilities, and role-based access control—are inadequate for systems based on probabilistic outputs, unstructured inputs, and vast parameter spaces.&lt;/p&gt;
&lt;p&gt;The investigation of publicly disclosed AI security incidents provides crucial empirical data for hardening modern machine learning pipelines. The following section analyzes recurring classes of vulnerabilities, their systemic causes, and architectural safeguards from a technical perspective.&lt;/p&gt;
&lt;h3 id=&quot;taxonomy-of-vulnerabilities-in-ai-production-systems&quot;&gt;Taxonomy of Vulnerabilities in AI Production Systems&lt;/h3&gt;
&lt;h4 id=&quot;a-prompt-injection-amp-indirect-control-flow-hijacking&quot;&gt;A. Prompt Injection &amp;amp; Indirect Control Flow Hijacking&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Mechanism:&lt;/strong&gt; Untrusted external data (e.g., crawled web pages, emails, or user-uploaded files) contain instructions designed to bypass system prompts or the control mechanisms of autonomous agents.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Incident profile:&lt;/strong&gt; LLM-based agents execute external API calls or apply Retrieval-Augmented Generation (RAG) to malicious datasets. When the model processes untrusted tokens, it interprets them as executable instructions rather than mere data.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cause:&lt;/strong&gt; Failure to maintain a strict separation between the instruction context (system/developer prompts) and the data context (user inputs and retrieved documents).&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;b-training-data-poisoning-amp-supply-chain-compromise&quot;&gt;B. Training Data Poisoning &amp;amp; Supply Chain Compromise&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Mechanism:&lt;/strong&gt; Adversarial manipulation of datasets or pre-trained base models prior to fine-tuning or deployment.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Incident profile:&lt;/strong&gt; Attackers inject subtle backdoors or trigger patterns into open-source datasets. This causes the models to behave securely on standard evaluation benchmarks while failing catastrophically under specific trigger conditions.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cause:&lt;/strong&gt; Inadequate provenance tracking, lack of cryptographic verification of training artifacts, and absence of automated anomaly detection for training corpora.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;c-modeling-of-infiltration-extraction-and-inversion&quot;&gt;C. Modeling of Infiltration, Extraction, and Inversion&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Mechanism:&lt;/strong&gt; Systematic querying of proprietary models to reconstruct proprietary weights, extract training data, or infer hyperparameters.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Incident profile:&lt;/strong&gt; Compromise of commercial APIs via mass side-channel queries that map decision boundaries or expose personally identifiable information (PII) embedded during training.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cause:&lt;/strong&gt; Excessively permissive rate limiting, absence of output perturbation or differential privacy mechanisms, and unlimited granularity of API responses.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;structural-root-causes&quot;&gt;Structural Root Causes&lt;/h3&gt;
&lt;p&gt;The analysis of these incidents reveals three systemic patterns of failure in technical organizations:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;The &quot;software vs. statistics&quot; category error:&lt;/strong&gt; treating ML models as deterministic components rather than as non-deterministic statistical systems. Since models execute semantic logic rather than syntactic rules, conventional input validation (such as sanitization using regular expressions) falls short when dealing with semantic payloads.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Unrestricted operational authority for agents:&lt;/strong&gt; Granting LLM agents extensive, unrestricted capabilities to use tools (e.g., shell access, unvalidated database write permissions, or unrestricted HTTP clients) without intermediary human validation or limiting access rights to the necessary minimum (principle of least privilege).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Blind spots in the supply chain:&lt;/strong&gt; reliance on unverified model checkpoints and third-party datasets downloaded from public platforms—without robust artifact scanning and behavior-based regression testing.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;engineering-playbook-architectural-safeguards&quot;&gt;Engineering Playbook: Architectural Safeguards&lt;/h3&gt;
&lt;p&gt;To develop a resilient AI infrastructure, teams must implement a multi-layered defense strategy (defense-in-depth) across the entire lifecycle:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Dual-model protection mechanisms &amp;amp; input sanitization:&lt;/strong&gt; Implement intermediate classification layers (smaller, highly specialized security models) to evaluate incoming data before it reaches the primary processing mechanisms. Treat all data chunks retrieved via RAG as untrusted input.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Isolation of structural context:&lt;/strong&gt; Leverage new API primitives and token-level structural markers that enforce strict separation between system logic and dynamic user data (e.g., explicit delimiters for chat templates and role isolation).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Execution of tools based on the principle of least privilege:&lt;/strong&gt; Agentic tool environments run in a sandbox. API calls are embedded in deterministic middleware that validates parameters against strict JSON schemas and business logic constraints prior to execution.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Artifact Provenance &amp;amp; SBOMs for AI:&lt;/strong&gt; Implement Software Bill of Materials (SBOM) standards for datasets and model weights. Use cryptographic hashing (SHA-256) and secure artifact registries to track every stage of the MLOps pipeline.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Continuous Red Teaming &amp;amp; Behavioral Fuzzing:&lt;/strong&gt; Integrate automated adversarial testing (e.g., automated prompt injection fuzzers) into CI/CD deployment pipelines to detect regressions in the model's security boundaries prior to production deployment.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;conclusion-1&quot;&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;Securing AI systems is an ongoing process; it requires a synthesis of traditional cybersecurity disciplines and novel, ML-specific threat models. As AI engineers and technical architects, our task is to develop systems that anticipate adversarial manipulation by default and shift our approach from reactive patching to proactive, demonstrable resilience.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;What architectural patterns has your team implemented to minimize risks from prompt injection and supply chain attacks in production? Let’s discuss this in the comments.&lt;/em&gt;&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/30792/post-mortem-analyses-of-publicly-known-ai-security-incidents-key-architectural-insights</guid>
<pubDate>Mon, 12 Oct 2026 10:09:49 +0000</pubDate>
</item>
<item>
<title>Why LeetCode Interviews Measure the Wrong Thing</title>
<link>https://coderlegion.com/30343/why-leetcode-interviews-measure-the-wrong-thing</link>
<description>&lt;p&gt;Coding tests can effectively filter out candidates who can't write working code. That's a legitimate purpose. The problem emerges when organizations test for timed algorithm-puzzle skills in roles that require entirely different capabilities: system design, debugging distributed systems, architectural decision-making, or cross-functional collaboration.&lt;/p&gt;
&lt;blockquote class=&quot;pull-quote&quot;&gt;
&lt;p&gt;A senior engineer who can architect scalable systems and debug production failures shouldn't be rejected because they can't solve a dynamic programming puzzle in thirty minutes.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The interview tests for skills the job doesn't require while ignoring skills that define success in the role.&lt;/p&gt;
&lt;h3 id=&quot;timed-puzzles-test-a-skill-most-roles-rarely-use&quot;&gt;Timed Puzzles Test a Skill Most Roles Rarely Use&lt;/h3&gt;
&lt;p&gt;Some roles genuinely require strong algorithm skills. If you're building database engines, compilers, or performance-critical systems, testing for algorithmic thinking makes sense. The problem is applying this filter by default regardless of what the job actually requires.&lt;/p&gt;
&lt;p&gt;A problem a competent engineer can solve cold with basic data structures, like reversing a linked list or finding duplicates with a hash map, checks the coding fluency every role needs. The mismatch starts with problems that are hard to finish in thirty minutes unless you recognize a named technique the role doesn't use, such as dynamic programming or a specific graph algorithm. The argument here is about those technique-dependent problems. Difficulty labels on practice sites don't mark that line, since many medium problems are plain hash-map work.&lt;/p&gt;
&lt;p&gt;Most software engineering roles demand system design thinking to architect scalable solutions, production debugging to troubleshoot complex failures, cross-functional collaboration to align technical decisions with business needs, and trade-off evaluation to balance competing constraints. Algorithm optimization rarely appears in the day-to-day work, yet coding rounds built on technique-dependent problems test almost exclusively for it. Larger interview loops add design and behavioral rounds, but the coding round often comes first as a screen. When that screen uses technique-dependent problems, a candidate who fails it never reaches the rounds that sample the job.&lt;/p&gt;
&lt;p&gt;Even when performance optimization matters in typical production systems, it's usually about database query optimization, caching strategies, network request patterns, and system architecture rather than the algorithmic complexity of isolated functions. For roles building web applications or distributed services, the engineer who can identify an N+1 query problem delivers more immediate value than the engineer who can implement graph traversal algorithms from memory. Different roles, different requirements.&lt;/p&gt;
&lt;p&gt;A candidate might struggle to find the optimal solution to a graph problem in thirty minutes while being excellent at diagnosing why a distributed system exhibits inconsistent behavior under load. They might fumble dynamic programming problems while being the engineer who catches architectural flaws in design reviews. When the role requires the latter skills, the interview measures their weakest capability and ignores their strongest. Public data doesn't show how often that happens across a hiring pool, so the case here rests on what the test contains rather than on counted rejections.&lt;/p&gt;
&lt;h4 id=&quot;drilled-patterns-pass-for-reasoning&quot;&gt;Drilled Patterns Pass for Reasoning&lt;/h4&gt;
&lt;p&gt;Pattern matching also masquerades as algorithmic thinking. Practice sites group their problems by technique, such as two pointers, sliding window, and dynamic programming. A candidate who has drilled enough of them can solve a hard problem by recognizing which template it fits. Recognizing patterns is a useful skill, and engineers diagnose production issues partly that way. But recognizing a problem from a drilled catalog measures hours of preparation.&lt;/p&gt;
&lt;p&gt;Under time pressure, recall produces the same answer on the whiteboard as reasoning from first principles. An interviewer can try to tell them apart by changing a constraint and asking why. That probe works only as well as the interviewer runs it, so the signal comes from the interviewer, not the format.&lt;/p&gt;
&lt;h4 id=&quot;one-live-round-adds-noise&quot;&gt;One Live Round Adds Noise&lt;/h4&gt;
&lt;p&gt;The live format adds noise of its own. In a randomized controlled trial published at ESEC/FSE 2020, Mahnaz Behroozi, Chris Parnin, and colleagues gave 48 computer science students a whiteboard problem, half with an interviewer watching and half alone in a private room. Being watched cut performance by more than half. That penalty comes from observation, not from the puzzle, so it applies to any live exercise.&lt;/p&gt;
&lt;p&gt;Aline Lerner found a second source of noise in 2016 data from interviewing.io, a practice-interview platform. Only about a quarter of candidates who interviewed more than once performed consistently, and many who earned a top score in one interview earned a failing score in another. That instability belongs to any single interview. Neither finding favors one format's content over another's. Both argue against letting one live round decide, and a lone timed puzzle used as a gate is exactly that. It measures preparation and composure on that day alongside a skill the role rarely uses.&lt;/p&gt;
&lt;h4 id=&quot;the-screen-s-misses-fall-unevenly&quot;&gt;The Screen's Misses Fall Unevenly&lt;/h4&gt;
&lt;p&gt;The usual defense is that this filter accepts false negatives to avoid false positives, because a bad hire is expensive and a large company can afford to lose good candidates. The trade can hold for the company, but it rests on an assumption. For a role that doesn't use the puzzle skill, the screen catches bad hires only as far as puzzle performance happens to track the skills that predict success in that role.&lt;/p&gt;
&lt;p&gt;Its false negatives also aren't spread evenly. A filter that rewards drilled patterns favors whoever had time to drill, and the talent pool narrows in that direction. Defenders read those drilling hours as a sign of conscientiousness, but hours available to drill also track free time, so the signal mixes diligence with circumstance.&lt;/p&gt;
&lt;h3 id=&quot;most-roles-run-on-debugging-design-and-review&quot;&gt;Most Roles Run on Debugging, Design, and Review&lt;/h3&gt;
&lt;p&gt;Most software engineering involves systems thinking applied to building products. That means understanding how components interact, how failures propagate, how changes impact users, and how technical decisions affect business outcomes. It means reading production metrics to diagnose issues, reviewing code to catch bugs before deployment, mentoring junior engineers to improve team capability, and collaborating with product teams to ensure what gets built actually solves user problems.&lt;/p&gt;
&lt;p&gt;Consider what a senior application engineer actually does. They investigate why a service's 99th percentile latency spiked from 200ms to 2 seconds overnight and discover a deployment introduced inefficient database queries. They review a pull request, identify a race condition in concurrent access to shared state, and suggest refactoring with proper locking. They pair with a junior developer to debug why a feature works locally but fails in staging due to differences in environment configuration. They join a design meeting and explain why the proposed real-time notification feature requires WebSocket infrastructure and database schema changes that will take three weeks, not three days.&lt;/p&gt;
&lt;p&gt;None of these activities resemble solving algorithm puzzles under time pressure.&lt;/p&gt;
&lt;h3 id=&quot;work-like-formats-keep-what-puzzles-get-right&quot;&gt;Work-Like Formats Keep What Puzzles Get Right&lt;/h3&gt;
&lt;p&gt;Puzzles persist for reasons that hold up. They're cheap to run at scale, every candidate gets a comparable problem, and the scoring is easy to defend. That consistency comes from giving every candidate the same problem and scoring it against written criteria. Neither step depends on puzzle content, and a work-like exercise can do both.&lt;/p&gt;
&lt;h4 id=&quot;prediction-is-tied-so-content-decides&quot;&gt;Prediction Is Tied, So Content Decides&lt;/h4&gt;
&lt;p&gt;Puzzles' strongest defenders also treat them as a proxy for general reasoning ability. Public data tying algorithm-round scores to later job performance is scarce, so the case for or against puzzles has to lean on general selection research. Paul Sackett and colleagues re-analyzed personnel-selection studies in the Journal of Applied Psychology in 2022. They estimated validity at .42 for structured interviews, .40 for job knowledge tests, .33 for work samples, and .31 for cognitive ability. Those are averages across occupations, and work samples don't clearly beat cognitive ability on their own.&lt;/p&gt;
&lt;p&gt;So the numbers don't crown a content winner. When prediction is roughly tied and no outcome data exists for a particular test, selection practice falls back on content validity. The US Uniform Guidelines on Employee Selection Procedures (29 CFR 1607.5) describe it as showing that a test's content is &quot;representative of important aspects of performance on the job.&quot;&lt;/p&gt;
&lt;p&gt;Puzzles fall short of that standard whichever way you classify them. Treated as a cognitive-ability test, a puzzle is a proxy that ranks below job knowledge. Treated as a job-knowledge test, it samples algorithm technique the role rarely uses. Either way, a structured exercise built from the role's own work meets the content standard more directly. That standard is about defensibility, not a proven gain in hires. But when prediction is tied, matching the job's content is the reason left to prefer one format, so it breaks the tie.&lt;/p&gt;
&lt;p&gt;Two problems from the first section, drilled recall and uneven misses, also favor the work-like format. Preparation bends the puzzle proxy, because puzzles draw from a public catalog of thousands of problems with published solutions, so a candidate who has seen the problem is measured partly on recall. And the screen's false negatives skew toward people without time to drill.&lt;/p&gt;
&lt;p&gt;A work-like format costs more. Scenarios have to be written per role, calibrated, and rotated, and interviewers have to be trained and recalibrated on the rubric. Most of that cost recurs per role. The longer formats also take more interviewer time per candidate, so a short debugging or review exercise suits the screen and the longer ones suit later rounds. A puzzle screen is cheaper to run, but its cost lands elsewhere, in each candidate it turns away for content the role doesn't use. No public outcome data shows which cost is larger.&lt;/p&gt;
&lt;h4 id=&quot;match-the-format-to-the-work&quot;&gt;Match the Format to the Work&lt;/h4&gt;
&lt;p&gt;Some companies have made that switch, and the community-maintained Hiring Without Whiteboards list on GitHub catalogs hundreds of them. The list shows the switch is workable at scale, not that those companies hire better, so the case for it rests on the research above. The format should follow the work the role does most:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;When the role mostly... &lt;/th&gt;&lt;th&gt;Test it with &lt;/th&gt;&lt;th&gt;What it reveals&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;Builds performance-critical internals like database engines, compilers, or low-latency libraries &lt;/td&gt;&lt;td&gt;Algorithm and data-structure problems, ideally drawn from the domain &lt;/td&gt;&lt;td&gt;Whether the candidate can reason about complexity where it decides the outcome&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Extends and maintains a product codebase &lt;/td&gt;&lt;td&gt;Pair programming on an existing codebase &lt;/td&gt;&lt;td&gt;Collaboration, code quality standards, and how they work through ambiguous requirements&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Delivers features end to end &lt;/td&gt;&lt;td&gt;A small take-home project with tests and documentation &lt;/td&gt;&lt;td&gt;Whether they can build a working feature without artificial time pressure&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Runs services in production &lt;/td&gt;&lt;td&gt;Diagnosing a seeded bug from logs and metrics &lt;/td&gt;&lt;td&gt;How they form and test hypotheses about a failure&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Shapes how services fit together &lt;/td&gt;&lt;td&gt;A system design discussion on a realistic problem &lt;/td&gt;&lt;td&gt;Their grasp of distributed systems, scalability patterns, and trade-off evaluation&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Reviews others' work and mentors &lt;/td&gt;&lt;td&gt;Critiquing a real pull request &lt;/td&gt;&lt;td&gt;Whether they can find bugs, suggest improvements, and give feedback constructively&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Some employers hire into a general pool and place engineers on teams later, so there is no single role to sample, and puzzles appeal because they are role-neutral. But a generalist pool still shares a core of work: reading unfamiliar code, debugging a failure, and reviewing a pull request. The debugging and review rows above sample that core for any team, and they meet the content standard where a puzzle doesn't.&lt;/p&gt;
&lt;h4 id=&quot;structure-makes-any-format-fairer&quot;&gt;Structure Makes Any Format Fairer&lt;/h4&gt;
&lt;p&gt;These approaches aren't perfect. Take-home projects favor candidates with more free time, the same skew a drilled screen has, which is a reason to keep them small. Pair programming can feel stressful, and system design discussions are harder to standardize.&lt;/p&gt;
&lt;p&gt;The observation penalty from the Behroozi study also hits every live row in the table, so give candidates private time to work before they discuss their approach wherever the format allows.&lt;/p&gt;
&lt;p&gt;Private work brings its own problem, because AI assistants make unobserved work hard to attribute. Any private or take-home portion needs a live follow-up where the candidate walks through their work and extends it. That follow-up is still observed, but the candidate explains a solution they already reached instead of searching for one under a watching eye. The Behroozi study's private condition used the same shape, with participants solving alone and then explaining their work in a retrospective think-aloud.&lt;/p&gt;
&lt;p&gt;Each format earns its place only when it's structured. That means the same scenario for every candidate, and a rubric written beforehand that lists what a strong answer finds, such as the seeded bug's root cause or the race condition in the pull request. Scenarios drawn from the company's own systems leak too, through interview-review sites. But they have no published solution set and can be rotated, so they resist drilling better than a public catalog does. Built that way, structure supplies the consistency and the job's own work supplies the content, rather than memorized algorithm patterns.&lt;/p&gt;
&lt;h3 id=&quot;learn-algorithms-for-the-work-not-the-interview&quot;&gt;Learn Algorithms for the Work, Not the Interview&lt;/h3&gt;
&lt;p&gt;Build your algorithmic foundation because it matters beyond the interview. Understanding data structures helps you choose the right tool for the job, and knowing complexity analysis helps you write efficient code. Study algorithms to become a better engineer, not just to pass interviews.&lt;/p&gt;
&lt;p&gt;But recognize that grinding LeetCode is interview preparation, not professional development. The hours spent memorizing dynamic programming patterns could be spent building systems, contributing to open source, or learning distributed systems concepts. Those investments develop professional capability while LeetCode grinding mostly develops interview performance.&lt;/p&gt;
&lt;blockquote class=&quot;pull-quote&quot;&gt;
&lt;p&gt;Strong engineers architect scalable systems, debug production failures, and collaborate across teams to ship features. Whether they can solve algorithm puzzles in thirty minutes is an indirect, noisy signal of any of that.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The problem isn't the candidates' capability. It's an interview process that measures the wrong thing for the role.&lt;/p&gt;
&lt;p&gt;Until hiring changes, engineers are stuck preparing for interviews that don't reflect the job, while companies reject candidates on content that says little about the role.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/30343/why-leetcode-interviews-measure-the-wrong-thing</guid>
<pubDate>Thu, 08 Oct 2026 20:56:22 +0000</pubDate>
</item>
<item>
<title>🧩 **Engineering a Machine-Validatable Contributor Evidence Workflow**</title>
<link>https://coderlegion.com/30305/engineering-a-machine-validatable-contributor-evidence-workflow</link>
<description>&lt;p&gt;🧩 &lt;strong&gt;Engineering a Machine-Validatable Contributor Evidence Workflow&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;I just merged a new infrastructure milestone into MyZubster:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;PR #1572 — standardized contributor interoperability checkpoints&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The problem I wanted to solve was not just “how do we document a successful contributor test?”&lt;/p&gt;
&lt;p&gt;The real problem was:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;How do we make technical evidence portable across contributors, technologies and verification paths without losing provenance or inflating claims?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Until now, MyZubster already had reproducible contributor checkpoints, but the structure was mostly expressed through Markdown, PR discussions and manually reviewed evidence.&lt;/p&gt;
&lt;p&gt;That works at small scale.&lt;/p&gt;
&lt;p&gt;It does not scale well when multiple contributors are producing different kinds of evidence.&lt;/p&gt;
&lt;p&gt;So I introduced a common machine-readable format:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;myzubster.interoperability-checkpoint.v1&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Each checkpoint now describes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;contributor identity&lt;/li&gt;
&lt;li&gt;repository / PR&lt;/li&gt;
&lt;li&gt;immutable commit reference&lt;/li&gt;
&lt;li&gt;technical bridge type&lt;/li&gt;
&lt;li&gt;exact scope&lt;/li&gt;
&lt;li&gt;test environment&lt;/li&gt;
&lt;li&gt;reproduction procedure&lt;/li&gt;
&lt;li&gt;observed output&lt;/li&gt;
&lt;li&gt;evidence state&lt;/li&gt;
&lt;li&gt;limitations&lt;/li&gt;
&lt;li&gt;canonical provenance&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;why-the-limitations-field-matters&quot;&gt;Why the limitations field matters&lt;/h4&gt;
&lt;p&gt;One of the most important design decisions was making limitations mandatory.&lt;/p&gt;
&lt;p&gt;A technical checkpoint should not silently become a broader claim.&lt;/p&gt;
&lt;p&gt;For example:&lt;/p&gt;
&lt;p&gt;A successful broker-to-agent test does not automatically mean:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;full decentralization&lt;/li&gt;
&lt;li&gt;direct P2P networking&lt;/li&gt;
&lt;li&gt;security certification&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;A research Knowledge Card marked &lt;code&gt;SUPPORTED&lt;/code&gt; should not become “scientifically validated.”&lt;/p&gt;
&lt;p&gt;A signed webhook regression should not become “payment settlement verified.”&lt;/p&gt;
&lt;p&gt;The schema is intentionally designed to preserve those boundaries.&lt;/p&gt;
&lt;h4 id=&quot;evidence-state-is-constrained-by-observation&quot;&gt;Evidence state is constrained by observation&lt;/h4&gt;
&lt;p&gt;The validator enforces an important rule:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;TESTED&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;requires:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;observation.result = PASS&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;That sounds obvious, but encoding it into the schema means it is no longer just a documentation convention.&lt;/p&gt;
&lt;p&gt;It becomes something CI can reject.&lt;/p&gt;
&lt;h4 id=&quot;ajv-ci-integration&quot;&gt;AJV + CI integration&lt;/h4&gt;
&lt;p&gt;I added an AJV-based validator and connected it to the existing &lt;strong&gt;Continuous Evidence Gate&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;So the pipeline now looks like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;Contributor work
        ↓
Scoped reproducible test
        ↓
Machine-readable checkpoint
        ↓
JSON Schema validation
        ↓
Continuous Evidence Gate
        ↓
Human review
        ↓
Canonical merge
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The important distinction:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;automation validates evidence structure; it does not replace engineering judgment.&lt;/strong&gt;&lt;/p&gt;
&lt;h4 id=&quot;testing-the-format-across-different-contribution-types&quot;&gt;Testing the format across different contribution types&lt;/h4&gt;
&lt;p&gt;I did not want the first schema version to be designed around one specific contributor.&lt;/p&gt;
&lt;p&gt;So the initial dataset already includes multiple technical paths.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;N4K48 / Nicola&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Runtime interoperability:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;MyZubster VPS
→ authenticated HTTPS broker
→ contributor-controlled agent
→ local catalog
→ result returned to Bridge
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;wasim-builds&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Security regression:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;unconfigured auth → rejected
missing credential → rejected
wrong credential → rejected
correct credential → accepted
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;khongten124 / Open Period Care&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Semantic evidence bridge:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;public research evidence
→ structured ingestion
→ contributor-scoped retrieval
→ Knowledge Cards returned
→ original SUPPORTED state preserved
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Aming9303&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Signed webhook regression:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;HMAC signing
→ stable delivery identity
→ unsigned request rejection
→ replay protection
→ stale-event rejection
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;These are very different systems.&lt;/p&gt;
&lt;p&gt;That is exactly why using the same evidence structure is useful.&lt;/p&gt;
&lt;h4 id=&quot;current-maturity&quot;&gt;Current maturity&lt;/h4&gt;
&lt;p&gt;I would describe the workflow as:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;repeatable
→ auditable
→ multi-contributor
→ machine-validatable
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The next layer will be:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;automated ingestion
→ Contributor Passport linkage
→ Knowledge Graph integration
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;while preserving the original evidence state and provenance.&lt;/p&gt;
&lt;h4 id=&quot;merge-result&quot;&gt;Merge result&lt;/h4&gt;
&lt;p&gt;PR #1572 passed:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;CI – Test &amp;amp; Lint&lt;/li&gt;
&lt;li&gt;Security Audit&lt;/li&gt;
&lt;li&gt;Continuous Evidence Gate&lt;/li&gt;
&lt;li&gt;policy checks&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Canonical merge:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;2be06b7e34f22744ecf81a9daef12f7ba11864be&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;What I like about this milestone is that it is not visually impressive.&lt;/p&gt;
&lt;p&gt;There is no shiny UI involved.&lt;/p&gt;
&lt;p&gt;But it improves something much more fundamental:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;the trust model between contributors and the platform.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Instead of:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;“This was tested.”&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;the system can increasingly answer:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Who tested it?&lt;br&gt;
Against which commit?&lt;br&gt;
In which environment?&lt;br&gt;
Using which procedure?&lt;br&gt;
What was observed?&lt;br&gt;
What does the evidence not prove?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;That is the direction I want to keep pushing.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Evidence should be inspectable data, not just prose.&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id=&quot;coderlegion-softwareengineering-jsonschema-ajv-ci-devops-systemdesign-opensource-reproducibility-testing-developertools&quot;&gt;CoderLegion #SoftwareEngineering #JSONSchema #AJV #CI #DevOps #SystemDesign #OpenSource #Reproducibility #Testing #DeveloperTools&lt;/h2&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/30305/engineering-a-machine-validatable-contributor-evidence-workflow</guid>
<pubDate>Thu, 08 Oct 2026 13:50:59 +0000</pubDate>
</item>
<item>
<title>What Building One API for Research, Media, and Editable Files Taught Me About Reliable AI Products</title>
<link>https://coderlegion.com/30156/what-building-one-api-for-research-media-and-editable-files-taught-about-reliable-products</link>
<description>&lt;p&gt;I started 3Stone AI because the work I wanted to do rarely ended in one format. Research might need to become a brief, a presentation, or a spreadsheet. An image might become a video. A plan might need to become an interactive tool. I wanted research, media generation, editable Office files, and useful software available through one product and one API instead of making the customer assemble a chain of unrelated services.&lt;/p&gt;
&lt;p&gt;At first, that sounds like an interface problem: put several models behind a chat box and route each request to the right provider. Building the real product taught me that routing is only the beginning. The harder work is making every accepted request produce a result the customer can actually open, use, download, revisit, and revise.&lt;/p&gt;
&lt;p&gt;Real customer problems forced that lesson repeatedly. A request could pass automated tests while the customer journey still broke. Audio could exist but be the wrong kind of audio. An entitlement could be correct in one system and stale in another. A provider could report success while the exported file was unusable. I stopped treating “the test passed” as the finish line. I began requiring evidence from the delivered output and the complete customer journey.&lt;/p&gt;
&lt;h3 id=&quot;one-api-still-needs-capability-specific-contracts&quot;&gt;One API still needs capability-specific contracts&lt;/h3&gt;
&lt;p&gt;Different kinds of work do not become identical merely because they share authentication. Research can complete synchronously and return sources. Image or Office generation may require a durable background job. A spreadsheet is not successful because a ZIP of XML exists; it must open in Excel, preserve formulas, and remain editable. A retry after a network timeout must not submit the provider job twice or charge twice.&lt;/p&gt;
&lt;p&gt;The current 3Stone developer contract therefore uses capability-specific endpoints behind common authentication, idempotency, usage, job, and artifact conventions. Certified public routes include chat, research, image analysis, image generation, editable PowerPoint, Excel and Word generation, music, and interactive tools. Video exists in the customer product, but it is not presented as a public API capability until the external lifecycle meets the same certification standard.&lt;/p&gt;
&lt;p&gt;That boundary matters. A useful API should describe what developers can verify today, not everything an internal product can theoretically attempt.&lt;/p&gt;
&lt;h3 id=&quot;make-asynchronous-work-explicit&quot;&gt;Make asynchronous work explicit&lt;/h3&gt;
&lt;p&gt;Long-running AI work creates an awkward interval: the provider may have accepted a paid job even when the HTTP response never reaches you. Retrying blindly can create two jobs. Reporting failure immediately can also be wrong.&lt;/p&gt;
&lt;p&gt;We use durable request and job identities, save provider receipts as early as possible, and separate states such as accepted, running, completed, failed, and reconciliation required. The customer polls the 3Stone job rather than learning each provider's lifecycle.&lt;/p&gt;
&lt;p&gt;A simplified image request looks like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;const base = &quot;https://one.3stoneai.com&quot;;
const idempotencyKey = crypto.randomUUID();

const submitted = await fetch(`${base}/v1/images`, {
  method: &quot;POST&quot;,
  headers: {
    Authorization: `Bearer ${process.env.THREESTONE_API_KEY}`,
    &quot;Content-Type&quot;: &quot;application/json&quot;,
    &quot;Idempotency-Key&quot;: idempotencyKey,
  },
  body: JSON.stringify({
    prompt: &quot;An editorial photograph of a solar-powered neighborhood library at sunrise; no text or logos.&quot;
  }),
});

const job = await submitted.json();
if (submitted.status !== 202) throw new Error(job.error?.message ?? &quot;Request rejected&quot;);

while (true) {
  await new Promise(resolve =&amp;gt; setTimeout(resolve, 2500));
  const response = await fetch(`${base}/v1/jobs/${job.job_id}`, {
    headers: { Authorization: `Bearer ${process.env.THREESTONE_API_KEY}` },
  });
  const current = await response.json();
  if (current.status === &quot;completed&quot;) break;
  if ([&quot;failed&quot;, &quot;reconciliation_required&quot;].includes(current.status)) {
    throw new Error(`Job ended as ${current.status}`);
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The important details are not glamorous: bearer keys stay on the server, every mutating request has a unique idempotency key, accepted asynchronous work returns a durable job ID, and ambiguous provider outcomes are reconciled instead of automatically replayed.&lt;/p&gt;
&lt;h3 id=&quot;a-fallback-must-support-the-same-job&quot;&gt;A fallback must support the same job&lt;/h3&gt;
&lt;p&gt;One recurring integration mistake was treating any available model as a fallback. It is not enough for two providers to share an output label such as “image” or “video.” A text-to-image endpoint may not edit an image. A text-to-video endpoint may not replace a character in existing footage. A video endpoint that returns silent footage cannot honestly claim synchronized audio.&lt;/p&gt;
&lt;p&gt;That changed how I think about recovery. A fallback is eligible only when it supports the requested inputs, preservation behavior, output format, and practical limits. It also must be safe for the particular failure. A known authentication rejection before execution is different from a connection loss after a provider may have accepted a paid request.&lt;/p&gt;
&lt;p&gt;For image generation, 3Stone's separate-provider fallback is constrained to confirmed operational failure classes. It does not activate for moderation decisions, ambiguous post-submission failures, or arbitrary errors. For video, audio routing distinguishes source audio, ambience and effects, dialogue, music, narration, and intentional silence. ElevenLabs can be a recovery option for appropriate speech; it is not automatically attached to every video.&lt;/p&gt;
&lt;p&gt;The broader lesson is simple: recovery that changes the customer's request is not recovery.&lt;/p&gt;
&lt;h3 id=&quot;validate-the-artifact-not-the-provider-status&quot;&gt;Validate the artifact, not the provider status&lt;/h3&gt;
&lt;p&gt;The defect pattern that most changed my release standard was seeing technical success without a usable customer outcome. A provider could say “succeeded,” a route could return HTTP 200, and a test suite could be green—yet the file might not open correctly, the audio might be irrelevant, or the next customer action might fail.&lt;/p&gt;
&lt;p&gt;Now I want evidence at the artifact boundary. Images must decode and meet the expected dimensions. Videos are probed for duration, codec, dimensions, and required audio. PowerPoint, Excel, and Word files are reopened and checked for their native structure, including slides, sheets, formulas, tables, and editability. Interactive HTML is rendered and exercised. Customer certification includes creation, preview or playback, download, persistence, reopen, and revision where the capability supports it.&lt;/p&gt;
&lt;p&gt;This standard also applies to account and entitlement work. A deletion button is not verified because it displayed a confirmation dialog; the customer must return to signed-out state and the intended account data must actually be gone. A microphone permission prompt is not proof of a live voice session; the system needs a real captured turn, an audible response, interruption behavior, transcript persistence, and correct accounting.&lt;/p&gt;
&lt;h3 id=&quot;idempotency-is-a-billing-promise&quot;&gt;Idempotency is a billing promise&lt;/h3&gt;
&lt;p&gt;Idempotency is often described as an API convenience. For paid AI work, it is also a billing guarantee.&lt;/p&gt;
&lt;p&gt;Each mutating request binds the caller's idempotency key to a fingerprint of the operation. The same key with the same request returns the original outcome. The same key with changed input is rejected. Before expensive execution, the system reserves a bounded allowance. Completion settles recorded usage. Known pre-execution failure releases the reservation. An ambiguous provider outcome enters reconciliation instead of triggering another paid attempt.&lt;/p&gt;
&lt;p&gt;We test the behavior, not just the presence of the header. In production certification, an immediate replay of a completed request returned the original request identity without creating another charge. That evidence is more meaningful to me than a unit test that merely proves the code reads &lt;code&gt;Idempotency-Key&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&quot;defects-became-architecture&quot;&gt;Defects became architecture&lt;/h3&gt;
&lt;p&gt;Several parts of the architecture came directly from customer-facing failures:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Provider routing became capability-aware because “another model” was not necessarily compatible.&lt;/li&gt;
&lt;li&gt;Recovery gained durable receipts because a timeout did not prove that no paid job existed.&lt;/li&gt;
&lt;li&gt;Audio became intent-aware because generic narration could technically make a file audible while making the result worse.&lt;/li&gt;
&lt;li&gt;Entitlement checks moved toward an authoritative shared view because stale plan state could break an otherwise correct journey.&lt;/li&gt;
&lt;li&gt;Office and media delivery gained output inspection because generated bytes were not proof of a usable artifact.&lt;/li&gt;
&lt;li&gt;Account deletion gained resumable phases because destructive work must finish completely and safely retry after partial failure.&lt;/li&gt;
&lt;li&gt;Alerts gained provenance because certification tests and capability questions should not be reported as failed customer jobs.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These were not abstract platform goals. They were responses to moments when the product's internal story and the customer's actual experience disagreed.&lt;/p&gt;
&lt;h3 id=&quot;the-public-request-can-stay-small&quot;&gt;The public request can stay small&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;curl https://one.3stoneai.com/v1/chat \
  -H &quot;Authorization: Bearer $THREESTONE_API_KEY&quot; \
  -H &quot;Idempotency-Key: first-request-001&quot; \
  -H &quot;Content-Type: application/json&quot; \
  -d '{&quot;model&quot;:&quot;3stone-auto&quot;,&quot;input&quot;:&quot;Explain exactly-once API billing simply.&quot;}'
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The small surface is intentional. Underneath it, the system still needs tenant isolation, request identity, usage attribution, safe retry behavior, and a result the developer can verify.&lt;/p&gt;
&lt;h3 id=&quot;what-i-would-tell-another-builder&quot;&gt;What I would tell another builder&lt;/h3&gt;
&lt;p&gt;My biggest shift was moving from provider success to customer-verifiable delivery. If I hope another developer changes one thing after reading this, it is this: do not release the promise represented by an endpoint until you have followed the real output through the customer's next action.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/30156/what-building-one-api-for-research-media-and-editable-files-taught-about-reliable-products</guid>
<pubDate>Wed, 07 Oct 2026 18:48:05 +0000</pubDate>
</item>
<item>
<title>Planning for Plan Continuation Bias: How to Keep Building the Right Thing</title>
<link>https://coderlegion.com/30158/planning-for-plan-continuation-bias-how-to-keep-building-the-right-thing</link>
<description>&lt;p&gt;Many of the software projects that go wrong go wrong through misalignment. They solve a need nobody had, accept tradeoffs nobody weighed, or carry risks nobody examined. PMI's 2014 survey of more than 2,000 practitioners put poor requirements management behind almost half of projects that missed their goals. Alignment isn't a phase that ends when the work starts. Every plan, discovery, commit, and test is a chance for the work to drift away from what it was meant to accomplish, and a chance to pull it back.&lt;/p&gt;
&lt;p&gt;Pulling it back requires something most advice skips over. You have to stop. For years, I couldn't. When a better idea surfaced mid-implementation, my first instinct wasn't to evaluate it but to finish what I was doing. By the time I finished, the switching costs had mounted and the moment for cheap redirection had passed. It was rarely pride, just a pre-rational impulse to proceed, as though stopping mid-stride carried some cost that continuing didn't.&lt;/p&gt;
&lt;p&gt;What changed it for me was learning to think about work in terms of business value and to treat my own direction as an assumption to test rather than a plan to complete. I stopped being a code cowboy. The impulse didn't disappear, though, and it isn't mine alone. It has a formal name, and it explains why realignment so often fails to happen.&lt;/p&gt;
&lt;h3 id=&quot;continuing-wins-because-it-takes-less-thought&quot;&gt;Continuing Wins Because It Takes Less Thought&lt;/h3&gt;
&lt;p&gt;Pilots call it get-there-itis. Researchers call it plan continuation bias. Judith Orasanu and colleagues at NASA's Ames Research Center went through the tactical decision errors in the 37 accidents covered by the NTSB's 1994 review of flightcrew-involved major accidents. Of the 51 tactical decision errors in that corpus, 38 were decisions to continue the original plan despite cues suggesting a different course of action.&lt;/p&gt;
&lt;p&gt;The ratio isn't a pure measure of bias. The sample counts only flights that ended in accidents, so nobody knows how often crews continued in similar conditions and landed safely. Crews also choose to continue far more often than to divert, so continuing has more chances to go wrong. What the cases do show is crews who had the cues calling for a change and carried on anyway.&lt;/p&gt;
&lt;p&gt;Orasanu's team locates much of the cause in effort rather than stubbornness. Revising your understanding of a situation and working out a new course of action costs more cognitive work than continuing with a plan whose details are already settled. People tend to take the cheap operation when it's available. Workload, time pressure, and ambiguous signals all shrink the budget for the expensive operation at exactly the moment it's needed. The original plan doesn't win the argument. It wins by default, because it never has to have one.&lt;/p&gt;
&lt;p&gt;The stakes and the minute-by-minute time pressure don't transfer to software, but the default does. Revising a plan still takes more thought than continuing it, and the budget for that thought still shrinks under deadlines and under commitments other people have built on. There is evidence for this at the scale of whole projects. Mark Keil's 1995 case study of a runaway software project found a failing course of action kept alive by project, psychological, social, and organizational factors together. Nobody has measured the single-task version. It rests on the same mechanism.&lt;/p&gt;
&lt;p&gt;What software lacks is the warning. A pilot pressing into weather has minutes and no undo. A developer pressing on with the wrong approach has weeks, git can undo any of it, and the work ends the way good work does, reviewed and merged. The pilot who got away with it at least knows it was close. The developer rarely learns there was anything to get away with. When the cost does arrive, it arrives months later as slow delivery or a rewrite, blamed on tech debt rather than on the choice. So the software version of the bias seldom corrects itself.&lt;/p&gt;
&lt;p&gt;Our own advice makes it worse. &quot;Finish what you started&quot; is plan continuation bias repackaged as a character virtue. It assumes that what you started should be finished, which is exactly the question the bias keeps you from asking. That isn't the Kanban rule of limiting work in progress, which stops new work from starting and says nothing about whether the current work is right. &quot;Think before you act&quot; assumes the thinking was sound, and once you've thought and decided, the decision has inertia. The problem was never acting without thinking. It's acting without &lt;em&gt;reconsidering&lt;/em&gt;.&lt;/p&gt;
&lt;h3 id=&quot;the-impulse-shows-up-at-every-stage&quot;&gt;The Impulse Shows Up at Every Stage&lt;/h3&gt;
&lt;p&gt;Each stage of software work has its own version of the impulse, and each one lets the work drift a little further from the need before anyone looks up. Planning and discovery are the worst offenders, because the decisions made there shape everything built afterward, and their misalignment stays invisible the longest.&lt;/p&gt;
&lt;h4 id=&quot;planning-the-first-workable-design-becomes-the-plan&quot;&gt;Planning: The First Workable Design Becomes the Plan&lt;/h4&gt;
&lt;p&gt;A plan is supposed to come from weighing options against the need, the tradeoffs, and the risk. Under the impulse, the plan is the first design that seemed workable, and the weighing never happens. Once that design is written into tickets, estimates, and a roadmap, it has momentum before a line of code exists. A team that sketches a synchronous integration in a kickoff meeting, estimates it, and schedules it rarely circles back to ask whether its behavior under failure fits the business's tolerance for delay. Questioning it now means reopening commitments other people rely on, so the plan proceeds unchallenged and its misalignment ships inside every task built from it.&lt;/p&gt;
&lt;h4 id=&quot;discovery-new-information-gets-filed-as-later-&quot;&gt;Discovery: New Information Gets Filed as &quot;Later&quot;&lt;/h4&gt;
&lt;p&gt;Discovery is when the work learns something the plan didn't know: a requirement nobody mentioned, a constraint in a system you integrate with, users who behave differently than assumed. That is the moment realignment exists for, and it's the moment the impulse is strongest, because acting on the discovery means stopping work already in motion. So the discovery becomes a follow-up ticket, a tech-debt item, or a note for the retrospective. It's acknowledged without being acted on, and the work continues against a picture of the need that is now known to be wrong. Deferring is right when a discovery doesn't change the direction, and it's the bias when the discovery would have.&lt;/p&gt;
&lt;h4 id=&quot;coding-let-me-just-get-this-working-first-&quot;&gt;Coding: &quot;Let Me Just Get This Working First&quot;&lt;/h4&gt;
&lt;p&gt;This is the version I see most in newer developers, and they make it constantly. Finishing first and then reevaluating sounds like sequencing rather than avoidance. The plan is to weigh the other approach once this one works. Then you spend another hour building out the current approach. You write tests around it. Other code starts depending on it. A colleague reviews it and builds understanding of how it works. At that point, &quot;considering the other approach&quot; means throwing away not just your work but the organizational investment in reviewing, understanding, and integrating what you've built.&lt;/p&gt;
&lt;p&gt;The impulse to finish manufactures the very sunk costs that now appear to justify not switching. That is what makes the bias self-sealing. Ordinary sunk-cost reasoning is a mistake about money you already spent. This one does the spending itself, then cites the receipt.&lt;/p&gt;
&lt;p&gt;Barry Staw's 1976 study Knee-Deep in the Big Muddy found that people who feel personally responsible for an initial decision commit more resources to it when it starts failing, not fewer. Among participants who received the same news of failure, the ones who had made the original decision themselves invested more, so the escalation tracked authorship of the decision rather than evidence about it. In software, this looks like the developer who hits a warning sign, such as a test that only passes with a workaround. They spend two more days making the approach work rather than an hour checking whether a different one would be simpler.&lt;/p&gt;
&lt;p&gt;Sometimes getting it working first is the right test, because some approaches can't be judged until something runs. A spike has a time box and an agreement to throw it away, so it tests the approach. &quot;Let me just get this working&quot; has neither, so it builds on the approach.&lt;/p&gt;
&lt;h4 id=&quot;testing-proving-instead-of-testing&quot;&gt;Testing: Proving Instead of Testing&lt;/h4&gt;
&lt;p&gt;Proving asks &quot;can I make this work?&quot; and the answer is almost always yes given enough effort. Testing asks &quot;should I be making this work?&quot; and that's the question alignment depends on. The impulse turns testing into proving.&lt;/p&gt;
&lt;p&gt;Tests written after the approach is settled check that the code does what its author intended, and rarely whether what the author intended is what the need required. A green suite then reads as confirmation of the direction, when all it confirmed was the implementation. Tests that come from the need before the approach is chosen, such as a check against the business's tolerance for delay, are the ones built to fail a direction. Without them, the current assumption gets the full weight of implementation and verification, while the alternative gets a hypothetical conversation, maybe, later, if there's time.&lt;/p&gt;
&lt;h3 id=&quot;a-team-s-momentum-feels-like-validation&quot;&gt;A Team's Momentum Feels Like Validation&lt;/h3&gt;
&lt;p&gt;The group version is worse, and it doesn't work the way most people assume.&lt;/p&gt;
&lt;p&gt;The familiar picture of groupthink is dissent being actively suppressed. That happens, but I'd argue a subtler and more passive version also exists, and it's harder to see. Nobody explicitly validated the direction. Nobody consciously suppressed alternatives. Everyone just assumed that someone else must have validated it, and the group's momentum itself became evidence that the direction is correct.&lt;/p&gt;
&lt;p&gt;This shares something with the bystander effect, where responsibility diffuses until nobody acts, but it runs a step further. Diffused responsibility explains why nobody checked. Momentum read as confirmation explains why nobody felt the need to.&lt;/p&gt;
&lt;p&gt;Questioning the direction doesn't just feel unproductive. It feels like you're slowing the team down, which in most team cultures marks you as the obstacle rather than the one asking the right question. From the inside, speed is indistinguishable from conviction.&lt;/p&gt;
&lt;p&gt;Orasanu's team didn't isolate momentum, but their data does show social pressure feeding the bias. Alongside ambiguous conditions, they named organizational and socially-induced goal conflicts as the second context that contributes to plan-continuation errors. So the group is more than a second instance of the bias. It's one of the conditions that feed it.&lt;/p&gt;
&lt;h3 id=&quot;process-decides-which-direction-is-free&quot;&gt;Process Decides Which Direction Is Free&lt;/h3&gt;
&lt;p&gt;Most methods for keeping work aligned assume that when discovery arrives, someone will notice and act. None of them do the registering for you.&lt;/p&gt;
&lt;h4 id=&quot;fixed-intervals-put-correction-at-the-far-end&quot;&gt;Fixed Intervals Put Correction at the Far End&lt;/h4&gt;
&lt;p&gt;Any cadence that commits a group to a fixed goal or set of work for a fixed window places the routine moment for changing direction at the end of that window. In a two-week sprint, the review is the official place to say the plan was wrong, and by the time it arrives two weeks of sunk cost have accumulated. The correction point sits exactly where the bias is strongest.&lt;/p&gt;
&lt;p&gt;Scrum is not blind to this. The Scrum Guide fixes the Sprint Goal rather than the scope, and the Daily Scrum adapts the plan toward that goal every day. A Product Owner can cancel a sprint, and the sprint boundary itself is a replanning point. But daily adaptation serves the goal rather than questioning it, and cancellation is a lever someone has to decide to pull, which is the very decision the bias suppresses. The standup, as many teams run it, has each person report progress against work already committed, which quietly rewards continuation every morning. It is part of what the framework trades for predictability.&lt;/p&gt;
&lt;h4 id=&quot;some-processes-make-stopping-the-default&quot;&gt;Some Processes Make Stopping the Default&lt;/h4&gt;
&lt;p&gt;Basecamp's Shape Up is organized around three moves that make a discovery cheaper to act on. A circuit breaker means unfinished work expires by default instead of receiving an extension, so stopping requires no argument from anyone. An appetite fixes the time and varies the scope, which makes dropping part of a plan an ordinary decision rather than an admission of failure. Shaping before committing means less has been invested at the moment the &quot;this isn't right&quot; signal arrives, giving the signal a chance to compete with momentum.&lt;/p&gt;
&lt;p&gt;What matters about a process is which direction it makes free, and a sprint and a circuit breaker pull in opposite directions. The Scrum Guide returns unfinished work to the backlog, but nothing stops the next planning session from pulling it straight back in, and the work already done argues for exactly that. Under a sprint, stopping has to be argued for. Under a circuit breaker, expiry is the default, so continuing has to be argued for. Shape Up's six-week window is longer than a sprint, so its advantage comes from the default, not from stopping sooner.&lt;/p&gt;
&lt;p&gt;Circuit breakers trip only at defined boundaries, so they miss the smaller discoveries between them, like the &quot;this approach isn't quite right&quot; signal on a Tuesday afternoon that the impulse to finish overrides.&lt;/p&gt;
&lt;h3 id=&quot;what-helps-when-awareness-isn-t-enough&quot;&gt;What Helps When Awareness Isn't Enough&lt;/h3&gt;
&lt;p&gt;Knowing about the bias doesn't prevent it, because the impulse operates faster than reflection. What worked for me wasn't trying harder to notice. It was changing what I thought the work was. Once each task was a bet on delivering some business value, my approach to it became an assumption about how to deliver that value, and assumptions get tested. A plan you treat as a commitment pulls you toward finishing it. A plan you treat as an assumption pulls you toward checking it. Reframing changes what stopping feels like, but the impulse stays, so the moments to stop still have to come from structure.&lt;/p&gt;
&lt;h4 id=&quot;checking-a-doubt-costs-less-than-building-on-it&quot;&gt;Checking a Doubt Costs Less Than Building on It&lt;/h4&gt;
&lt;p&gt;For most people, stopping feels like failure or waste. You were making progress and now you're not. But once a doubt has surfaced, evaluating it is usually cheaper than building more on top of it, and seeing it that way changes the emotional calculus even when the impulse is still there.&lt;/p&gt;
&lt;h4 id=&quot;low-switching-costs-keep-the-signal-audible&quot;&gt;Low Switching Costs Keep the Signal Audible&lt;/h4&gt;
&lt;p&gt;The impulse draws power from switching costs that accumulate with every hour of continued execution. Cheap experiments before commitment, small commits, well-defined interfaces, and feature flags all keep the cost of being wrong low for as long as possible.&lt;/p&gt;
&lt;h4 id=&quot;a-fixed-rhythm-beats-waiting-for-permission&quot;&gt;A Fixed Rhythm Beats Waiting for Permission&lt;/h4&gt;
&lt;p&gt;The Pomodoro technique was designed for productivity, but it accidentally created the kind of permission structure this bias requires. Every 25 minutes, you stop. That forced pause is a moment where &quot;am I still working on the right thing?&quot; can be asked without the social or psychological cost that usually prevents reassessment. The technique puts nothing about direction into the break, though, so the pause needs a switch criterion, written in advance, to check against. Events can trigger the same check, like a test that needs a workaround or an estimate that overruns.&lt;/p&gt;
&lt;p&gt;The natural objection is that these pauses cost working hours that would otherwise ship something. A check against a criterion written in advance is usually quick, though, and a doubt too big to check quickly deserves the time. Four hours of uninterrupted coding on a misunderstood problem doesn't produce the right solution, and the context-switching argument can't tell that case apart from the one where the four hours were well spent. Breaking focus on work you understood throws away context you had built. Breaking focus on work you hadn't examined throws away nothing you would want to keep.&lt;/p&gt;
&lt;h4 id=&quot;the-stop-test&quot;&gt;The Stop Test&lt;/h4&gt;
&lt;p&gt;The pause only helps if something happens inside it. The person asking whether to switch is usually the one who made the original decision, and Staw's finding predicts they'll lean toward their own choice. Itamar Simonson and Barry Staw later tested ways to counter that, and one that worked was setting, in advance, a minimum target that triggers a change if it isn't met. So before starting, write down what result, cost, or discovery would make you switch, and which assumptions the plan depends on, so an unexpected discovery has something to contradict.&lt;/p&gt;
&lt;p&gt;That puts the criterion before the sunk cost exists and gives every pause something to check against. It also guards against the opposite mistake, switching whenever a new idea merely feels better. The stop test is then three questions for the pause, answered against what you wrote down, ideally by someone who didn't make the original call:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Am I proving or testing?&lt;/strong&gt; Can this be made to work, or should it be?&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Is switching getting more expensive while I keep going?&lt;/strong&gt; Compare what you would throw away by switching now with what you would have thrown away an hour ago. If that pile keeps growing while the doubt sits unresolved, you are building the sunk cost that will later argue against switching.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;If I hadn't already started, would I choose this approach today?&lt;/strong&gt; This is Peter Drucker's question from The Effective Executive, &quot;If we did not already do this, would we go into it now?&quot;, scaled down from a program to a single task. A no here is the entire signal. Everything after it is negotiation with sunk cost.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;alignment-depends-on-the-ability-to-stop&quot;&gt;Alignment Depends on the Ability to Stop&lt;/h3&gt;
&lt;p&gt;You can't realign after discovery if you can't stop long enough to receive the discovery. You can't weigh a tradeoff you never paused to see, or catch a risk while you're busy proving the approach works. Alignment isn't something you achieve at kickoff. You recover it at every plan, discovery, commit, and test, and each recovery starts with a stop.&lt;/p&gt;
&lt;p&gt;So the work isn't only to try harder at noticing. The work is to arrange things so that stopping doesn't wait on noticing:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Rhythms that pause whether or not anything looks wrong, and check against criteria set before the work began&lt;/li&gt;
&lt;li&gt;Boundaries that expire on their own&lt;/li&gt;
&lt;li&gt;Commitments small enough that changing your mind costs less than defending it&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Build those, and the moment you would otherwise have missed arrives on a schedule that isn't yours to override.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/30158/planning-for-plan-continuation-bias-how-to-keep-building-the-right-thing</guid>
<pubDate>Wed, 07 Oct 2026 17:08:45 +0000</pubDate>
</item>
<item>
<title>Analyzing TP-Link Tapo Protocol Using Wireshark</title>
<link>https://coderlegion.com/30079/analyzing-tp-link-tapo-protocol-using-wireshark</link>
<description>&lt;h2 id=&quot;introduction&quot;&gt;Introduction&lt;/h2&gt;
&lt;p&gt;Wireshark dissector for a proprietary protocol found on TP-Link Tapo cameras, dissecting device discovery packets on ports &lt;code&gt;UDP/20002&lt;/code&gt; and &lt;code&gt;UDP/20004&lt;/code&gt;, as well as control and media stream packets on port &lt;code&gt;TCP/8800&lt;/code&gt; and keep-alive packets on port &lt;code&gt;TCP/8001&lt;/code&gt;. Tested on TP-Link Tapo C100 IP camera.&lt;/p&gt;
&lt;p&gt;While trying to understand how my personal accounts and personal data is being secured, I've done some research on TP-Link's Tapo Android application as well (it includes analysis of HTTP request signature and termID building processes), which can be found &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/KostasEreksonas/Tapo_application_analysis&quot;&gt;by following this link to a Github repo&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;filters&quot;&gt;Filters&lt;/h2&gt;
&lt;p&gt;The following filters are programmed for this Tapo protocol dissector:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th align=&quot;center&quot;&gt;Port&lt;/th&gt;&lt;th align=&quot;center&quot;&gt;Packet Type&lt;/th&gt;&lt;th align=&quot;center&quot;&gt;Field Name&lt;/th&gt;&lt;th align=&quot;center&quot;&gt;Description&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td align=&quot;center&quot;&gt;UDP/20002&lt;/td&gt;&lt;td align=&quot;center&quot;&gt;Device discovery packet&lt;/td&gt;&lt;td align=&quot;center&quot;&gt;tapo.header&lt;/td&gt;&lt;td align=&quot;center&quot;&gt;Full header, saved as raw bytes&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td align=&quot;center&quot;&gt;UDP/20002&lt;/td&gt;&lt;td align=&quot;center&quot;&gt;Device discovery packet&lt;/td&gt;&lt;td align=&quot;center&quot;&gt;tapo.version&lt;/td&gt;&lt;td align=&quot;center&quot;&gt;Packet version&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td align=&quot;center&quot;&gt;UDP/20002&lt;/td&gt;&lt;td align=&quot;center&quot;&gt;Device discovery packet&lt;/td&gt;&lt;td align=&quot;center&quot;&gt;tapo.type&lt;/td&gt;&lt;td align=&quot;center&quot;&gt;Packet type&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td align=&quot;center&quot;&gt;UDP/20002&lt;/td&gt;&lt;td align=&quot;center&quot;&gt;Device discovery packet&lt;/td&gt;&lt;td align=&quot;center&quot;&gt;tapo.opcode&lt;/td&gt;&lt;td align=&quot;center&quot;&gt;Operation code for current message&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td align=&quot;center&quot;&gt;UDP/20002&lt;/td&gt;&lt;td align=&quot;center&quot;&gt;Device discovery packet&lt;/td&gt;&lt;td align=&quot;center&quot;&gt;tapo.payload_length&lt;/td&gt;&lt;td align=&quot;center&quot;&gt;Payload length&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td align=&quot;center&quot;&gt;UDP/20002&lt;/td&gt;&lt;td align=&quot;center&quot;&gt;Device discovery packet&lt;/td&gt;&lt;td align=&quot;center&quot;&gt;tapo.flags&lt;/td&gt;&lt;td align=&quot;center&quot;&gt;Flags for current message&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td align=&quot;center&quot;&gt;UDP/20002&lt;/td&gt;&lt;td align=&quot;center&quot;&gt;Device discovery packet&lt;/td&gt;&lt;td align=&quot;center&quot;&gt;tapo.align&lt;/td&gt;&lt;td align=&quot;center&quot;&gt;Extra value for struct alignment&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td align=&quot;center&quot;&gt;UDP/20002&lt;/td&gt;&lt;td align=&quot;center&quot;&gt;Device discovery packet&lt;/td&gt;&lt;td align=&quot;center&quot;&gt;tapo.serial_number&lt;/td&gt;&lt;td align=&quot;center&quot;&gt;Serial number&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td align=&quot;center&quot;&gt;UDP/20002&lt;/td&gt;&lt;td align=&quot;center&quot;&gt;Device discovery packet&lt;/td&gt;&lt;td align=&quot;center&quot;&gt;tapo.checksum&lt;/td&gt;&lt;td align=&quot;center&quot;&gt;Cheksum&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td align=&quot;center&quot;&gt;UDP/20002&lt;/td&gt;&lt;td align=&quot;center&quot;&gt;Device discovery packet&lt;/td&gt;&lt;td align=&quot;center&quot;&gt;tapo.data&lt;/td&gt;&lt;td align=&quot;center&quot;&gt;JSON payload&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td align=&quot;center&quot;&gt;UDP/20002&lt;/td&gt;&lt;td align=&quot;center&quot;&gt;Device discovery packet&lt;/td&gt;&lt;td align=&quot;center&quot;&gt;tapo.binary&lt;/td&gt;&lt;td align=&quot;center&quot;&gt;Discovery packet + binary data&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td align=&quot;center&quot;&gt;TCP/8800&lt;/td&gt;&lt;td align=&quot;center&quot;&gt;Contol/media packet&lt;/td&gt;&lt;td align=&quot;center&quot;&gt;tapo.device_stream_boundary&lt;/td&gt;&lt;td align=&quot;center&quot;&gt;Device stream boundary&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td align=&quot;center&quot;&gt;TCP/8800&lt;/td&gt;&lt;td align=&quot;center&quot;&gt;Contol/media packet&lt;/td&gt;&lt;td align=&quot;center&quot;&gt;tapo.content_type&lt;/td&gt;&lt;td align=&quot;center&quot;&gt;Content Type&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td align=&quot;center&quot;&gt;TCP/8800&lt;/td&gt;&lt;td align=&quot;center&quot;&gt;Contol/media packet&lt;/td&gt;&lt;td align=&quot;center&quot;&gt;tapo.content_length&lt;/td&gt;&lt;td align=&quot;center&quot;&gt;Content Length&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td align=&quot;center&quot;&gt;TCP/8800&lt;/td&gt;&lt;td align=&quot;center&quot;&gt;Contol/media packet&lt;/td&gt;&lt;td align=&quot;center&quot;&gt;tapo.session_id&lt;/td&gt;&lt;td align=&quot;center&quot;&gt;Session ID&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td align=&quot;center&quot;&gt;TCP/8800&lt;/td&gt;&lt;td align=&quot;center&quot;&gt;Contol/media packet&lt;/td&gt;&lt;td align=&quot;center&quot;&gt;tapo.encrypted&lt;/td&gt;&lt;td align=&quot;center&quot;&gt;Payload encryption flag&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td align=&quot;center&quot;&gt;TCP/8001&lt;/td&gt;&lt;td align=&quot;center&quot;&gt;Keep-alive packet&lt;/td&gt;&lt;td align=&quot;center&quot;&gt;tapo.data&lt;/td&gt;&lt;td align=&quot;center&quot;&gt;JSON payload&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&quot;device-discovery-packets&quot;&gt;Device Discovery Packets&lt;/h2&gt;
&lt;p&gt;Ports &lt;code&gt;UDP/20002&lt;/code&gt; and &lt;code&gt;UDP/20004&lt;/code&gt; are used with configuration and cryptography-related information being sent.&lt;/p&gt;
&lt;h3 id=&quot;header-structure&quot;&gt;Header Structure&lt;/h3&gt;
&lt;p&gt;A writeup on &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.flashback.sh/blog/lao-bomb-tplink-archer-lan-rce&quot;&gt;command injection vulnerability within tdpServer daemon that listens on port UDP/200002&lt;/a&gt;, as well as a writeup on &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.synacktiv.com/publications/pwn2own-tokyo-2020-defeating-the-tp-link-ac1750&quot;&gt;remote code execution (RCE) vulnerability found in the TP-Link AC1750 Smart Wi-Fi router&lt;/a&gt;, describe that a device discovery packet has 16 bytes long header with it's fields defined as follows:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://coderlegion.com/?qa=blob&amp;amp;qa_blobid=5159285027642947917&quot; alt=&quot;Device discovery packet header schema&quot;&gt;&lt;/p&gt;
&lt;p&gt;The fields are identified as follows:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Byte 1:&lt;/strong&gt; Packet version.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Byte 2:&lt;/strong&gt; Packet type.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Bytes 3-4:&lt;/strong&gt; Packet opcode.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Bytes 5-6:&lt;/strong&gt; Packet payload length.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Byte 7:&lt;/strong&gt; Packet flags.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Byte 8:&lt;/strong&gt; Struct alignment.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Bytes 9-12:&lt;/strong&gt; Serial Number.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Bytes 13-16:&lt;/strong&gt; Checksum.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Below is the header's representation in Wireshark:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://coderlegion.com/?qa=blob&amp;amp;qa_blobid=1634966362717395479&quot; alt=&quot;Device discovery packet header in wireshark&quot;&gt;&lt;/p&gt;
&lt;p&gt;More information about header structure of the tapo device discovery packet can be found &lt;a rel=&quot;nofollow&quot; href=&quot;https://stackoverflow.com/questions/62493536/decoding-the-binary-part-of-a-discovery-udp-packet-payload-for-tp-link-tapo-c200&quot;&gt;on this Stack Overflow discussion thread&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;payloads&quot;&gt;Payloads&lt;/h3&gt;
&lt;p&gt;Observed payloads include:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Device configuration messages, some data encrypted with AES key:&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code class=&quot;language-json&quot;&gt;{
  &quot;error_code&quot;: 0,
  &quot;result&quot;: {
    &quot;device_id&quot;: &quot;REDACTED&quot;,
    &quot;device_name&quot;: &quot;Tapo C100&quot;,
    &quot;device_type&quot;: &quot;SMART.IPCAMERA&quot;,
    &quot;device_model&quot;: &quot;C100&quot;,
    &quot;ip&quot;: &quot;REDACTED&quot;,
    &quot;mac&quot;: &quot;REDACTED&quot;,
    &quot;hardware_version&quot;: &quot;4.0&quot;,
    &quot;firmware_version&quot;: &quot;1.1.21 Build 220511 Rel.42882n(4555)&quot;,
    &quot;factory_default&quot;: false,
    &quot;mgt_encrypt_schm&quot;: {
      &quot;is_support_https&quot;: true
    },
    &quot;encrypt_info&quot;: {
      &quot;sym_schm&quot;: &quot;AES&quot;,
      &quot;key&quot;: &quot;REDACTED&quot;,
      &quot;data&quot;: &quot;REDACTED&quot;
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;Public RSA key:&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code class=&quot;language-json&quot;&gt;{
  &quot;params&quot;: {
    &quot;rsa_key&quot;: &quot;-----BEGIN PUBLIC KEY-----\\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvzPNrLCSw3Rqf+AKwla2\\nntENZ+P4UUwGDzwvSc5HyJIYkY5+aQ8tpdQAO6NfN1JlVAUm94gxH5cw9s+xGgok\\nusbhFCHvMFp2g0CxvFg1P225+fq8Hmqqm9JN/ZkL2qwR4to6yvn256IqUxzmwKi8\\nFiX60VmAkmeJDOy8XGcN8ch385T9/sX1KyKZNxzX4z5xJEOlwcr5ZdhUeOmB6dbF\\njFF8GliOKSZdE01GVgeZPqJA21/8EQa1yJy8v31o+NNRd3QJaWjIzFqjGs1sl+Zj\\niPggKWcR/LqGt8Dd031velv2gVgmyBIjqi779cxns8IA5zYsvJbkxm0pFr3CU3GZ\\nYQIDAQAB\\n-----END PUBLIC
KEY-----\\n&quot;
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;control-and-media-packets&quot;&gt;Control and Media Packets&lt;/h2&gt;
&lt;p&gt;A proprietary protocol that manages JSON control messages and encrypted media (audio + video) streams runs on port &lt;code&gt;TCP/8800&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&quot;header-structure-1&quot;&gt;Header Structure&lt;/h3&gt;
&lt;p&gt;Header is formed from the first 28 bytes of a control and media packet. Fields are text based rather than byte-based:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th align=&quot;center&quot;&gt;Field Name&lt;/th&gt;&lt;th align=&quot;center&quot;&gt;Description&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td align=&quot;center&quot;&gt;----device-stream-boundary--&lt;/td&gt;&lt;td align=&quot;center&quot;&gt;Every control/media packet starts with this text as a start boundary&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td align=&quot;center&quot;&gt;Content-Type: &lt;/td&gt;&lt;td align=&quot;center&quot;&gt;&lt;code&gt;application/json&lt;/code&gt; for JSON payloads and &lt;code&gt;video/mp2t&lt;/code&gt; for video stream&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td align=&quot;center&quot;&gt;Content-Length: &lt;/td&gt;&lt;td align=&quot;center&quot;&gt;Length of a single message&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td align=&quot;center&quot;&gt;X-Session-Id: &lt;/td&gt;&lt;td align=&quot;center&quot;&gt;ID of a current session&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td align=&quot;center&quot;&gt;X-If-Encrypt: &lt;/td&gt;&lt;td align=&quot;center&quot;&gt;Payload encryption flag; 0 - unencrypted, 1 - encrypted&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&quot;payloads-1&quot;&gt;Payloads&lt;/h3&gt;
&lt;p&gt;Payloads of this protocol include JSON-formatted stream information and encrypted video stream with a type of &lt;code&gt;mp2t&lt;/code&gt;.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Video stream request:&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code class=&quot;language-json&quot;&gt;{
  &quot;params&quot;: {
    &quot;preview&quot;: {
      &quot;audio&quot;: [
        &quot;default&quot;
      ],
      &quot;audio_config&quot;: {
        &quot;encode_type&quot;: &quot;G711alaw&quot;,
        &quot;sample_rate&quot;: &quot;8&quot;
      },
      &quot;channels&quot;: [
        0
      ],
      &quot;deviceId&quot;: &quot;REDACTED&quot;,
      &quot;resolutions&quot;: [
        &quot;HD&quot;
      ],
      &quot;track_id&quot;: &quot;REDACTED&quot;
    },
    &quot;method&quot;: &quot;get&quot;
  },
  &quot;seq&quot;: 1,
  &quot;type&quot;: &quot;request&quot;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;Response on success:&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code class=&quot;language-json&quot;&gt;{
  &quot;type&quot;: &quot;response&quot;,
  &quot;seq&quot;: 1,
  &quot;params&quot;: {
    &quot;error_code&quot;: 0,
    &quot;session_id&quot;: &quot;1&quot;
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;Channel lens mask info:&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code class=&quot;language-json&quot;&gt;{
  &quot;type&quot;: &quot;notification&quot;,
  &quot;params&quot;: {
    &quot;event_type&quot;: &quot;channel_lens_mask_info&quot;,
    &quot;channels&quot;: 0,
    &quot;enabled&quot;: &quot;off&quot;
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;Channel resolution info:&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code class=&quot;language-json&quot;&gt;{
  &quot;type&quot;: &quot;notification&quot;,
  &quot;params&quot;: {
    &quot;event_type&quot;: &quot;channel_resolution_info&quot;,
    &quot;channels&quot;: 0,
    &quot;resolution&quot;: &quot;1280*720&quot;
  }
}
&lt;/code&gt;&lt;/pre&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/30079/analyzing-tp-link-tapo-protocol-using-wireshark</guid>
<pubDate>Wed, 07 Oct 2026 05:48:40 +0000</pubDate>
</item>
<item>
<title>We Promise Transparency. Not the &quot;Liquid Glass&quot; Kind.</title>
<link>https://coderlegion.com/29977/we-promise-transparency-not-the-liquid-glass-kind</link>
<description>&lt;p&gt;Let's get the joke out of the way first, because I can already see someone's fingers hovering over the comment box: no, when we talk about &quot;transparency&quot; here, we are not launching a translucent UI skin with rounded corners and a marketing deck about &quot;depth&quot; and &quot;light.&quot; Nobody's redesigning your icons to look like they're floating in a glass of water. Apple can keep that one. Transparency, as we mean it, isn't about making things look like glass. It's about making sure there's nothing opaque left to hide behind in the first place.&lt;/p&gt;
&lt;h3 id=&quot;transparency-is-the-rarest-feature-in-modern-software&quot;&gt;Transparency Is the Rarest Feature in Modern Software&lt;/h3&gt;
&lt;p&gt;Here's the uncomfortable observation that started all of this: almost nothing you run today will actually tell you what it's doing. You call a function, and underneath it, a dozen layers you never asked for quietly make decisions on your behalf — a runtime schedules something, an allocator decides where your data actually lives, a framework &quot;helpfully&quot; retries a failed call you didn't know could fail, a standard library wraps three other standard libraries you've never heard of. Most of the time that's a perfectly reasonable trade. Most software genuinely benefits from not making every engineer re-derive memory management from first principles on a Tuesday.&lt;/p&gt;
&lt;p&gt;But somewhere along the way, &quot;we abstracted this for your convenience&quot; quietly turned into &quot;we abstracted this so thoroughly that nobody on the team, including us, can tell you with certainty what your program actually does when it touches the kernel.&quot; That's not convenience anymore. That's a black box with a changelog. And an entire generation of developers has been trained to treat &quot;it works, don't ask why&quot; as an acceptable final answer, because asking why usually means descending through four more layers of someone else's unexamined assumptions.&lt;/p&gt;
&lt;p&gt;Developer transparency, the version we actually care about, is the opposite stance, stated plainly: no hidden magic, no invisible runtime quietly squatting on your CPU cycles like a tenant you never agreed to lease to, and no stack of &quot;convenience&quot; layers standing between your code and the hardware that's supposed to be running it. You should be able to point at any behaviour your program exhibits and trace it to a decision — yours, specifically — not to an abstraction that made the call for you somewhere you'll never see.&lt;/p&gt;
&lt;p&gt;This is where Gloria comes in, because it's the sharpest, most literal expression of that idea we've built so far.&lt;/p&gt;
&lt;h3 id=&quot;what-transparency-actually-looks-like-in-gloria&quot;&gt;What Transparency Actually Looks Like in Gloria&lt;/h3&gt;
&lt;p&gt;When you write something in Gloria, you know exactly what instruction goes to the CPU, exactly what memory gets mapped, and exactly which register is holding your data at any given moment. You are not down on one knee, hat in hand, begging a heavyweight framework or a sprawling C standard library to please do the thing for you. You own the execution, start to finish, with nobody else's fingerprints on it.&lt;/p&gt;
&lt;p&gt;From a Linux perspective, Gloria treats the kernel as its one and only direct interface. No linking against &lt;code&gt;glibc&lt;/code&gt;. No standard C startup routine quietly initializing half a universe of state before your &lt;code&gt;main&lt;/code&gt; function has even had the decency to run. Gloria generates self-contained, raw ELF64 binaries for AMD64, and that's genuinely all that's in there.&lt;/p&gt;
&lt;p&gt;When your code needs to open a socket, allocate memory, or spin up a bit of lightweight concurrency, it doesn't go politely knocking on some third-party wrapper function's door and waiting to be let in. It loads the syscall number straight into &lt;code&gt;RAX&lt;/code&gt;, lays out the arguments exactly per the System V AMD64 ABI, and hits the kernel directly with a &lt;code&gt;syscall&lt;/code&gt; instruction. No chaperone. No translation layer quietly editorializing your request on the way through. The resulting binaries stay under &lt;strong&gt;9KB&lt;/strong&gt;, because there is precisely zero runtime bloat riding along for the trip. Put that next to the multi-megabyte &quot;hello world&quot; some modern frameworks ship by default, and you start to get an honest sense of exactly how much weight we've actually cut.&lt;/p&gt;
&lt;h3 id=&quot;why-this-needed-to-exist-at-all&quot;&gt;Why This Needed to Exist at All&lt;/h3&gt;
&lt;p&gt;Here's the pitch, cynicism included at no extra charge: modern systems engineering has gone soft. Not incompetent — soft. Comfortable. Padded on every side with abstractions that exist specifically to protect you from having to understand what your own program is doing. Most software doesn't need to care which register holds a value. This project exists for the software that does, and for the engineers who'd rather know than be protected from knowing.&lt;/p&gt;
&lt;p&gt;We wanted something that gives you the raw control and speed of C, the type expressiveness and memory-safety thinking of Rust, and the dead-simple concurrency model of Go — minus the overhead each of those languages still quietly carries around. This is built for the specific kind of developer who needs to wring every last drop of performance out of a box, who's writing ultra-fast microservices, custom network infrastructure, or logic that has to survive on genuinely minimal resources, where every kilobyte and every CPU cycle is a line item somebody's actually tracking, not an abstraction nobody bothers to measure.&lt;/p&gt;
&lt;h3 id=&quot;the-roadmap-stated-without-the-usual-hedging&quot;&gt;The Roadmap, Stated Without the Usual Hedging&lt;/h3&gt;
&lt;p&gt;Our self-hosting plan is straightforward and, I'll admit, a little uncompromising. Right now, the ForgeZero compiler, &lt;code&gt;fz&lt;/code&gt;, is written in Go. That's a deliberate choice, not a compromise we're quietly hoping nobody notices — Go let us bootstrap the language fast, iterate on syntax without a chicken-and-egg problem slowing every decision down, and shake the remaining edge cases out of the lexer, parser, and semantic checker while the ground was still allowed to move under our feet.&lt;/p&gt;
&lt;p&gt;But Go is a bridge, not a destination. The actual milestone the entire Gloria ecosystem is being built toward is full self-hosting. We're building out the language constructs, inline assembly support, and memory primitives specifically so that Gloria can eventually compile its own compiler, from scratch, with nothing borrowed. The day &lt;code&gt;fz&lt;/code&gt; is rewritten in pure Gloria is the day we cut the last external dependency loose entirely — and the day this architecture stops being &quot;an interesting idea with training wheels&quot; and starts being something you can point at and say, flatly, that it's ready for serious production.&lt;/p&gt;
&lt;p&gt;That's the whole philosophy, really, scaled up from one register to an entire compiler: nothing in this stack should exist that we can't fully explain, and nothing should run that isn't accountable to someone who actually understands it.&lt;/p&gt;
&lt;h3 id=&quot;watch-it-happen-instead-of-reading-about-it-later&quot;&gt;Watch It Happen Instead of Reading About It Later&lt;/h3&gt;
&lt;p&gt;If any of this is the kind of problem that genuinely interests you — not the polished retrospective six months from now, but the actual process, bugs included — we post it all in close to real time on our Telegram channel, &lt;strong&gt;@forgezeroteam&lt;/strong&gt;. Build updates, broken things, fixed things, syntax decisions argued out loud before they're final. It's faster and considerably more honest than waiting for the cleaned-up version to show up here.&lt;/p&gt;
&lt;p&gt;No liquid glass. No rounded corners pretending to be depth. Just a binary you can actually read, a register you can actually name, and a compiler that, one day soon, won't owe its existence to anything but itself.&lt;/p&gt;
</description>
<category>Discussions</category>
<guid isPermaLink="true">https://coderlegion.com/29977/we-promise-transparency-not-the-liquid-glass-kind</guid>
<pubDate>Tue, 06 Oct 2026 11:10:46 +0000</pubDate>
</item>
<item>
<title>The Goldfish Brain: How AI fakes a memory it doesn't have, and why the illusion is so expensive</title>
<link>https://coderlegion.com/29873/the-goldfish-brain-how-ai-fakes-a-memory-it-doesnt-have-and-why-the-illusion-is-so-expensive</link>
<description>&lt;p&gt;&lt;em&gt;EP:5 The Curiosity Crunch&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;You've been chatting with an AI for an hour. You brainstormed a product name, told it an embarrassing story from college, and cracked a joke about your landlord. Twenty minutes later, it calls back to that joke at exactly the right moment.&lt;/p&gt;
&lt;p&gt;It feels personal. It feels like someone is listening, keeping notes, and getting to know you.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Nobody is. There's no one in there who remembers you.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Every time you press &lt;strong&gt;Send&lt;/strong&gt;, the AI starts from zero. It has no idea who you are, what you said a minute ago, or that a conversation is happening at all. It's the most convincing magic trick in modern tech, and by the end of this episode you'll know how it works.&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;The Crunch in one sentence:&lt;/strong&gt; an AI doesn't remember your conversation. Your chat app re-reads the whole thing to it, from the top, every single time.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h3 id=&quot;1-the-50-first-dates-problem&quot;&gt;1. The &lt;em&gt;50 First Dates&lt;/em&gt; problem&lt;/h3&gt;
&lt;p&gt;Models like ChatGPT, Claude and Gemini are &lt;strong&gt;stateless&lt;/strong&gt;. A model is a giant file of numbers (its &quot;weights&quot;) that was frozen when training ended. When you send a message, the model reads your text, produces a reply, and stops. Nothing about your conversation is written back into it. It never learns from the chat in real time, and it doesn't keep a file with your name on it.&lt;/p&gt;
&lt;p&gt;If you've seen &lt;em&gt;50 First Dates&lt;/em&gt;, you know the setup. Lucy wakes up every morning with her memory wiped. Henry gets around this by showing her a video that catches her up on everything she's missed.&lt;/p&gt;
&lt;p&gt;Your chat app is Henry, and it plays that video &lt;strong&gt;every time you speak&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;When you type a new message, the app doesn't send only that message. It grabs:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;the hidden instructions the company gave the model (&quot;you are a helpful assistant…&quot;),&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;everything you've said so far,&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;everything the AI has said so far,&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;your new message,&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;and glues them into one long document. That whole document goes to the model. The model reads it in a blink, writes the next reply, and forgets everything again.&lt;/p&gt;
&lt;p&gt;Here's roughly what your chat app sends on your &lt;em&gt;third&lt;/em&gt; message:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;[system]    You are a helpful assistant.
[user]      Help me name my coffee startup.
[assistant] How about &quot;Bean There&quot;?
[user]      Lol, my landlord says &quot;Bean There, Done That&quot; is his catchphrase.
[assistant] Ha! Maybe that's a sign. Want a few variations?
[user]      Can you make it funnier?        &amp;lt;-- the only part that's actually &quot;new&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The model sees all six lines as if for the first time. When it replies to &lt;em&gt;&quot;Can you make it funnier?&quot;&lt;/em&gt;, it can mention the landlord's catchphrase because the catchphrase is &lt;strong&gt;sitting right there in the text&lt;/strong&gt;, not because it remembered anything.&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;Try it yourself.&lt;/strong&gt; Use any AI's API (or a playground) and send only &lt;em&gt;&quot;Can you make it funnier?&quot;&lt;/em&gt; with no history. You'll get a confused answer. The model is the same. The only thing missing is the transcript.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h3 id=&quot;2-the-conveyor-belt-a-k-a-the-context-window-&quot;&gt;2. The conveyor belt (a.k.a. the context window)&lt;/h3&gt;
&lt;p&gt;That transcript can't be infinite. The maximum amount of text a model can read at once is called its &lt;strong&gt;context window&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Context windows are measured in &lt;strong&gt;tokens&lt;/strong&gt;, which are chunks of text, roughly three-quarters of a word each. Early chatbots could handle a few thousand tokens, about a few pages. Modern models handle hundreds of thousands of tokens, and a few reach a million or more, which is about the length of several novels.&lt;/p&gt;
&lt;p&gt;That sounds like plenty, but think of the context window as a &lt;strong&gt;conveyor belt&lt;/strong&gt; with a fixed length. Every message you send and every reply you receive goes onto the belt. When the belt is full, something has to fall off the end. Depending on the app, one of three things happens:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;What the app does &lt;/th&gt;&lt;th&gt;What you experience&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Truncates:&lt;/strong&gt; drops the oldest messages &lt;/td&gt;&lt;td&gt;The AI forgets the rule you gave it at the very start&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Summarizes:&lt;/strong&gt; compresses old messages into a short recap &lt;/td&gt;&lt;td&gt;The AI remembers the gist but loses exact wording and details&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Refuses:&lt;/strong&gt; tells you the conversation is too long &lt;/td&gt;&lt;td&gt;&quot;Please start a new chat&quot;&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;This is why, deep into a long chat, an AI suddenly breaks a rule you set in message one (&quot;always answer in bullet points&quot;). It didn't get lazy or stop listening. &lt;strong&gt;That message fell off the belt.&lt;/strong&gt; If it isn't in the transcript, then as far as the model is concerned, it never existed.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;3-but-chatgpt-does-remember-my-name-&quot;&gt;3. &quot;But ChatGPT &lt;em&gt;does&lt;/em&gt; remember my name&quot;&lt;/h3&gt;
&lt;p&gt;Fair objection. Some products now have a &lt;strong&gt;memory&lt;/strong&gt; feature. They really do seem to know your job, your dog's name and your coding style across completely separate chats.&lt;/p&gt;
&lt;p&gt;It's the same trick with better stage management.&lt;/p&gt;
&lt;p&gt;The app saves short notes about you in an ordinary database, something like &lt;em&gt;&quot;User is a backend engineer. Has a dog named Biscuit. Prefers concise answers.&quot;&lt;/em&gt; The next time you start a chat, the app quietly &lt;strong&gt;pastes those notes onto the top of the transcript&lt;/strong&gt; before sending it to the model.&lt;/p&gt;
&lt;p&gt;The model still remembers nothing. It's just handed a cheat sheet, and the cheat sheet lives in the app, not in the AI.&lt;/p&gt;
&lt;p&gt;That's the real distinction worth knowing:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The model&lt;/strong&gt; is stateless. Same input in, same kind of output out.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The app around the model&lt;/strong&gt; holds all the memory: chat history, saved notes, retrieved documents.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;When people say &quot;AI memory,&quot; they almost always mean the second thing.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;4-the-detective-s-corkboard-self-attention-&quot;&gt;4. The detective's corkboard (self-attention)&lt;/h3&gt;
&lt;p&gt;Now the puzzle. If the model has to re-read the &lt;em&gt;entire&lt;/em&gt; transcript on every message, how does it reply so quickly, and how does it know that &quot;it&quot; in &lt;em&gt;&quot;make it funnier&quot;&lt;/em&gt; refers to the landlord joke and not the startup name?&lt;/p&gt;
&lt;p&gt;The answer is the breakthrough behind modern AI, &lt;strong&gt;self-attention&lt;/strong&gt;, introduced in the 2017 paper &lt;em&gt;&quot;Attention Is All You Need.&quot;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Picture a detective in front of a corkboard covered in photos. Using red string, the detective connects pieces of evidence that relate to each other: this suspect to that location, that receipt to this timestamp.&lt;/p&gt;
&lt;p&gt;Attention does the same thing with words. For every token in the transcript, the model asks, &lt;em&gt;&quot;Which other tokens matter most for understanding this one?&quot;&lt;/em&gt; and draws a weighted red string to each of them. A thick string means &quot;very relevant,&quot; and a hair-thin string means &quot;mostly ignore.&quot;&lt;/p&gt;
&lt;p&gt;For &lt;em&gt;&quot;Can you make&lt;/em&gt; &lt;strong&gt;&lt;em&gt;it&lt;/em&gt;&lt;/strong&gt; &lt;em&gt;funnier?&quot;&lt;/em&gt;, the thickest string from &quot;it&quot; runs to the joke you told earlier. That's how the model works out what &quot;it&quot; means.&lt;/p&gt;
&lt;p&gt;Here's the part that surprises people. Models don't read left to right the way you do. When they take in your transcript, they process &lt;strong&gt;all the words in parallel&lt;/strong&gt;, which is exactly what GPUs are built for. That's a big reason the first part of the response (reading your whole history) is so fast.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;(One honest footnote: when the model&lt;/em&gt; &lt;strong&gt;&lt;em&gt;writes&lt;/em&gt;&lt;/strong&gt; &lt;em&gt;its reply, it does go one token at a time, left to right. That's why you see the answer stream onto the screen word by word. Reading is parallel and writing is sequential.)&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;5-the-staggering-cost-of-a-pdf&quot;&gt;5. The staggering cost of a PDF&lt;/h3&gt;
&lt;p&gt;Those red strings come at a price.&lt;/p&gt;
&lt;p&gt;If every token connects to every other token, then a transcript with &lt;strong&gt;N&lt;/strong&gt; tokens needs about &lt;strong&gt;N × N&lt;/strong&gt; connections. That's &lt;strong&gt;quadratic scaling&lt;/strong&gt;.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;Transcript length &lt;/th&gt;&lt;th&gt;Red strings to compute&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;1,000 tokens &lt;/td&gt;&lt;td&gt;~1 million&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;2,000 tokens &lt;/td&gt;&lt;td&gt;~4 million (2× the text, &lt;strong&gt;4×&lt;/strong&gt; the work)&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;10,000 tokens &lt;/td&gt;&lt;td&gt;~100 million (10× the text, &lt;strong&gt;100×&lt;/strong&gt; the work)&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;100,000 tokens &lt;/td&gt;&lt;td&gt;~10 &lt;strong&gt;billion&lt;/strong&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Doubling the document quadruples the work. This is why dropping a 100-page PDF into a chat is such a heavy lift, and why long-context models are expensive to run.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Engineers are fighting back.&lt;/strong&gt; Real systems don't pay the full bill every time. Here's what they use:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;KV caching.&lt;/strong&gt; The model saves the intermediate math for the part of the transcript it already processed. When you send message #31, it only needs to work on what's new, not redo messages #1 to #30. (Providers now even sell &quot;prompt caching&quot; as a discount feature.)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;FlashAttention and similar kernels.&lt;/strong&gt; Smarter GPU code that does the same math using far less memory and time.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Sparse and sliding-window attention.&lt;/strong&gt; Some models don't draw a string between &lt;em&gt;every&lt;/em&gt; pair of tokens, only the ones that probably matter.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So the honest picture is more nuanced than &quot;AI recomputes everything from scratch.&quot; Conceptually it re-reads everything, and in practice clever engineering skips the repeated work. But the underlying cost of long context is real, and it's why providers charge more for longer inputs and why &quot;just give it a million tokens&quot; isn't free.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;6-so-what-practical-takeaways&quot;&gt;6. So what? Practical takeaways&lt;/h3&gt;
&lt;p&gt;Understanding the trick makes you better at using the tool:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Long chats get worse, not better.&lt;/strong&gt; As the belt fills with old, irrelevant text, quality drops and rules get lost. Start a new chat for a new topic.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Repeat what matters.&lt;/strong&gt; If a rule is critical, restate it in your latest message. Don't trust that it survived 60 messages.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Front-load a summary.&lt;/strong&gt; Before a long session, paste in a short &quot;here's who I am and what we're doing&quot; block. You're doing manually what memory features do automatically.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Don't treat &quot;forgot&quot; as &quot;ignored you.&quot;&lt;/strong&gt; Usually the instruction was truncated or diluted, not defied.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Be careful what you paste.&lt;/strong&gt; Everything in the chat is re-sent to the model on every turn. That includes anything private you pasted in at the start.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h3 id=&quot;the-crunch-key-takeaways-&quot;&gt;The Crunch (key takeaways)&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;No memory in the model.&lt;/strong&gt; The AI is frozen and stateless. It lives in a permanent present tense.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The transcript trick.&lt;/strong&gt; Your chat app re-sends the entire conversation, plus hidden instructions, with every message.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The conveyor belt.&lt;/strong&gt; The context window is a fixed-size box. When it fills, old messages are dropped or summarized.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&quot;Memory&quot; features are sticky notes.&lt;/strong&gt; The app stores notes about you and pastes them into the prompt. The AI never learns you.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Attention connects the dots.&lt;/strong&gt; Red strings between tokens let the model understand &quot;it&quot; and &quot;that joke,&quot; and they're why the cost grows quadratically with length.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The illusion is engineered.&lt;/strong&gt; KV caching and smarter attention make it affordable, but it's still a lot of computation.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;The next time an AI nails a detail you mentioned an hour ago, don't be flattered. It didn't remember you.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;It just read the whole script again, faster than you can blink.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Until next time, keep crunching!!!&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;Enjoyed this? Forward it to the friend who says &quot;my AI knows me.&quot; And tell us what to crunch next: reply with the topic you're most curious about.&lt;/em&gt;&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/29873/the-goldfish-brain-how-ai-fakes-a-memory-it-doesnt-have-and-why-the-illusion-is-so-expensive</guid>
<pubDate>Mon, 05 Oct 2026 19:59:56 +0000</pubDate>
</item>
<item>
<title>Entitlements Are a Domain, Not a Feature Flag</title>
<link>https://coderlegion.com/29901/entitlements-are-a-domain-not-a-feature-flag</link>
<description>&lt;p&gt;It usually starts with one feature. The product team wants the new reporting dashboard available only on the enterprise plan, and there's no plan model in the code yet. The flag service is already wired in and supports targeting, and a segment called &lt;code&gt;enterprise-customers&lt;/code&gt; takes ten minutes to set up. A year later there are a dozen such segments. Every grant now goes through engineering, the code understands plans only as segment names, billing and access drift apart, and change logs are not sufficient to audit the proper state of customer product access. Most people involved feel the strain, but there's no clear way back.&lt;/p&gt;
&lt;p&gt;I think the way back starts with seeing that this was never a flag decision. Release toggles, experiments, and kill switches are decisions about how code behaves, made by the people who build and run it. Deciding which customers are owed a feature is a commercial decision, and it belongs to a business domain with its own owner, policies, and audits. Beyond the server-side and operational issues, a solution also has to account for client-side apps, which can quickly accelerate the pain and complicate a solution.&lt;/p&gt;
&lt;h3 id=&quot;feature-flags-decide-how-code-behaves&quot;&gt;Feature Flags Decide How Code Behaves&lt;/h3&gt;
&lt;p&gt;Martin Fowler's 2010 entry on feature toggles presents them as a way to keep unfinished work deployed but switched off on the main branch when a feature takes longer than a release cycle. Unleash, an open-source feature flag platform, still describes a release flag as a way to &quot;manage the release of new or incomplete features,&quot; with an expected lifetime of 40 days.&lt;/p&gt;
&lt;p&gt;Experiments and kill switches use the same switch to decide which version should ship or whether code should run right now.&lt;/p&gt;
&lt;p&gt;When the team that builds and runs the product decides how the code should behave, it's a feature flag. That holds even when a product manager runs the experiment, because no customer is owed the variant they landed in. By the same test, a beta promised to named design partners is owed for as long as the promise runs, so it's an entitlement. When sales, finance, or a contract decides who gets a feature, it's an entitlement that happens to use a flag. A flag service can still carry that answer, but then it runs one department's decisions through a tool built for another's.&lt;/p&gt;
&lt;h3 id=&quot;the-standard-references-treat-entitlement-as-a-flag-type&quot;&gt;The Standard References Treat Entitlement as a Flag Type&lt;/h3&gt;
&lt;p&gt;Pete Hodgson's catalog of toggle types, published on Fowler's site, sets permissioning toggles beside release, experiment, and ops toggles. Permissioning toggles change &quot;the features or product experience that certain users receive.&quot; Hodgson notes these &quot;may be very-long lived compared to other categories of Feature Toggles - at the scale of multiple years.&quot;&lt;/p&gt;
&lt;p&gt;The vendors built the category in. Unleash ships a permanent permission flag type to &quot;control feature access based on user roles or entitlements.&quot; LaunchDarkly's entitlements guide treats entitlement flags as permanent ones, which &quot;are part of the everyday operation of the application.&quot; A 2020 post on LaunchDarkly's blog by Dawn Parzych goes further: &quot;Instead of creating a custom build for a customer requesting a specific feature, wrap a flag around the feature and release it to that particular customer.&quot;&lt;/p&gt;
&lt;p&gt;A 2020 interview study by Meinicke, Wong, Vasilescu, and Kästner shows teams drifting there without deciding to. Practitioners &quot;usually do not distinguish between different kinds of flags,&quot; and &quot;a feature flag that initially guards a new feature may become a configuration option that should be only available for 'premium' users.&quot;&lt;/p&gt;
&lt;h3 id=&quot;what-a-flag-service-can-t-do-when-it-holds-the-entitlement&quot;&gt;What a Flag Service Can't Do When It Holds the Entitlement&lt;/h3&gt;
&lt;p&gt;Each of a flag service's design choices is likely sensible for a release decision and wrong for a commercial one.&lt;/p&gt;
&lt;h4 id=&quot;no-single-fallback-is-right-for-every-customer&quot;&gt;No Single Fallback Is Right for Every Customer&lt;/h4&gt;
&lt;p&gt;Flag clients are built to never break the application. OpenFeature, a vendor-neutral standard for flag evaluation APIs, requires in its specification that client methods &quot;MUST NOT throw exceptions&quot; and that evaluation calls &quot;must always return the default value in the event of abnormal execution.&quot; For a release flag, that's right, because falling back to off means the old behavior everyone has been running.&lt;/p&gt;
&lt;p&gt;For an entitlement there's no safe default. LaunchDarkly's entitlements guide warns that if the application can't connect, &quot;all of your end users will receive a single fallback variation.&quot; Default to off, and every enterprise customer loses what they bought. Default to on, and every free customer gets it.&lt;/p&gt;
&lt;p&gt;An entitlement service can fail too. A client that starts with nothing cached falls back to one default, no matter which system holds the grant. The difference is who decides how long a cached grant still counts. LaunchDarkly's offline-mode documentation says a client SDK that can't reach the vendor uses &quot;the latest stored flag variation values.&quot; Unless every app build adds its own expiry check, a cancelled customer's device keeps any paid feature that works offline for as long as it stays offline.&lt;/p&gt;
&lt;p&gt;An entitlement system makes that an explicit window. RevenueCat, which manages in-app subscriptions, fixes one for every app, so an entitlement active when a device went offline &quot;will remain active for up to three days.&quot; A team that owns its entitlements lets the plan owner set it.&lt;/p&gt;
&lt;h4 id=&quot;a-change-log-isn-t-an-audit-trail&quot;&gt;A Change Log Isn't an Audit Trail&lt;/h4&gt;
&lt;p&gt;When finance, support, or legal asks why a tenant has a feature, they want the agreement that granted it, when it started, and when it ends. A flag service records that someone added a tenant ID to a segment on a given day. Giving sales a role in the flag tool changes who edits, not what's recorded.&lt;/p&gt;
&lt;p&gt;Approvals, a comment, and a removal date can sit on that change, but the contract, trial, or sales exception behind it lives in a ticket or a Slack thread, if anywhere. Even a required contract ID is free text the flag service can't validate or query. Asking which tenants had a feature last quarter, and under which agreements, means replaying segment edits. A segment's history is enough for a rollout but not for a decision with money attached.&lt;/p&gt;
&lt;h4 id=&quot;billing-and-access-drift-apart&quot;&gt;Billing and Access Drift Apart&lt;/h4&gt;
&lt;p&gt;Once the flag service decides access, billing knows what customers bought and the flag service knows what they can use, and usually nothing reconciles the two. A downgrade in billing doesn't remove the tenant from the segment, and a sales exception granted in the segment never reaches billing. Schematic is an entitlement platform vendor with an interest in the point. It describes the common path: &quot;many teams use their feature flag tool for entitlements early on, and then realize the two concerns need to be separated as their pricing gets more complex.&quot;&lt;/p&gt;
&lt;p&gt;LaunchDarkly's guide offers segments that stay in sync with an external system, so billing could feed one, but only if every grant, sales exceptions included, goes into billing first. A separate domain still needs that discipline, but it can enforce it. Billing sends plan changes as events, and the entitlement store refuses any grant with no agreement behind it. Exceptions and trials start in the entitlement store and flow out to billing. A hand-edited segment can't refuse anything.&lt;/p&gt;
&lt;h3 id=&quot;entitlement-is-a-domain-with-its-own-owner&quot;&gt;Entitlement Is a Domain With Its Own Owner&lt;/h3&gt;
&lt;p&gt;Product access encodes the company's pricing, contracts, and obligations to each customer. It has an owner outside engineering, usually product and sales, policies about who may grant exceptions, and audit requirements because what a customer can use is part of what they're billed for.&lt;/p&gt;
&lt;p&gt;It also has its own model of plans that bundle features, contracts that override them with their own dates, expiring trials, and seat limits, none of which a segment expresses. That's the profile of a bounded context, not a flag type. Modeling it as its own domain, with grants that are dated and never overwritten, closes each gap above. A service answers &quot;may this tenant use this feature?&quot; from data it owns. Billing can host that domain if its catalog maps plans to features and also records exceptions and trials with their dates.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;Concern &lt;/th&gt;&lt;th&gt;Entitlement held in a flag service &lt;/th&gt;&lt;th&gt;Entitlement held in its own domain&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;Who grants access &lt;/td&gt;&lt;td&gt;Whoever edits segments, checked against nothing &lt;/td&gt;&lt;td&gt;Product and sales grant it, under their own exception policy&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;What a grant records &lt;/td&gt;&lt;td&gt;Who changed a segment, and when &lt;/td&gt;&lt;td&gt;The agreement behind it, with start and end dates&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;What a renewal touches &lt;/td&gt;&lt;td&gt;Removal dates edited per segment, linked to no contract &lt;/td&gt;&lt;td&gt;The contract's dates, once, and every plan feature follows&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;How billing stays in step &lt;/td&gt;&lt;td&gt;Two systems hold one fact, and neither wins &lt;/td&gt;&lt;td&gt;Billing sends plan changes in, and exceptions flow out to it&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;What happens in an outage &lt;/td&gt;&lt;td&gt;On clients, the last stored value with no expiry, or one coded fallback &lt;/td&gt;&lt;td&gt;A fallback and offline window the plan owner sets&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&quot;one-contract-can-justify-a-temporary-flag&quot;&gt;One Contract Can Justify a Temporary Flag&lt;/h3&gt;
&lt;p&gt;The hardest case is the one Parzych's advice describes. A single customer's contract requires a feature nobody else has asked for. For now, everything about it is in doubt, including its specification, its stability, whether it meets the customer's needs, and whether it will ever sell. The contract item itself isn't negotiable.&lt;/p&gt;
&lt;h4 id=&quot;two-decisions-share-one-flag&quot;&gt;Two Decisions Share One Flag&lt;/h4&gt;
&lt;p&gt;Who is owed the feature is settled. Whether the feature survives is the open question, and it's partly about the code and partly about the market.&lt;/p&gt;
&lt;p&gt;Engineering's usual reason to keep unproven code behind a flag is the ability to turn it off. For this tenant, turning it off breaches a contract, so the off switch is a commercial decision and not an operational one. The fallback problem returns in its starkest form, since no single default can be on for the one tenant who holds the contract and off for everyone else. Where an entitlement domain already exists, the grant goes there and the flag only isolates the code. Where none exists, the flag carries both decisions.&lt;/p&gt;
&lt;h4 id=&quot;every-exit-removes-the-flag&quot;&gt;Every Exit Removes the Flag&lt;/h4&gt;
&lt;p&gt;Neither the off switch nor the fallback problem goes away, but both can be accepted on purpose. Making an unproven feature that one contract requires into a plan feature would be premature, and the flag keeps the code isolated until the open question is answered. I think that makes this case a legitimate use of a flag, declared as intentional technical debt and owned by product and sales rather than engineering. The off switch and the outage fallback are now their risks to carry.&lt;/p&gt;
&lt;p&gt;That ownership means only product or sales may change the grant, and its exit trigger is recorded with the contract. If engineering needs an incident kill switch, it's a separate flag whose use product signed off on in advance.&lt;/p&gt;
&lt;p&gt;What separates it from Hodgson's multi-year permissioning toggles is that it has a decision point, due no later than the first renewal, and every way out of that decision removes the flag:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Another customer wants it.&lt;/strong&gt; The grant moves into the entitlement model as a plan feature, and the flag goes away.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;It becomes standard.&lt;/strong&gt; The code path becomes permanent and the flag is deleted.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The contract ends or the feature fails.&lt;/strong&gt; The code is removed with the flag.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Parzych's advice is right on the first day. It becomes a problem only when nobody records the exits and the flag turns into a permanent grant with no owner.&lt;/p&gt;
&lt;h3 id=&quot;on-client-apps-vendor-coupling-compounds-the-entitlement-problem&quot;&gt;On Client Apps, Vendor Coupling Compounds the Entitlement Problem&lt;/h3&gt;
&lt;p&gt;Mobile, desktop, and browser apps need the same answer as servers, sometimes offline, and flags usually reach them through the vendor's SDK.&lt;/p&gt;
&lt;h4 id=&quot;clients-still-need-flags-but-never-decide-entitlements&quot;&gt;Clients Still Need Flags, but Never Decide Entitlements&lt;/h4&gt;
&lt;p&gt;Client apps arguably need flags more than servers do, since a client fix waits on app store review and on users who don't update. But the client is never the authority on an entitlement. It can hold a grant the server issued for as long as the plan's rules trust it, but the server enforces, and the client only decides what to show.&lt;/p&gt;
&lt;h4 id=&quot;the-vendor-s-sdk-ties-every-build-to-the-vendor&quot;&gt;The Vendor's SDK Ties Every Build to the Vendor&lt;/h4&gt;
&lt;p&gt;The vendor's SDK, and its representation of the answer, become part of every installed build. Swapping vendors waits on app releases, and until old builds disappear, you can't put your own layer between the client and the vendor. The layer serving the answer isn't yours either, so you can't merge sources, set expiry and invalidation for each kind of data, or push your own change events. I think direct vendor SDK access from client apps is a trap, though many teams accept it for the streaming updates and built-in experiment analysis that come with it.&lt;/p&gt;
&lt;p&gt;Entitlements in the flag service make that coupling worse. Every build then depends on the vendor for what customers are owed. Exposure gets worse too. LaunchDarkly's secure mode exists because a malicious end user &quot;could use a context or user key to identify what flag values another end user receives,&quot; and it covers only its JavaScript-based SDKs. Its other SDKs have no such protection. If tenant keys are readable slugs like &lt;code&gt;acme&lt;/code&gt;, a modified client can probe flags and learn which paid features a named customer holds, even when the server enforces every grant.&lt;/p&gt;
&lt;h4 id=&quot;a-projection-fixes-the-source-but-not-the-layer&quot;&gt;A Projection Fixes the Source but Not the Layer&lt;/h4&gt;
&lt;p&gt;For a team already deep in the hack, a first step back is fixing where entitlements come from while the flag service keeps serving them. I've used this shape before, with a read store that only receives changes from the audited business store and is never written to directly. The flag service becomes a projection of an entitlement store that sales and product own.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt; ┌──────────────────────────────────┐
 │ Entitlement store                │ &amp;lt;── sales and product write
 │ audited source of truth          │
 └────────────────┬─────────────────┘
                  │ events (only writer)
                  v
 ┌──────────────────────────────────┐
 │ Flag service                     │
 │ release flags + entitlement      │
 │ segments, rebuilt from source    │
 └────────────────┬─────────────────┘
                  │ vendor SDK
                  v
               Client
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That makes LaunchDarkly's synced segment, described earlier, a rule instead of a hope, and two conditions keep the projection honest. Only the sync integration may write to entitlement segments, enforced in the vendor's permissions rather than by agreement, and the whole projection can be rebuilt by replaying the source. It works, but the sync and the rebuild can each break. It also leaves the device getting grants through the vendor's SDK, with every problem above.&lt;/p&gt;
&lt;h4 id=&quot;entitlements-can-ride-on-an-endpoint-you-already-own&quot;&gt;Entitlements Can Ride on an Endpoint You Already Own&lt;/h4&gt;
&lt;p&gt;The cheaper way out takes grants off the vendor's path entirely, and it may not need a new endpoint. The app already asks the server who the user is and which tenant they belong to, and that session or account lookup can return the tenant's grants with the answer:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-json&quot;&gt;{
  &quot;user&quot;: &quot;u_1842&quot;,
  &quot;tenant&quot;: &quot;acme&quot;,
  &quot;entitlements&quot;: {
    &quot;generatedAt&quot;: &quot;2026-10-05T14:00:00Z&quot;,
    &quot;refreshAfterSeconds&quot;: 3600,
    &quot;staleSeconds&quot;: 259200,
    &quot;grants&quot;: [&quot;advanced-reporting&quot;]
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;One number says how often to refresh, and the other says how long a cached answer still counts when a refresh fails. Contract grants change rarely, so hourly is plenty, and an in-app purchase forces a refresh. The plan owner decides grants stay trusted for three days offline. That window is a promise, so data a user creates under a cached grant has to sync even if the grant ended while the device was offline. What happens to that data next is the plan owner's call too. &lt;code&gt;generatedAt&lt;/code&gt; lets the client tell how old its copy is, and nothing in the exchange touches the flag vendor.&lt;/p&gt;
&lt;h4 id=&quot;a-facade-decouples-flags-from-the-sdk-as-a-separate-choice&quot;&gt;A Facade Decouples Flags From the SDK, as a Separate Choice&lt;/h4&gt;
&lt;p&gt;Release flags still come through the vendor's SDK, and moving them can wait. The usual shape is a thin endpoint you own in front of the flag service. It's the kind of server component Sam Newman describes in his backends-for-frontends pattern. The vendor's contract ends at the server. If one endpoint serves both grants and flags, the response should keep them apart, because one flat map of booleans can't tell a release toggle from a contract grant.&lt;/p&gt;
&lt;p&gt;The SDK also records who saw each variation, for experiments. Behind a facade, the app takes over that job. Statsig's experimentation guidance explains that counting users who never reached a feature dilutes an experiment's result. For a change that lives entirely in the interface, only the client knows whether the user reached it.&lt;/p&gt;
&lt;p&gt;A facade also gives up the vendor's real-time streaming of flag changes, so a client kill switch waits for the next refresh. A change too urgent for that usually belongs on the server. A client-only one, like disabling a crashing native screen, is a reason to keep release flags on the vendor's SDK.&lt;/p&gt;
&lt;h3 id=&quot;a-flag-can-stand-in-when-the-gap-is-written-down&quot;&gt;A Flag Can Stand In When the Gap Is Written Down&lt;/h3&gt;
&lt;p&gt;None of this means a startup with one paid tier needs an entitlement service on day one. But I think holding product access in a flag is still a hack, and the only good reason for it is a timeline or budget that would otherwise block delivery. Even then it's a cost, because paying it off means refactoring the code and migrating the grants. Someone should decide it on purpose and write down three things: that the flag is standing in for an entitlement domain, who owns that gap, and what will trigger paying it off. A good trigger is the first grant no plan explains, such as a sales exception, after which segments hold facts billing doesn't.&lt;/p&gt;
&lt;p&gt;Unstated, the gap compounds. Each segment and sales exception adds to a later migration. Each client build that reads entitlements from the vendor's SDK adds a contract you can't recall. And the flag service keeps gaining authority because nobody recorded that it was never supposed to have any.&lt;/p&gt;
&lt;p&gt;Hacking feature flags can work because each wrong answer is usually recoverable on its own. A tenant who gets a feature a day late complains and gets a credit. No credit undoes exposing what other customers bought, though. A different system layer that decides who can sign in, move money, or read another tenant's data has no such margin, and the same shortcut is never an option. A hack can hold together for a while, but the leaks will show and the costs will add up.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/29901/entitlements-are-a-domain-not-a-feature-flag</guid>
<pubDate>Mon, 05 Oct 2026 18:58:45 +0000</pubDate>
</item>
<item>
<title>The Goldfish Brain: How AI fakes a memory it doesn't have, and why the illusion is so expensive</title>
<link>https://coderlegion.com/29872/the-goldfish-brain-how-ai-fakes-a-memory-it-doesnt-have-and-why-the-illusion-is-so-expensive</link>
<description>&lt;p&gt;&lt;em&gt;EP:5 The Curiosity Crunch&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;You've been chatting with an AI for an hour. You brainstormed a product name, told it an embarrassing story from college, and cracked a joke about your landlord. Twenty minutes later, it calls back to that joke at exactly the right moment.&lt;/p&gt;
&lt;p&gt;It feels personal. It feels like someone is listening, keeping notes, and getting to know you.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Nobody is. There's no one in there who remembers you.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Every time you press &lt;strong&gt;Send&lt;/strong&gt;, the AI starts from zero. It has no idea who you are, what you said a minute ago, or that a conversation is happening at all. It's the most convincing magic trick in modern tech, and by the end of this episode you'll know how it works.&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;The Crunch in one sentence:&lt;/strong&gt; an AI doesn't remember your conversation. Your chat app re-reads the whole thing to it, from the top, every single time.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h3 id=&quot;1-the-50-first-dates-problem-1&quot;&gt;1. The &lt;em&gt;50 First Dates&lt;/em&gt; problem&lt;/h3&gt;
&lt;p&gt;Models like ChatGPT, Claude and Gemini are &lt;strong&gt;stateless&lt;/strong&gt;. A model is a giant file of numbers (its &quot;weights&quot;) that was frozen when training ended. When you send a message, the model reads your text, produces a reply, and stops. Nothing about your conversation is written back into it. It never learns from the chat in real time, and it doesn't keep a file with your name on it.&lt;/p&gt;
&lt;p&gt;If you've seen &lt;em&gt;50 First Dates&lt;/em&gt;, you know the setup. Lucy wakes up every morning with her memory wiped. Henry gets around this by showing her a video that catches her up on everything she's missed.&lt;/p&gt;
&lt;p&gt;Your chat app is Henry, and it plays that video &lt;strong&gt;every time you speak&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;When you type a new message, the app doesn't send only that message. It grabs:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;the hidden instructions the company gave the model (&quot;you are a helpful assistant…&quot;),&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;everything you've said so far,&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;everything the AI has said so far,&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;your new message,&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;and glues them into one long document. That whole document goes to the model. The model reads it in a blink, writes the next reply, and forgets everything again.&lt;/p&gt;
&lt;p&gt;Here's roughly what your chat app sends on your &lt;em&gt;third&lt;/em&gt; message:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;[system]    You are a helpful assistant.
[user]      Help me name my coffee startup.
[assistant] How about &quot;Bean There&quot;?
[user]      Lol, my landlord says &quot;Bean There, Done That&quot; is his catchphrase.
[assistant] Ha! Maybe that's a sign. Want a few variations?
[user]      Can you make it funnier?        &amp;lt;-- the only part that's actually &quot;new&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The model sees all six lines as if for the first time. When it replies to &lt;em&gt;&quot;Can you make it funnier?&quot;&lt;/em&gt;, it can mention the landlord's catchphrase because the catchphrase is &lt;strong&gt;sitting right there in the text&lt;/strong&gt;, not because it remembered anything.&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;Try it yourself.&lt;/strong&gt; Use any AI's API (or a playground) and send only &lt;em&gt;&quot;Can you make it funnier?&quot;&lt;/em&gt; with no history. You'll get a confused answer. The model is the same. The only thing missing is the transcript.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h3 id=&quot;2-the-conveyor-belt-a-k-a-the-context-window--1&quot;&gt;2. The conveyor belt (a.k.a. the context window)&lt;/h3&gt;
&lt;p&gt;That transcript can't be infinite. The maximum amount of text a model can read at once is called its &lt;strong&gt;context window&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Context windows are measured in &lt;strong&gt;tokens&lt;/strong&gt;, which are chunks of text, roughly three-quarters of a word each. Early chatbots could handle a few thousand tokens, about a few pages. Modern models handle hundreds of thousands of tokens, and a few reach a million or more, which is about the length of several novels.&lt;/p&gt;
&lt;p&gt;That sounds like plenty, but think of the context window as a &lt;strong&gt;conveyor belt&lt;/strong&gt; with a fixed length. Every message you send and every reply you receive goes onto the belt. When the belt is full, something has to fall off the end. Depending on the app, one of three things happens:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;What the app does &lt;/th&gt;&lt;th&gt;What you experience&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Truncates:&lt;/strong&gt; drops the oldest messages &lt;/td&gt;&lt;td&gt;The AI forgets the rule you gave it at the very start&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Summarizes:&lt;/strong&gt; compresses old messages into a short recap &lt;/td&gt;&lt;td&gt;The AI remembers the gist but loses exact wording and details&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Refuses:&lt;/strong&gt; tells you the conversation is too long &lt;/td&gt;&lt;td&gt;&quot;Please start a new chat&quot;&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;This is why, deep into a long chat, an AI suddenly breaks a rule you set in message one (&quot;always answer in bullet points&quot;). It didn't get lazy or stop listening. &lt;strong&gt;That message fell off the belt.&lt;/strong&gt; If it isn't in the transcript, then as far as the model is concerned, it never existed.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;3-but-chatgpt-does-remember-my-name--1&quot;&gt;3. &quot;But ChatGPT &lt;em&gt;does&lt;/em&gt; remember my name&quot;&lt;/h3&gt;
&lt;p&gt;Fair objection. Some products now have a &lt;strong&gt;memory&lt;/strong&gt; feature. They really do seem to know your job, your dog's name and your coding style across completely separate chats.&lt;/p&gt;
&lt;p&gt;It's the same trick with better stage management.&lt;/p&gt;
&lt;p&gt;The app saves short notes about you in an ordinary database, something like &lt;em&gt;&quot;User is a backend engineer. Has a dog named Biscuit. Prefers concise answers.&quot;&lt;/em&gt; The next time you start a chat, the app quietly &lt;strong&gt;pastes those notes onto the top of the transcript&lt;/strong&gt; before sending it to the model.&lt;/p&gt;
&lt;p&gt;The model still remembers nothing. It's just handed a cheat sheet, and the cheat sheet lives in the app, not in the AI.&lt;/p&gt;
&lt;p&gt;That's the real distinction worth knowing:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The model&lt;/strong&gt; is stateless. Same input in, same kind of output out.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The app around the model&lt;/strong&gt; holds all the memory: chat history, saved notes, retrieved documents.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;When people say &quot;AI memory,&quot; they almost always mean the second thing.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;4-the-detective-s-corkboard-self-attention--1&quot;&gt;4. The detective's corkboard (self-attention)&lt;/h3&gt;
&lt;p&gt;Now the puzzle. If the model has to re-read the &lt;em&gt;entire&lt;/em&gt; transcript on every message, how does it reply so quickly, and how does it know that &quot;it&quot; in &lt;em&gt;&quot;make it funnier&quot;&lt;/em&gt; refers to the landlord joke and not the startup name?&lt;/p&gt;
&lt;p&gt;The answer is the breakthrough behind modern AI, &lt;strong&gt;self-attention&lt;/strong&gt;, introduced in the 2017 paper &lt;em&gt;&quot;Attention Is All You Need.&quot;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Picture a detective in front of a corkboard covered in photos. Using red string, the detective connects pieces of evidence that relate to each other: this suspect to that location, that receipt to this timestamp.&lt;/p&gt;
&lt;p&gt;Attention does the same thing with words. For every token in the transcript, the model asks, &lt;em&gt;&quot;Which other tokens matter most for understanding this one?&quot;&lt;/em&gt; and draws a weighted red string to each of them. A thick string means &quot;very relevant,&quot; and a hair-thin string means &quot;mostly ignore.&quot;&lt;/p&gt;
&lt;p&gt;For &lt;em&gt;&quot;Can you make&lt;/em&gt; &lt;strong&gt;&lt;em&gt;it&lt;/em&gt;&lt;/strong&gt; &lt;em&gt;funnier?&quot;&lt;/em&gt;, the thickest string from &quot;it&quot; runs to the joke you told earlier. That's how the model works out what &quot;it&quot; means.&lt;/p&gt;
&lt;p&gt;Here's the part that surprises people. Models don't read left to right the way you do. When they take in your transcript, they process &lt;strong&gt;all the words in parallel&lt;/strong&gt;, which is exactly what GPUs are built for. That's a big reason the first part of the response (reading your whole history) is so fast.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;(One honest footnote: when the model&lt;/em&gt; &lt;strong&gt;&lt;em&gt;writes&lt;/em&gt;&lt;/strong&gt; &lt;em&gt;its reply, it does go one token at a time, left to right. That's why you see the answer stream onto the screen word by word. Reading is parallel and writing is sequential.)&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;5-the-staggering-cost-of-a-pdf-1&quot;&gt;5. The staggering cost of a PDF&lt;/h3&gt;
&lt;p&gt;Those red strings come at a price.&lt;/p&gt;
&lt;p&gt;If every token connects to every other token, then a transcript with &lt;strong&gt;N&lt;/strong&gt; tokens needs about &lt;strong&gt;N × N&lt;/strong&gt; connections. That's &lt;strong&gt;quadratic scaling&lt;/strong&gt;.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;Transcript length &lt;/th&gt;&lt;th&gt;Red strings to compute&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;1,000 tokens &lt;/td&gt;&lt;td&gt;~1 million&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;2,000 tokens &lt;/td&gt;&lt;td&gt;~4 million (2× the text, &lt;strong&gt;4×&lt;/strong&gt; the work)&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;10,000 tokens &lt;/td&gt;&lt;td&gt;~100 million (10× the text, &lt;strong&gt;100×&lt;/strong&gt; the work)&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;100,000 tokens &lt;/td&gt;&lt;td&gt;~10 &lt;strong&gt;billion&lt;/strong&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Doubling the document quadruples the work. This is why dropping a 100-page PDF into a chat is such a heavy lift, and why long-context models are expensive to run.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Engineers are fighting back.&lt;/strong&gt; Real systems don't pay the full bill every time. Here's what they use:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;KV caching.&lt;/strong&gt; The model saves the intermediate math for the part of the transcript it already processed. When you send message #31, it only needs to work on what's new, not redo messages #1 to #30. (Providers now even sell &quot;prompt caching&quot; as a discount feature.)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;FlashAttention and similar kernels.&lt;/strong&gt; Smarter GPU code that does the same math using far less memory and time.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Sparse and sliding-window attention.&lt;/strong&gt; Some models don't draw a string between &lt;em&gt;every&lt;/em&gt; pair of tokens, only the ones that probably matter.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So the honest picture is more nuanced than &quot;AI recomputes everything from scratch.&quot; Conceptually it re-reads everything, and in practice clever engineering skips the repeated work. But the underlying cost of long context is real, and it's why providers charge more for longer inputs and why &quot;just give it a million tokens&quot; isn't free.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;6-so-what-practical-takeaways-1&quot;&gt;6. So what? Practical takeaways&lt;/h3&gt;
&lt;p&gt;Understanding the trick makes you better at using the tool:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Long chats get worse, not better.&lt;/strong&gt; As the belt fills with old, irrelevant text, quality drops and rules get lost. Start a new chat for a new topic.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Repeat what matters.&lt;/strong&gt; If a rule is critical, restate it in your latest message. Don't trust that it survived 60 messages.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Front-load a summary.&lt;/strong&gt; Before a long session, paste in a short &quot;here's who I am and what we're doing&quot; block. You're doing manually what memory features do automatically.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Don't treat &quot;forgot&quot; as &quot;ignored you.&quot;&lt;/strong&gt; Usually the instruction was truncated or diluted, not defied.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Be careful what you paste.&lt;/strong&gt; Everything in the chat is re-sent to the model on every turn. That includes anything private you pasted in at the start.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h3 id=&quot;the-crunch-key-takeaways--1&quot;&gt;The Crunch (key takeaways)&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;No memory in the model.&lt;/strong&gt; The AI is frozen and stateless. It lives in a permanent present tense.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The transcript trick.&lt;/strong&gt; Your chat app re-sends the entire conversation, plus hidden instructions, with every message.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The conveyor belt.&lt;/strong&gt; The context window is a fixed-size box. When it fills, old messages are dropped or summarized.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&quot;Memory&quot; features are sticky notes.&lt;/strong&gt; The app stores notes about you and pastes them into the prompt. The AI never learns you.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Attention connects the dots.&lt;/strong&gt; Red strings between tokens let the model understand &quot;it&quot; and &quot;that joke,&quot; and they're why the cost grows quadratically with length.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The illusion is engineered.&lt;/strong&gt; KV caching and smarter attention make it affordable, but it's still a lot of computation.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;The next time an AI nails a detail you mentioned an hour ago, don't be flattered. It didn't remember you.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;It just read the whole script again, faster than you can blink.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Until next time, keep crunching!!!&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;Enjoyed this? Forward it to the friend who says &quot;my AI knows me.&quot; And tell us what to crunch next: reply with the topic you're most curious about.&lt;/em&gt;&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/29872/the-goldfish-brain-how-ai-fakes-a-memory-it-doesnt-have-and-why-the-illusion-is-so-expensive</guid>
<pubDate>Mon, 05 Oct 2026 15:37:37 +0000</pubDate>
</item>
<item>
<title>Hi, I'm the Guy Who Got Angry at Garbage Collectors and Built an Entire Toolchain About It</title>
<link>https://coderlegion.com/29880/hi-im-the-guy-who-got-angry-at-garbage-collectors-and-built-an-entire-toolchain-about-it</link>
<description>&lt;p&gt;Hey, everyone. Alex Voste here — the person behind ForgeZero and its resident experimental language, Gloria. I've written plenty about the engineering internals on this blog already, but I realized I never actually introduced the &lt;em&gt;why&lt;/em&gt;, or told you where to go if you want to watch this thing get built in real time instead of reading about it after the fact. Let's fix both.&lt;/p&gt;
&lt;h3 id=&quot;the-origin-story-condensed&quot;&gt;The Origin Story, Condensed&lt;/h3&gt;
&lt;p&gt;I wanted total control. Over the code, over the hardware, over every byte between my logic and the CPU actually executing it. What I kept running into instead was an industry quietly content with heavy runtimes, abstraction stacked on abstraction, and garbage collectors that show up uninvited in the middle of your hot path like a relative who &quot;was just in the neighbourhood.&quot; None of that is evil. Most of it is genuinely useful for most people. It is also, for the specific thing I wanted to build, a tax I was no longer willing to pay.&lt;/p&gt;
&lt;p&gt;So I built &lt;strong&gt;ForgeZero&lt;/strong&gt; — an orchestration toolchain for assembly, C, C++, Objective-C, and Gloria, designed to get projects built without Makefiles, without a pile of configuration nobody fully understands, and without the &quot;trust me&quot; energy most build systems ask of you. It's fast, it's memory-conscious, and critically, it never asks you to take anything on faith — every cache decision, every toolchain choice, every flag is something you can actually see, not something an abstraction layer quietly decided on your behalf. GitHub's right here: &lt;strong&gt;github.com/forgezero-cli&lt;/strong&gt;.&lt;/p&gt;
&lt;h3 id=&quot;then-gloria-happened-because-one-obsession-is-never-enough&quot;&gt;Then Gloria Happened, Because One Obsession Is Never Enough&lt;/h3&gt;
&lt;p&gt;Once ForgeZero existed, I wanted a language to go with it — something low-level and genuinely powerful, but without the syntax feeling like a punishment for wanting control. You've probably already seen a few of the pieces I'm proudest of: chained file operations with &lt;code&gt;&amp;gt;&amp;gt;&lt;/code&gt;, error propagation with &lt;code&gt;|?&lt;/code&gt; that doesn't force you into a pyramid of nested checks, and &lt;code&gt;repeat&lt;/code&gt; loops that read the way you'd actually describe the loop out loud instead of the way a 1970s compiler wanted you to describe it.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://coderlegion.com/?qa=blob&amp;amp;qa_blobid=2944738903716608593&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;There is no garbage collector in Gloria. There are no comfortable abstractions sitting between you and the machine. Gloria compiles straight to raw x86_64 binary and talks to the operating system directly through syscalls, because every layer between your intent and the instruction stream is a layer that can lie to you about what's actually happening. I'd rather know exactly what's happening, even when what's happening is occasionally my own fault.&lt;/p&gt;
&lt;h3 id=&quot;yes-i-use-ai-no-it-doesn-t-get-to-make-the-decisions-&quot;&gt;Yes, I Use AI. No, It Doesn't Get to Make the Decisions.&lt;/h3&gt;
&lt;p&gt;I don't build this alone — I keep a neural net around as a constant sparring partner. It's genuinely useful for kicking around new syntax ideas, helping refactor, stress-testing a concept before I commit to it, and yes, occasionally helping me phrase a post like this one without sounding like I've been awake for thirty hours (I frequently have been).&lt;/p&gt;
&lt;p&gt;What it doesn't get is unsupervised trust. If you've read my other posts here, you already know my relationship with AI-generated code is closer to &quot;overconfident intern I review like a hostile code auditor&quot; than &quot;pair programmer I defer to.&quot; It's a tool that accelerates thinking. It is not the thing doing the thinking, and it never gets anywhere near the parts of this project where a mistake means a corrupted binary instead of an awkward sentence.&lt;/p&gt;
&lt;h3 id=&quot;where-gloria-actually-stands-right-now&quot;&gt;Where Gloria Actually Stands Right Now&lt;/h3&gt;
&lt;p&gt;We've got solid, working filesystem interaction — reading and writing data confidently, not as a toy demo. Right now the active front line is &lt;strong&gt;dynamic arrays (vectors)&lt;/strong&gt;, which isn't a feature for its own sake — it's the foundation everything downstream actually needs, the language and the compiler both.&lt;/p&gt;
&lt;h3 id=&quot;where-this-is-going&quot;&gt;Where This Is Going&lt;/h3&gt;
&lt;p&gt;Networking is next — full socket support, which cracks the door open to high-performance servers, network utilities, and game engines written in Gloria, with nothing hiding between your code and the actual packets going over the wire.&lt;/p&gt;
&lt;p&gt;And the actual north star, the thing I'm building all of this toward: &lt;strong&gt;self-hosting.&lt;/strong&gt; The day ForgeZero's compiler for Gloria is itself written in Gloria is the day this entire project proves its own premise. Until a language can bootstrap itself, it's still borrowing credibility from whatever built it. I want Gloria standing entirely on its own.&lt;/p&gt;
&lt;h3 id=&quot;come-build-it-with-us&quot;&gt;Come Build It With Us&lt;/h3&gt;
&lt;p&gt;If any of this sounds like your kind of problem — if you genuinely enjoy getting into the hardware, obsessing over a single instruction more than is strictly healthy, and want to watch something real, low-level, and unapologetically system-oriented get built in public, not just read the polished retrospective six months later — this is your invitation.&lt;/p&gt;
&lt;p&gt;We post live updates, real bugs, actual wins, and everything happening behind the curtain on our Telegram channel: &lt;strong&gt;t.me/forgezeroteam&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;No garbage collectors were harmed in the making of this project. Several were, however, deeply and personally offended.&lt;/p&gt;
</description>
<category>Discussions</category>
<guid isPermaLink="true">https://coderlegion.com/29880/hi-im-the-guy-who-got-angry-at-garbage-collectors-and-built-an-entire-toolchain-about-it</guid>
<pubDate>Mon, 05 Oct 2026 15:29:39 +0000</pubDate>
</item>
<item>
<title>Why Good Agent Evaluation Wasn’t Enough for Production</title>
<link>https://coderlegion.com/29819/why-good-agent-evaluation-wasnt-enough-for-production</link>
<description>&lt;p&gt;&lt;strong&gt;&quot;They used it once and refused to touch it again.&quot;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;When that feedback landed two weeks in, I could barely believe it. An agent validated against a benchmark built on real-world data had still failed to solve the foundation’s workflow problem.&lt;/p&gt;
&lt;p&gt;Only in hindsight did I realize: I had evaluated the agent's processing capability, but I never evaluated whether volunteers could actually use it to get their job done.&lt;/p&gt;
&lt;h2 id=&quot;1-every-house-has-a-kitchen-why-build-a-community-canteen-&quot;&gt;1. Every House Has a Kitchen—Why Build a Community Canteen?&lt;/h2&gt;
&lt;p&gt;A friend working at a non-profit foundation once shared a story with me:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;A rural village had poured resources into numerous senior welfare initiatives over the years, yet life satisfaction barely moved. What ultimately made a difference was building a communal canteen.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;When the village head first heard the project proposal, he was deeply skeptical: Every single elderly household has its own kitchen. Why on earth do we need a village canteen?&lt;/p&gt;
&lt;p&gt;Field visits later validated two straightforward reasons. First, cooking had simply become too physically taxing for the seniors—especially firing up a stove in the sweltering heat, which was pure misery. Second, beyond serving hot meals, the canteen at the village entrance created a regular venue for group interaction. It gave them a place to run into people and chat, directly addressing their need for social connection.&lt;/p&gt;
&lt;p&gt;This counterintuitive, genuine need was surfaced by foundation staff and volunteers through round after round of authentic home-visit conversations with local seniors.&lt;/p&gt;
&lt;p&gt;Their organization runs diverse programs nationwide. These projects rely on cohorts of short-term volunteers who pay regular visits to seniors across different age groups, speaking varied regional dialects. Those conversational touchpoints hold the richest, yet most frequently overlooked, context regarding the seniors' current living conditions and real needs.&lt;/p&gt;
&lt;p&gt;While my friend had the foresight to archive these interview audio recordings, data collection still leaned entirely on volunteers manually filling out questionnaires.&lt;/p&gt;
&lt;p&gt;That survey workflow was time-consuming and labor-intensive, making standardization difficult. More crucially, longitudinal insights—such as tracking changes in a senior's condition over time—rarely materialized.&lt;/p&gt;
&lt;p&gt;Meanwhile, a lean foundation team couldn't realistically audit hours of raw audio by hand.&lt;/p&gt;
&lt;p&gt;They needed a way to truly listen to these seniors at scale.&lt;/p&gt;
&lt;h2 id=&quot;2-from-an-audio-file-to-an-auditable-interview-record&quot;&gt;2. From an Audio File to an Auditable Interview Record&lt;/h2&gt;
&lt;p&gt;I realized the foundation didn’t need raw audio files; they needed reliable, verifiable interview records. That breakdown came down to three concrete requirements.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;First: Accurate content transcription.&lt;/strong&gt; Across these interviews, seniors speaking regional Chinese dialects was the baseline reality. This required a rigorous vendor evaluation for speech-to-text (STT).&lt;/p&gt;
&lt;p&gt;I requested a small batch of authentic interview datasets. These were dialect-heavy recordings—each running over 30 minutes, spanning dozens of back-and-forth turns, with standard Mandarin and local dialects constantly interspersed. I benchmarked several speech recognition vendors that supported dialects against these files.&lt;/p&gt;
&lt;p&gt;Evaluation was constrained to spot-checks by team members fluent in the respective dialects, paired with holistic context sanity checks. Through this filtering, I locked down our technical vendor and measured average transcription costs on real-world data.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Second: Accurate speaker identification.&lt;/strong&gt; Listening closely to the tapes revealed messy, real-world conversational dynamics, typically involving multi-party conversations.&lt;/p&gt;
&lt;p&gt;Volunteers often conducted visits in small teams. Meanwhile, the senior being interviewed was frequently surrounded by family members or neighbors chiming in. For instance, if an extraction pipeline relied purely on raw transcripts to log the senior's health updates, it could easily misattribute statements made by the spouse.&lt;/p&gt;
&lt;p&gt;However, STT vendors supporting broad dialect coverage rarely offered out-of-the-box speaker identification. In our processing pipeline, we therefore had to run speaker identification on the raw audio of each segmented utterance, injecting speaker identity as a critical context layer.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Third: Grounded rationale extraction.&lt;/strong&gt; The survey questions had to be answered strictly based on the interview dialogue, and many items had direct evidentiary clues scattered across the transcript.&lt;/p&gt;
&lt;p&gt;My primary concern here was hallucination in the underlying LLM—specifically, downstream evaluative conclusions generated without factual backing.&lt;/p&gt;
&lt;p&gt;I addressed this with a two-layer design: 1) Structurally, the workflow retained a human-in-the-loop review step for volunteers to verify answers. 2) Technically, in addition to outputting the structured form fields, the agent was forced to simultaneously extract the verbatim dialogue snippets serving as evidence.&lt;/p&gt;
&lt;p&gt;This second design unlocked a standardized benchmark to score relevance between the model’s outputs and the source transcript, measuring precision and recall on evidence retrieval. While this doesn't offer an absolute algorithmic guarantee against hallucinations, it effectively benchmarked different model providers. It also enabled runtime quality checks on citation integrity in production, providing a reliable proxy for dialogue session health.&lt;/p&gt;
&lt;p&gt;Anchored on these three core principles, I set out to hand-label the small sample of real interview data to stand up our benchmark.&lt;/p&gt;
&lt;p&gt;Dialect testing was limited to spot-checking rather than turn-by-turn ground-truth matching, so the benchmark baseline assumed clean, pre-segmented utterances. I built a lightweight internal web tool to load this context, play back audio per utterance, and let me label speaker identities.&lt;/p&gt;
&lt;p&gt;For the post-interview survey extraction, I created ground-truth annotations of verbatim evidence and corresponding answers across the sample cases. The evidence retrieval was cleanly testable with deterministic metrics, while answer accuracy was evaluated using an LLM-as-a-judge approach as a directional reference.&lt;/p&gt;
&lt;p&gt;With this benchmark running, I vibe-coded an end-to-end web prototype and iterated through multiple testing loops. This involved refactoring the data processing pipeline, redesigning the web interface flow, and selecting key vendors—particularly balancing latency, accuracy, and operational cost.&lt;/p&gt;
&lt;p&gt;Ultimately, the prototype reached a very usable level on the benchmark. The dataset was small, but I had reasonable confidence in the pipeline's output quality.&lt;/p&gt;
&lt;h2 id=&quot;3-the-benchmark-looked-great-why-did-volunteers-refuse-to-use-it-&quot;&gt;3. The Benchmark Looked Great. Why Did Volunteers Refuse to Use It?&lt;/h2&gt;
&lt;p&gt;The benchmark numbers gave me strong confidence in this build. I recorded a demo video for my friend, who was also impressed with the output. Within two days, he assigned a remote intern and the volunteer operations lead to pilot the tool in an upcoming regional field visit.&lt;/p&gt;
&lt;p&gt;For the next few weeks, I repeatedly checked our dashboard, watching trial volunteers sign up and trigger runs. The counters showed tasks completing normally, and I convinced myself that the tool had solved the foundation's interview processing problem.&lt;/p&gt;
&lt;p&gt;Two weeks later, my friend came back with the field report: the end-user experience was terrible. After testing it once, the frontline volunteers refused to touch it again.&lt;/p&gt;
&lt;p&gt;I have to admit, when that feedback first came in, I found it hard to believe. With an evaluation backed by real-world audio showing strong results, the actual production experience should not have been so poor.&lt;/p&gt;
&lt;p&gt;Where did it break?&lt;/p&gt;
&lt;p&gt;I first synced with the intern, who insisted the product vision was completely spot-on and genuinely meaningful. We brainstormed a backlog of incremental feature optimizations. Then, pulled away by other priorities over the next few months, I let the project stall.&lt;/p&gt;
&lt;p&gt;Later, my friend insisted on pushing this tool through to full deployment for ongoing volunteer operations. I joined one of their internal working sessions. That was when I realized the intern was working remotely; like me, he had limited visibility into the day-to-day work of frontline volunteers.&lt;/p&gt;
&lt;p&gt;In that meeting, I finally spoke with the volunteer coordinator who had reported the poor experience. Listening to her describe the workflow, I caught the core friction point causing the backlash: for volunteers, the operational overhead had multiplied, while the perceived efficiency gain over their legacy process was negligible.&lt;/p&gt;
&lt;p&gt;I asked her to walk me through their baseline workflow. They already had a standardized SOP for regional field teams: volunteers simply completed their onsite interview, uploaded the audio file, and manually filled out the survey form.&lt;/p&gt;
&lt;p&gt;Volunteers executed this entire legacy flow on their phones via WeChat, simply tapping a Feishu web form link provided by headquarters.&lt;/p&gt;
&lt;p&gt;My &quot;solution,&quot; by contrast, required volunteers to open a third-party speech SaaS on desktop, register an account, pre-import questionnaire prompt templates, upload audio files turn-by-turn, and export the transcribed extraction as a CSV file.&lt;/p&gt;
&lt;p&gt;It was an embarrassingly obvious failure mode. In retrospect, I struggled to understand how I missed such glaring workflow friction when the initial feedback came in.&lt;/p&gt;
&lt;p&gt;It boils down to one reality: I wasn’t on the ground.&lt;/p&gt;
&lt;h2 id=&quot;4-an-agent-only-works-when-it-disappears-into-the-existing-workflow&quot;&gt;4. An Agent Only Works When It Disappears into the Existing Workflow&lt;/h2&gt;
&lt;p&gt;That feedback was useful because the source of the problem was clear.&lt;/p&gt;
&lt;p&gt;A straightforward idea surfaced: How could I wire this benchmarked pipeline directly into the volunteers' existing workflow? Ideally, the integration would be completely invisible to them, dropping adoption friction to near zero.&lt;/p&gt;
&lt;p&gt;I focused on Feishu, their existing workspace tool. Volunteers were already relying on its native file upload and survey forms for automated data collection. Digging into the platform capabilities, I found that Feishu supported custom workflows combining audio ingestion and LLM execution. I could rebuild the pipeline natively on top of their existing automation infrastructure.&lt;/p&gt;
&lt;p&gt;I stood up the working prototype over a single weekend. It mirrored the exact audio submission entry point volunteers were already accustomed to, handling dialect transcription, speaker parsing, and grounded Q&amp;amp;A extraction entirely downstream.&lt;/p&gt;
&lt;p&gt;The turnaround was immediate and stark.&lt;/p&gt;
&lt;p&gt;The intern quickly grasped why our earlier disconnect from the operational context had derailed us. After reviewing the prototype architecture, he took over and built out the complete internal production implementation. He also spearheaded rollout validation across pilot regions, mapping the geographic distribution of elderly dialects to define the hard operating boundaries of the system. In regions speaking dialects beyond our coverage limits, the legacy manual workflow was kept intact.&lt;/p&gt;
&lt;p&gt;Only then was the project truly in production use.&lt;/p&gt;
&lt;p&gt;Whenever I look back at this case, I stay paranoid about one question: What are the actual measurement boundaries of the benchmark in front of me? And what is it failing to measure?&lt;/p&gt;
&lt;p&gt;This experience didn't invalidate our benchmark. What it made painfully clear was that the benchmark answered a very narrow question: Given an audio file, can the agent generate an accurate, auditable interview record? What it completely failed to answer was: Can a volunteer, as an end user, complete their end-to-end task inside their existing workflow with less operational friction?&lt;/p&gt;
&lt;p&gt;I now view the workflow itself as part of the evaluation, rather than just scoring raw model outputs. How input enters the system, how outputs are reviewed and submitted, and whether human users continue using the tool all help determine whether an agent delivers sustained real-world value.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/29819/why-good-agent-evaluation-wasnt-enough-for-production</guid>
<pubDate>Mon, 05 Oct 2026 10:28:24 +0000</pubDate>
</item>
<item>
<title>The API Gateway Shouldn't Know Your Business</title>
<link>https://coderlegion.com/29545/the-api-gateway-shouldnt-know-your-business</link>
<description>&lt;p&gt;I appreciate what an API gateway brings to most systems, at the right time and in the right place. It gives every service a shared first layer. TLS, rate limiting, and access logging happen once at the edge, and bad tokens get turned away before any service spends work on them. The services behind it still check what reaches them, but they start from cleaner traffic. The gateway also lets those services move and split behind a stable address without breaking their clients.&lt;/p&gt;
&lt;p&gt;But a gateway's configuration tends to collect things nobody designed it to hold. It might be a check on a customer's plan, a response field renamed back for an older client version, or a call to a second service to decide whether the first one should be reached.&lt;/p&gt;
&lt;p&gt;That drift, away from the gateway's purpose and from the code that owns the rule, isn't always carelessness. The gateway is the cheapest place to apply a business domain rule to every external request, so rules flow toward it. The decade-old advice that business logic doesn't belong there hasn't removed that pull. It says where business logic shouldn't go, and little about the forces that send it there or how to keep a rule out when the need is urgent.&lt;/p&gt;
&lt;p&gt;A business domain rule belongs in the gateway only if the team that owns the gateway could change it correctly without asking a domain team. Keeping the rest out takes an answer to each force, and an emergency rule that does go in goes in as debt, with a domain owner and a scheduled removal.&lt;/p&gt;
&lt;h3 id=&quot;the-advice-has-been-clear-since-2014&quot;&gt;The Advice Has Been Clear Since 2014&lt;/h3&gt;
&lt;p&gt;In March 2014, James Lewis and Martin Fowler described microservices as favoring &quot;smart endpoints and dumb pipes,&quot; against Enterprise Service Bus products that &quot;often include sophisticated facilities for ... applying business rules.&quot; Thoughtworks put &quot;overambitious API gateways&quot; on Hold in its Technology Radar from November 2015 through May 2018. The April 2016 entry calls the pattern &quot;a worrying re-emergence of this disease,&quot; the disease being business smarts pushed into middleware. It says &quot;any domain smarts such as data transformation or rule processing should live in applications or services where they can be controlled by product teams.&quot;&lt;/p&gt;
&lt;p&gt;Microsoft's Gateway Offloading pattern agrees, saying &quot;Never offload business logic to the gateway.&quot;&lt;/p&gt;
&lt;h3 id=&quot;why-rules-drift-into-the-gateway&quot;&gt;Why Rules Drift Into the Gateway&lt;/h3&gt;
&lt;p&gt;Four forces pull a rule toward the gateway, and each makes sense to whoever makes the change that day.&lt;/p&gt;
&lt;h4 id=&quot;every-external-request-passes-through-it&quot;&gt;Every External Request Passes Through It&lt;/h4&gt;
&lt;p&gt;Premium customers can request to export more than 10,000 rows, and everyone else can't. In the services, that rule is a change to the export service, and possibly to each service that exposes a large download. In the gateway, it's one policy that rejects the request before any service sees it. The person making the change may not see it as a pricing rule, because it can look like an urgent fix to keep large exports from degrading the system.&lt;/p&gt;
&lt;h4 id=&quot;it-ships-on-a-different-schedule&quot;&gt;It Ships on a Different Schedule&lt;/h4&gt;
&lt;p&gt;A gateway policy usually ships as configuration, and a platform team often owns it, so adding the export limit there doesn't wait on the export team's backlog. What the gateway offers is a different queue, not necessarily a faster one. When the export team is busy and the deadline is close, the rule goes wherever it can ship this week.&lt;/p&gt;
&lt;p&gt;After that, every change to the export limit waits in the platform team's queue, even when the export team could ship it sooner. The Gateway Offloading pattern says offloading doesn't fit when centralizing concerns &quot;creates a change-management bottleneck&quot; behind a slower gateway release cycle. The schedule that let the rule in once is the schedule it's stuck with.&lt;/p&gt;
&lt;h4 id=&quot;vendors-compete-on-programmability&quot;&gt;Vendors Compete on Programmability&lt;/h4&gt;
&lt;p&gt;A rule can only move into the gateway if the gateway can express it, and gateway products have made sure it can. Azure API Management runs policy expressions written in C#, and its &lt;code&gt;send-request&lt;/code&gt; policy calls another service mid-request. Kong's Pre-Function plugin &quot;lets you dynamically run Lua code.&quot; Thoughtworks' later radar entries said vendors kept adding such features to differentiate their products.&lt;/p&gt;
&lt;p&gt;Suppose the export team is booked for the quarter, so leadership overrides the queue and hands the export limit to the platform team, which runs Azure API Management. The platform team writes a policy that takes four steps on each export request:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Read the customer ID from the caller's validated token.&lt;/li&gt;
&lt;li&gt;Ask the entitlements service for that customer's plan with &lt;code&gt;send-request&lt;/code&gt;, caching the answer for a few minutes.&lt;/li&gt;
&lt;li&gt;Read the requested row count from the &lt;code&gt;rows&lt;/code&gt; query parameter, treating a missing value as zero so clients that omit it keep working.&lt;/li&gt;
&lt;li&gt;Return a 403 if the plan isn't &lt;code&gt;premium&lt;/code&gt; and the count is over 10,000.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Every step uses a standard gateway feature, and the result is business logic. Once the plan lookup exists, the next rule that needs a customer's plan costs one more condition.&lt;/p&gt;
&lt;h4 id=&quot;security-wants-one-place-to-check&quot;&gt;Security Wants One Place to Check&lt;/h4&gt;
&lt;p&gt;Authorization can be the first business rule to land in the gateway, pushed by a security group that wants one place to audit rather than trusting each dev team to get it right. The gateway already checks tokens, so it looks like that place. The drift starts when the gateway moves from checking that a token is valid to deciding what the caller may do. It usually begins with a role check on a route, which grows into decisions about which records the caller may touch.&lt;/p&gt;
&lt;h3 id=&quot;a-gateway-rule-loses-its-owner-its-coverage-and-its-tests&quot;&gt;A Gateway Rule Loses Its Owner, Its Coverage, and Its Tests&lt;/h3&gt;
&lt;h4 id=&quot;its-meaning-and-its-enforcement-get-different-owners&quot;&gt;Its Meaning and Its Enforcement Get Different Owners&lt;/h4&gt;
&lt;p&gt;Step 4 of the platform team's export policy encodes two business facts, that a plan called premium exists and that it allows larger exports than every other plan. The entitlements team decides what plans exist and the export team decides what they allow, but the platform team owns the policy. The team that can change the rule's meaning can't see where it's enforced, and the team that enforces it can't judge whether it's still right.&lt;/p&gt;
&lt;p&gt;Suppose sales introduces an enterprise plan. The entitlements service starts returning &lt;code&gt;enterprise&lt;/code&gt;, and the gateway rule still compares against &lt;code&gt;premium&lt;/code&gt;, so enterprise customers, who pay the most, get rejected on large exports. No test fails, because no test in either service ever ran the rule. Hard-coding &lt;code&gt;premium&lt;/code&gt; would be a bug in the service too. Here, though, someone must first find the rule, and then the platform team must change a rule whose meaning they didn't write.&lt;/p&gt;
&lt;p&gt;The same split makes the rule hard to remove. The platform team can see the rule but can't tell whether any client depends on it, and the domain team could answer that but may not know the rule exists. So rules can accumulate, each one cheaper to leave than to trace, and the gateway drifts toward the middleware Lewis and Fowler described.&lt;/p&gt;
&lt;h4 id=&quot;the-rule-covers-only-what-the-gateway-can-see&quot;&gt;The Rule Covers Only What the Gateway Can See&lt;/h4&gt;
&lt;p&gt;The first gap is the routes that never cross the gateway. Follow one free-plan customer who wants 50,000 rows. Calling the public API, they get the 403. Then they click Export in the product's web app, whose backend calls the export service over the internal network, and the export runs. They set up a nightly scheduled export. The scheduler, acting with its own service identity, publishes an &lt;code&gt;ExportRequested&lt;/code&gt; message that the export service consumes, and that export runs too. The same customer asks the same question three ways and gets two different answers.&lt;/p&gt;
&lt;p&gt;The Gateway Offloading pattern recommends that backends accept requests only through the gateway, which stops outside clients from bypassing it but doesn't help here. The web app's backend and the scheduler are part of the product. The queued message never becomes an HTTP request. Even a scheduler call routed through the gateway would carry the scheduler's identity, with no customer for the gateway to look up. So the export service needs its own check, and the enterprise-plan change now has to reach two copies of the rule.&lt;/p&gt;
&lt;p&gt;The second gap is on the gateway's own route, where it can check only what a request declares, while an export's size is known only after the query runs. The service treats &lt;code&gt;rows&lt;/code&gt; as an optional cap, so a request for &lt;code&gt;?from=2020-01-01&amp;amp;to=2026-01-01&lt;/code&gt; can leave it out. Step 3 treats a missing count as 0, so the request passes and gets every row the date range matches. Requiring &lt;code&gt;rows&lt;/code&gt; would close the hole, but that changes the export API's contract, which is the export team's decision.&lt;/p&gt;
&lt;h4 id=&quot;the-rule-leaves-the-domain-s-tests&quot;&gt;The Rule Leaves the Domain's Tests&lt;/h4&gt;
&lt;p&gt;The export team's test suite exercises the export service, not the policy. Unless gateway config runs in the export team's pipeline, the rule has no test that runs when the export service or the plan catalog changes. API Management's documentation adds that policy expressions have &quot;only limited verification&quot; when defined and run at request time, where any exception becomes a runtime error.&lt;/p&gt;
&lt;h3 id=&quot;keep-the-gateway-to-rules-its-own-team-can-change&quot;&gt;Keep the Gateway to Rules Its Own Team Can Change&lt;/h3&gt;
&lt;p&gt;One question tests whether a rule is about the request or about the business: &lt;strong&gt;could the team that owns the gateway change this rule correctly without asking a domain team?&lt;/strong&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;Kind of rule &lt;/th&gt;&lt;th&gt;Example &lt;/th&gt;&lt;th&gt;Where it runs &lt;/th&gt;&lt;th&gt;Who decides its meaning&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;About the request &lt;/td&gt;&lt;td&gt;Token expiry, request size, per-caller rate limit &lt;/td&gt;&lt;td&gt;Gateway &lt;/td&gt;&lt;td&gt;Gateway team&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;An adapter for a backend or client that can't change &lt;/td&gt;&lt;td&gt;Renaming fields for a vendor system or an old client version &lt;/td&gt;&lt;td&gt;Gateway, until retired &lt;/td&gt;&lt;td&gt;A named team, with a test against the backend&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Composition for one client &lt;/td&gt;&lt;td&gt;One response for a mobile screen &lt;/td&gt;&lt;td&gt;Backend for frontend &lt;/td&gt;&lt;td&gt;That client's team&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;A stopgap during an incident &lt;/td&gt;&lt;td&gt;Blocking large free-plan exports until a fix ships &lt;/td&gt;&lt;td&gt;Gateway, until the fix ships &lt;/td&gt;&lt;td&gt;Domain team, with a removal date&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;A business rule &lt;/td&gt;&lt;td&gt;Export limits by plan &lt;/td&gt;&lt;td&gt;Owning service &lt;/td&gt;&lt;td&gt;Domain team&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Rules about the request pass because their correct values don't change when the business changes its mind. A rule that reads a business concept, such as a plan, account status, or price, fails, however little code it takes.&lt;/p&gt;
&lt;p&gt;Two better-built versions of the export rule still fail. In the first, the export team keeps its own gateway config. The split owners and the platform team's queue go away, but the coverage gaps remain.&lt;/p&gt;
&lt;p&gt;In the second, the policy asks entitlements for the customer's limit as a number, so plan names leave the gateway. But which routes count as exports is still the export team's decision. A new bulk download endpoint would slip past the gateway rule, while a check in the export code covers it on release. Tagging export routes in the export team's spec closes that gap, but the gateway copy still misses the internal paths.&lt;/p&gt;
&lt;h4 id=&quot;authentication-passes-authorization-doesn-t&quot;&gt;Authentication Passes, Authorization Doesn't&lt;/h4&gt;
&lt;p&gt;Tokens sit on both sides of the ownership test. Rejecting a token that is malformed, badly signed, expired, or tied to a revoked session passes, because what makes a token valid doesn't change when the business does.&lt;/p&gt;
&lt;p&gt;Any decision about what the caller may do fails the test, even at the route level. A scope per route looks like a property of the token, but which scope &lt;code&gt;POST /exports&lt;/code&gt; requires is the export team's call. If they split &lt;code&gt;exports:write&lt;/code&gt; into a standard and a bulk scope, the gateway enforces the old contract until the platform team catches up. The exception is a scope check generated from the service's API spec. It removes the lag and can stay as an early reject, because the export team still decides its meaning and the service runs its own check.&lt;/p&gt;
&lt;p&gt;The Gateway Offloading pattern lists authorization among the concerns a gateway can centralize, so this line is narrower than Microsoft's. The gateway sees &lt;code&gt;/accounts/4417/exports&lt;/code&gt; and a user ID, but only the service that loads account 4417 knows who belongs to it. Broken object level authorization tops the 2023 OWASP API Security Top 10, which describes the check as one &quot;usually implemented at the code level.&quot;&lt;/p&gt;
&lt;h4 id=&quot;capacity-limits-pass-plan-quotas-don-t&quot;&gt;Capacity Limits Pass, Plan Quotas Don't&lt;/h4&gt;
&lt;p&gt;Rate limits sit on both sides too. A per-caller limit that protects capacity, such as 100 requests a second per key, passes. Its value tracks capacity, not what a customer bought, and it only has to protect the edge. A quota sold as part of a plan, such as 10,000 API calls a month on the basic tier, fails. The export limit splits the same way. A size cap that protects the system passes, and the extra rows premium customers may request belong in the export service.&lt;/p&gt;
&lt;p&gt;Amazon API Gateway's usage plans attach a quota to each API key, which makes the gateway a second copy of each customer's plan to update on every upgrade and cancellation. The exception is an API that is itself the product, with its plans sold in the gateway. There the gateway's owner owns the plan, and the quota passes.&lt;/p&gt;
&lt;h4 id=&quot;adapters-and-composition-are-narrow-exceptions&quot;&gt;Adapters and Composition Are Narrow Exceptions&lt;/h4&gt;
&lt;p&gt;A transformation for a backend or client that can't change, such as a vendor system, an old client, or a service partway through a migration, can live in the gateway. It needs an owner, a test against the backend, and a recorded change that will retire it. Its meaning belongs to the backend's owner, so it fails the ownership test. Like a stopgap, it may still run in the gateway, but only on those conditions. Composition for one client belongs in a backend for frontend, Sam Newman's pattern of a service owned by the team that builds the client. That service owns presentation, not the domain rules behind it.&lt;/p&gt;
&lt;h3 id=&quot;answer-each-force&quot;&gt;Answer Each Force&lt;/h3&gt;
&lt;p&gt;Under a deadline, the easiest place for a rule wins, so each force needs an answer that makes the owning service as easy a choice.&lt;/p&gt;
&lt;h4 id=&quot;every-request-reaches-the-service&quot;&gt;Every Request Reaches the Service&lt;/h4&gt;
&lt;p&gt;Put the check in the export service from the start, and keep plan details out of it. The service asks the entitlements service for this customer's export limit, as a number, and refuses any export over it. That adds a call to each service that serves large downloads, but a shared client library with caching keeps it to a few lines. When sales adds an enterprise plan, only the entitlements service's limits change.&lt;/p&gt;
&lt;h4 id=&quot;borrow-the-gateway-s-schedule-only-as-debt&quot;&gt;Borrow the Gateway's Schedule Only as Debt&lt;/h4&gt;
&lt;p&gt;For a one-time change it can make alone, the platform team's queue is short, and sometimes that's what a missed deadline or an incident needs. Suppose a Friday release breaks the export service's limit check, and free customers start pulling millions of rows through the public API. The platform team can ship the four-step policy within the hour, changed to refuse any export without a declared row count, long before the export team can safely deploy a fix. Ship it, but ship it as debt:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;The export team owns the fix and the required &lt;code&gt;rows&lt;/code&gt; parameter the policy imposed,&lt;/strong&gt; booked into a release the day the policy ships. Once the incident ends, nothing pushes anyone to replace a policy that seems to work.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The policy carries its owner, ticket, and removal date,&lt;/strong&gt; so anyone reading the gateway can tell a stopgap from a rule that belongs there.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The fix isn't done until the policy is deleted&lt;/strong&gt; and the service's own test covers the routes the stopgap never saw.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;make-code-in-the-gateway-require-an-owner&quot;&gt;Make Code in the Gateway Require an Owner&lt;/h4&gt;
&lt;p&gt;A business rule needs only the platform team's approval to get into the gateway, and that review tends to ask whether the policy works, not whose rule it is. A policy needs cross-team review if it calls another service, runs custom code, or compares a claim to a business value. A pipeline can detect the first two. The review comes from the domain team that owns the concept, or from architecture review when no team clearly owns it.&lt;/p&gt;
&lt;p&gt;Some policies can't wait for review or can't pass it. If the owning team can't review before an emergency ships, the review joins the stopgap's debt. If the policy fails the ownership test, it merges only as a dated stopgap or an owned adapter.&lt;/p&gt;
&lt;h4 id=&quot;split-authorization-by-who-owns-the-facts&quot;&gt;Split Authorization by Who Owns the Facts&lt;/h4&gt;
&lt;p&gt;Security can get consistent, auditable authorization without the gateway. Role-based decisions, such as what the support-agent role may do or which tenant a user belongs to, rest on facts that rarely change mid-request. They can live in one central policy, distributed to every service and evaluated locally on every route, with each domain team owning the entries for its own capabilities. Contextual decisions, such as whether a user may export account 4417 given its status or a legal hold, rest on domain facts that can change from one request to the next. Centralizing those repeats the gateway's split one layer over, so they stay in the domain service, tested by the team that owns the facts.&lt;/p&gt;
&lt;p&gt;Where security doesn't own the rules, it can still own how every service enforces them. Shared middleware can reject any request a service doesn't explicitly allow, so a forgotten check refuses access instead of granting it. Every service can also log its authorization decisions to one place security audits.&lt;/p&gt;
&lt;p&gt;Either way, the check runs in the service. A gateway-only check trusts the web app and scheduler routes because they run inside the network. NIST's zero trust architecture, SP 800-207, rejects that, holding that there's no &quot;implicit trust granted to assets or user accounts based solely on their physical or network location.&quot;&lt;/p&gt;
&lt;h3 id=&quot;find-the-business-rules-already-in-your-gateway&quot;&gt;Find the Business Rules Already in Your Gateway&lt;/h3&gt;
&lt;p&gt;Open your gateway's policies, plugins, mapping templates, and route rules:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Search for business concepts.&lt;/strong&gt; Claim names and domain terms find most of them. Put each through the ownership test.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Move each rule that fails into the service that owns it,&lt;/strong&gt; with a check and a test that cover every path to the capability, including jobs, consumers, internal tools, and other gateways.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Give any stopgap that has to stay an owner and a removal date,&lt;/strong&gt; and add a pipeline check that rejects a policy missing either or past its date. A stopgap with neither is business logic that stayed.&lt;/li&gt;
&lt;/ul&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/29545/the-api-gateway-shouldnt-know-your-business</guid>
<pubDate>Fri, 02 Oct 2026 13:53:46 +0000</pubDate>
</item>
</channel>
</rss>