<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0">
<channel>
<title>Coder Legion - Recent posts tagged software-development</title>
<link>https://coderlegion.com/tag/software-development</link>
<description>Powered by Question2Answer</description>
<item>
<title>The Semicolon That Stole 5 Days of My Life</title>
<link>https://coderlegion.com/33206/the-semicolon-that-stole-5-days-of-my-life</link>
<description>&lt;p&gt;I have never been this frustrated with a single bug in my life.&lt;/p&gt;
&lt;p&gt;And the culprit? The classic, infamous semicolon.&lt;/p&gt;
&lt;p&gt;I recently started exploring Express.js and wired up a MongoDB database for an assignment. Connecting the database was a small hurdle, but that wasn’t the real fight.&lt;br&gt;
The terminal said the database was connected. But Postman and the browser? Total silence. No response, no error.&lt;/p&gt;
&lt;p&gt;I created a fresh test database. Nothing. Then I disconnected the database entirely and bypassed my .env file, and suddenly the routes worked.&lt;/p&gt;
&lt;p&gt;So my brain locked onto the wrong villain: “The URI must be broken.” “Maybe it’s the network whitelist.” “Maybe the connection string is formatted wrong.”&lt;/p&gt;
&lt;p&gt;I spent 5 days down that rabbit hole.&lt;/p&gt;
&lt;p&gt;This morning, with the submission deadline today, I told myself: if I don’t fix this now, I’m deleting the whole folder and starting over. So I opened every file one last time.&lt;br&gt;
And there it was, hiding in plain sight in my .env file:&lt;/p&gt;
&lt;p&gt;PORT=3000;&lt;/p&gt;
&lt;p&gt;In JavaScript, a semicolon ends a statement. In a .env file, it’s just part of the value. My port wasn’t 3000, it was “3000;”, so the server never listened where I was sending requests.&lt;/p&gt;
&lt;p&gt;Five days. One character.&lt;/p&gt;
&lt;p&gt;I still can’t decide whether to be relieved that it works or annoyed that a single semicolon stole my peace for a week. But this is part of what we signed up for as developers. The wins are great, and the silent syntax traps are real.&lt;/p&gt;
&lt;p&gt;No time to celebrate though. The deadline is today.&lt;/p&gt;
&lt;p&gt;What’s the most ridiculous bug that ate days of your life for no good reason? I want to hear your horror stories.&lt;/p&gt;
&lt;p&gt;Adiós.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/33206/the-semicolon-that-stole-5-days-of-my-life</guid>
<pubDate>Sat, 31 Oct 2026 08:21:59 +0000</pubDate>
</item>
<item>
<title>From Writing the Spec to Approving It: What AI-DLC Looks Like on a Real Build</title>
<link>https://coderlegion.com/33078/from-writing-the-spec-to-approving-it-what-ai-dlc-looks-like-on-a-real-build</link>
<description>&lt;h3 id=&quot;-from-writing-the-spec-to-approving-it-what-ai-dlc-looks-like-on-a-real-build&quot;&gt;🤖 From Writing the Spec to Approving It: What AI-DLC Looks Like on a Real Build&lt;/h3&gt;
&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;AI-DLC is public and open source: &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/awslabs/aidlc-workflows&quot;&gt;awslabs/aidlc-workflows&lt;/a&gt;. The method comes from this &lt;a rel=&quot;nofollow&quot; href=&quot;https://aws.amazon.com/blogs/devops/ai-driven-development-life-cycle/&quot;&gt;AWS DevOps blog post&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;My only written input was one sentence. Everything else I did was pick options and approve gates.&lt;/li&gt;
&lt;li&gt;The run produced requirements, four architecture decisions, a unit plan, a traceability file, and 12 tests that passed when I ran them, in about 44 minutes from start to the code approval.&lt;/li&gt;
&lt;li&gt;The human role moves from author to decision-maker. The decisions are still yours, and the record shows where they changed the outcome.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;Most AI-assisted development still follows the old split. A human writes the spec, a human writes the code, and the AI helps with autocomplete or generates tests at the end. AI-DLC (the AI-Driven Development Life Cycle) reverses that. The AI drafts the requirements, the design, and the code. The human answers short questions and approves each stage before the next one starts.&lt;/p&gt;
&lt;p&gt;To see what that looks like in practice, I built a small command-line tool with it and then read the record the workflow left behind. This post walks through that record, folder by folder.&lt;/p&gt;
&lt;h3 id=&quot;the-app&quot;&gt;The app&lt;/h3&gt;
&lt;p&gt;The tool is a Spooky Date Fact Generator. It picks one of 10 spooky dates (or takes &lt;code&gt;--date MM-DD&lt;/code&gt;) and prints a spooky event for that day from Wikipedia's &quot;On this day&quot; feed. It is small on purpose.&lt;/p&gt;
&lt;p&gt;The whole request was this sentence:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/aidlc build random date generator with fact around that number
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;the-flip-in-one-table&quot;&gt;The flip, in one table&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;&lt;/th&gt;&lt;th&gt;Traditional with AI help &lt;/th&gt;&lt;th&gt;AI-DLC&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;Requirements &lt;/td&gt;&lt;td&gt;Human writes them &lt;/td&gt;&lt;td&gt;AI drafts them from multiple-choice answers&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Design &lt;/td&gt;&lt;td&gt;Human sketches it &lt;/td&gt;&lt;td&gt;AI proposes, human picks between options&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Code &lt;/td&gt;&lt;td&gt;Human writes, AI assists &lt;/td&gt;&lt;td&gt;AI writes against an approved plan&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Tests &lt;/td&gt;&lt;td&gt;Human or AI at the end &lt;/td&gt;&lt;td&gt;AI writes them to the stated requirements&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Human job &lt;/td&gt;&lt;td&gt;Author &lt;/td&gt;&lt;td&gt;Decide, validate, approve&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Evidence &lt;/td&gt;&lt;td&gt;Pull request diff &lt;/td&gt;&lt;td&gt;Audit log, decisions, traceability file&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&quot;where-everything-was-written&quot;&gt;Where everything was written&lt;/h3&gt;
&lt;p&gt;Two locations matter. The generated app sits in its own folder, and the workflow record sits in an &lt;code&gt;aidlc/&lt;/code&gt; folder.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;app/                                  &amp;lt;- the generated tool
  src/spooky-facts.ts      core: dates, fetch, keyword filter, nearest-date search
  src/cli.ts               command line: options, output, exit codes
  test/                    12 tests, offline, with a fake fetch
  package.json, biome.json, tsconfig.json, README.md

aidlc/
  codekb/                             &amp;lt;- 9 docs about the existing repo
  intents/&amp;lt;date&amp;gt;-date-fact-generator/ &amp;lt;- the record for this build
    aidlc-state.md                    stage checklist and settings
    audit/                            every event, decision, and approval
    inception/
      reverse-engineering/            scan of the existing repo
      requirements-analysis/          questions + requirements.md
      domain-design/                  questions + components.md + decisions.md
      units-generation/               unit-of-work files
    construction/u1-spooky-date-cli/code-generation/
      code-generation-plan.md, code-summary.md,
      unit-test-instructions.md, traceability.json
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Each stage writes a &lt;code&gt;-questions.md&lt;/code&gt; file, and your answers sit in the same file as the AI's questions. That makes the decision trail readable without any tooling.&lt;/p&gt;
&lt;h3 id=&quot;stage-by-stage-what-it-did-and-why-it-helps&quot;&gt;Stage by stage: what it did and why it helps&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Initialization.&lt;/strong&gt; The workflow scaffolded the record folder, scanned the workspace, and classified it as brownfield TypeScript on Bun. This is automatic. It helps because every later stage starts from facts about the repo instead of guesses.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Reverse Engineering.&lt;/strong&gt; A developer agent scanned the existing repo and wrote nine knowledge-base documents (architecture, technology stack, code structure and others). This matters most in a brownfield repo. The later code followed the repo's Bun, TypeScript, and Biome conventions because the AI had read them first.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Requirements Analysis.&lt;/strong&gt; The AI asked six questions, such as where facts come from, what to do when a date has no spooky event, and whether this should be a web page or a command line. Each had lettered options. I answered, and one answer included a free-text note: generate dates that have spooky facts, from 10 such scenarios. The result is &lt;code&gt;requirements.md&lt;/code&gt; with 15 functional and 5 non-functional requirements, each with acceptance criteria and a tag pointing back to the answer that produced it, such as &lt;code&gt;[Q5]&lt;/code&gt;. This stage turns one vague sentence into testable statements before any code exists.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Domain Design.&lt;/strong&gt; Four more questions produced &lt;code&gt;components.md&lt;/code&gt; and four architecture decision records. The AI recommended splitting the tool into four parts. I chose two. The decision record states that the four-part split was rejected as more structure than needed. This is the clearest example of the validator role, because the human overrode the AI's recommendation and the reason is recorded.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Units Generation.&lt;/strong&gt; One question: build as one unit or two. I chose one, approved the plan, and got &lt;code&gt;unit-of-work.md&lt;/code&gt; with boundaries, responsibilities, and constraints. It fixes the blast radius. The unit is limited to &lt;code&gt;apps/date-fact-generator/&lt;/code&gt;, and nothing in the framework folders may change.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Code Generation.&lt;/strong&gt; The AI wrote a plan, then the code, then the tests. It asked one thing here: Bun was not installed, so should it install Bun or switch to Node. I chose Bun. The &lt;code&gt;code-summary.md&lt;/code&gt; lists files created, decisions made, and deviations from the plan. One deviation worth noting: the AI added word forms such as &quot;dies&quot; and &quot;sank&quot; to the keyword list, because whole-word matching would otherwise miss &quot;Houdini dies&quot;. The summary also states that some planned checks were grouped into fewer tests to match the 12-test count.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Build and Test.&lt;/strong&gt; In the record I read, this stage was still marked in progress, so its summary was not written yet. I ran the checks myself from the app folder: 12 tests passed with 93 assertions, and lint and typecheck were clean.&lt;/p&gt;
&lt;h3 id=&quot;what-the-human-actually-did&quot;&gt;What the human actually did&lt;/h3&gt;
&lt;p&gt;Reading the audit log, the human work was this:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Typed one sentence to start.&lt;/li&gt;
&lt;li&gt;Approved a recomposed plan. The workflow's planner found the default express plan was missing inputs for later stages, and proposed adding Domain Design and Units Generation while dropping deployment and monitoring.&lt;/li&gt;
&lt;li&gt;Answered 11 multiple-choice questions across three stages, plus two more decisions: build inside the existing repo, and install Bun.&lt;/li&gt;
&lt;li&gt;Approved 5 stage gates: reverse engineering, requirements, domain design, units, and code generation.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That is the flip. Nothing in that list is writing a spec or writing code. All of it is deciding and checking.&lt;/p&gt;
&lt;h3 id=&quot;why-this-holds-up-better-than-a-prompt&quot;&gt;Why this holds up better than a prompt&lt;/h3&gt;
&lt;p&gt;Two details make the validation real instead of a rubber stamp.&lt;/p&gt;
&lt;p&gt;First, traceability. &lt;code&gt;traceability.json&lt;/code&gt; maps every requirement ID (FR1.1 through NFR5, 20 in total) to the file that satisfies it. If a requirement has no target, it shows up.&lt;/p&gt;
&lt;p&gt;Second, the record is complete. The audit log holds the timestamp of each question, answer, and approval. A reviewer six months later can see why the design has two components and not four.&lt;/p&gt;
&lt;h3 id=&quot;what-this-run-does-not-show&quot;&gt;What this run does not show&lt;/h3&gt;
&lt;p&gt;This was an express-scope build of a 658-line app, with plan approval, sensors, and learnings switched off by the scope. It shows the shape of the method, not how it behaves on a large system with many units and teams. The Build and Test stage had not completed in the record, and the tests are offline, so only one manual live smoke run, recorded in the code summary, exercised the real Wikipedia feed.&lt;/p&gt;
&lt;p&gt;The questions are only as good as the options offered. A weak option list can steer a hurried approver, so reading the alternatives still matters.&lt;/p&gt;
&lt;h3 id=&quot;takeaway&quot;&gt;Takeaway&lt;/h3&gt;
&lt;p&gt;The useful change is not that the AI writes more code. It is that the human's time moves to the points where judgment counts: what the tool should do, which design to accept, and whether the result meets the requirements. If you want to try it, start with a small brownfield repo and read the &lt;code&gt;-questions.md&lt;/code&gt; files first. They show the method faster than any description.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/33078/from-writing-the-spec-to-approving-it-what-ai-dlc-looks-like-on-a-real-build</guid>
<pubDate>Fri, 30 Oct 2026 10:36:49 +0000</pubDate>
</item>
<item>
<title>Entity Framework Core Patterns I Actually Use in Production</title>
<link>https://coderlegion.com/32896/entity-framework-core-patterns-i-actually-use-in-production</link>
<description>&lt;p&gt;There is a gap between knowing how Entity Framework Core works and knowing how to use it in a way that holds up under real conditions. The documentation will teach you the first thing. Production will teach you the second, usually by showing you what happens when you get it wrong.&lt;/p&gt;
&lt;p&gt;This post is not a tutorial. It is not a comprehensive guide to EF Core features. It is a walkthrough of the specific patterns I apply consistently across every .NET project I build, with the reasoning behind each one. The patterns themselves are not exotic. The discipline of applying them deliberately is what separates a codebase that ages well from one that accumulates quiet performance problems and confusing data states.&lt;/p&gt;
&lt;p&gt;If you are past the getting-started stage with EF Core and want to understand how experienced developers think about it in production, this is written for you.&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Knowing how EF Core works and knowing how to use it without creating problems at scale are two different skills. Production teaches you the second one.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;asnotracking-the-default-you-should-be-questioning&quot;&gt;ASNOTRACKING: THE DEFAULT YOU SHOULD BE QUESTIONING&lt;/h2&gt;
&lt;p&gt;By default, every entity you query through EF Core gets attached to the change tracker. The change tracker watches that entity for modifications and uses those modifications to generate UPDATE statements when you call SaveChanges. This is the feature that makes EF Core convenient for write operations.&lt;/p&gt;
&lt;p&gt;It is also overhead you are paying on every read operation, whether you need it or not.&lt;/p&gt;
&lt;p&gt;For a read-only query, attaching entities to the change tracker gives you nothing. The entity is never going to be modified and saved. The tracking is pure cost: memory allocated to hold the snapshot, CPU cycles spent watching for changes, and a change tracker that grows with every query you execute in the same DbContext lifetime.&lt;/p&gt;
&lt;p&gt;The pattern I apply is deliberate rather than default. Every query that exists purely to return data gets AsNoTracking. Every query that might lead to an update does not.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// Read-only: tracking adds no value here
var tasks = await _context.Tasks
    .AsNoTracking()
    .Where(t =&amp;gt; t.AssignedUserId == userId)
    .ToListAsync();

// Write path: tracking is needed
var task = await _context.Tasks
    .FirstOrDefaultAsync(t =&amp;gt; t.Id == id);

if (task != null)
{
    task.StatusId = newStatusId;
    await _context.SaveChangesAsync();
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The practical impact becomes visible at scale. A dashboard that loads twenty entities without AsNoTracking is allocating tracking overhead for twenty objects the application will never modify. Multiply that across concurrent users and the cost is real.&lt;/p&gt;
&lt;p&gt;The habit worth building is this: when you write a query, ask immediately whether the result will ever be written back. If the answer is no, add AsNoTracking before you move on. It is a two-second decision that compounds positively across the entire codebase.&lt;/p&gt;
&lt;h2 id=&quot;the-n-plus-1-problem-what-it-is-and-why-it-keeps-happening&quot;&gt;THE N PLUS 1 PROBLEM: WHAT IT IS AND WHY IT KEEPS HAPPENING&lt;/h2&gt;
&lt;p&gt;The N plus 1 problem is one of the most common performance issues in applications built with ORMs, and it is easy to introduce without realising it because the code that causes it looks completely reasonable.&lt;/p&gt;
&lt;p&gt;The pattern goes like this. You query a list of entities. Then, for each entity in that list, you access a navigation property that has not been loaded yet. EF Core executes a separate database query for each entity to load that property. One query to get the list, then one query per item in the list. If the list has fifty items, you have made fifty-one database round trips instead of one or two.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// This looks fine but is dangerous
var projects = await _context.Projects
    .AsNoTracking()
    .ToListAsync();

foreach (var project in projects)
{
    // Each iteration triggers a separate query
    var taskCount = project.Tasks.Count;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The fix is to load related data intentionally rather than letting EF Core load it lazily on demand. Include brings related entities into the initial query. Select projects only the fields you need. Both approaches consolidate the data retrieval into a single round trip.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// Explicit loading with Include
var projects = await _context.Projects
    .AsNoTracking()
    .Include(p =&amp;gt; p.Tasks)
    .ToListAsync();

// Projection with Select: more efficient when you need specific fields
var projectSummaries = await _context.Projects
    .AsNoTracking()
    .Select(p =&amp;gt; new ProjectSummaryDto
    {
        Id = p.Id,
        Name = p.Name,
        TaskCount = p.Tasks.Count
    })
    .ToListAsync();
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The projection approach with Select is generally preferable when you are building read models or DTOs, because it gives the database engine the information it needs to execute an optimal query rather than loading full entity graphs and discarding most of the data in application code.&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;The N plus 1 problem does not look like a bug. It looks like working code. That is why it keeps shipping to production.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The discipline that prevents N plus 1 problems is reviewing every query that accesses navigation properties and asking whether those properties will be loaded by the time they are accessed. If the answer is uncertain, make it certain by being explicit about what gets loaded and when.&lt;/p&gt;
&lt;h2 id=&quot;soft-deletes-with-lookup-tables&quot;&gt;SOFT DELETES WITH LOOKUP TABLES&lt;/h2&gt;
&lt;p&gt;Hard deletes, where a record is physically removed from the database, are rarely the right choice for production data. They break audit trails, they complicate recovery scenarios, and they create referential integrity problems when other records depend on the deleted entity.&lt;/p&gt;
&lt;p&gt;Soft deletes, where a record is marked as inactive rather than removed, solve these problems. The record stays in the database. Its relationships remain intact. The deletion is reversible. And you have a complete history of every state the record has passed through.&lt;/p&gt;
&lt;p&gt;My approach to soft deletes connects directly to the lookup table convention I use for all status fields. Rather than adding a boolean IsDeleted column, every entity that supports soft deletion has a StatusId that references a shared Statuses lookup table. A status of Active means the record is live. A status of Deleted means it has been soft deleted. A status of Archived means it has been intentionally retired without deletion.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// Statuses lookup table
public class Status
{
    public int Id { get; set; }
    public string Name { get; set; } = string.Empty;
}

// Entity with status-based soft delete
public class Task
{
    public int Id { get; set; }
    public string Title { get; set; } = string.Empty;
    public int StatusId { get; set; }
    public Status Status { get; set; } = null!;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The advantage over a boolean IsDeleted column is that the status field carries meaning beyond a binary active or inactive state. You can distinguish between a record that was deleted intentionally, one that was archived, one that is pending review, and one that is in draft, all within the same field, without adding columns or changing the schema.&lt;/p&gt;
&lt;p&gt;Adding a new status is a data operation. You insert a row into the Statuses table. No migration required. No enum update. No redeployment. The application picks it up immediately because it is reading status values from the database rather than from a hardcoded list in code.&lt;/p&gt;
&lt;h3 id=&quot;filtering-deleted-records-consistently&quot;&gt;Filtering deleted records consistently&lt;/h3&gt;
&lt;p&gt;The operational discipline that makes soft deletes work is consistent filtering. Every query that should return active records needs to filter on status. If you add that filter in some places and forget it in others, deleted records surface unexpectedly and the soft delete pattern breaks down.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// Always filter by active status on read queries
var activeTasks = await _context.Tasks
    .AsNoTracking()
    .Where(t =&amp;gt; t.StatusId == StatusIds.Active)
    .ToListAsync();

// Soft delete: update status, do not remove the record
var task = await _context.Tasks
    .FirstOrDefaultAsync(t =&amp;gt; t.Id == id);

if (task != null)
{
    task.StatusId = StatusIds.Deleted;
    await _context.SaveChangesAsync();
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;EF Core global query filters are worth considering for applications where the filtered entity is accessed in many places. A global query filter defined on the DbContext applies automatically to every query for that entity type, which eliminates the risk of forgetting the filter in a particular repository method.&lt;/p&gt;
&lt;h2 id=&quot;repository-methods-that-match-real-use-cases&quot;&gt;REPOSITORY METHODS THAT MATCH REAL USE CASES&lt;/h2&gt;
&lt;p&gt;The generic repository base covers the standard CRUD operations. In production, most of the interesting work happens in the specific repository interfaces that extend the generic base with query methods that match real application needs.&lt;/p&gt;
&lt;p&gt;The principle I apply is that a repository method should represent a meaningful operation in the domain, not a thin wrapper around a LINQ query. The difference is in how the application code reads.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// Thin wrapper: the caller still needs to know too much
var tasks = await _taskRepository.GetAllAsync();
var userTasks = tasks.Where(t =&amp;gt; t.AssignedUserId == userId
    &amp;amp;&amp;amp; t.StatusId == StatusIds.Active).ToList();

// Domain-meaningful method: intention is clear at the call site
var userTasks = await _taskRepository
    .GetActiveTasksByUserAsync(userId);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The second version is easier to read, easier to test, and easier to optimise. The filtering happens in the repository, close to the database, rather than in application code after a full table scan. And the method name communicates intent in a way that a chain of LINQ operators does not.&lt;/p&gt;
&lt;p&gt;This matters more as a codebase grows. A repository with well-named methods becomes a vocabulary for talking about the domain. A repository that returns raw IQueryable and lets callers filter arbitrarily becomes a leaky abstraction that is difficult to reason about and impossible to optimise consistently.&lt;/p&gt;
&lt;h2 id=&quot;managing-dbcontext-lifetime&quot;&gt;MANAGING DBCONTEXT LIFETIME&lt;/h2&gt;
&lt;p&gt;DbContext is designed to be a unit of work. It is meant to be created, used for a set of related operations, and disposed. It is not meant to be a long-lived singleton shared across the application.&lt;/p&gt;
&lt;p&gt;In an ASP.NET Core application, the standard pattern is to register DbContext as a scoped service, which means one instance per HTTP request. This is correct for most use cases. Each request gets its own context, its own change tracker, and its own transaction boundary. When the request completes, the context is disposed and any uncommitted changes are discarded.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// Program.cs: register DbContext as scoped
builder.Services.AddDbContext&amp;lt;AppDbContext&amp;gt;(options =&amp;gt;
    options.UseSqlServer(connectionString));
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The problems arise when DbContext escapes the scope it was designed for. Injecting it into a singleton service is the most common mistake. A singleton lives for the lifetime of the application. A scoped DbContext injected into a singleton effectively becomes a singleton itself, with a change tracker that accumulates state across every request the application handles until it is restarted.&lt;/p&gt;
&lt;p&gt;If you need database access in a singleton or a background service, use IDbContextFactory to create a short-lived context for each operation, use it, and dispose it immediately. This gives you the database access you need without the lifetime mismatch that causes subtle and difficult-to-diagnose bugs.&lt;/p&gt;
&lt;h2 id=&quot;the-mindset-behind-the-patterns&quot;&gt;THE MINDSET BEHIND THE PATTERNS&lt;/h2&gt;
&lt;p&gt;What these patterns have in common is that they require you to be deliberate rather than accepting defaults. EF Core defaults are designed to be convenient for getting started. They are not always the right choice for production, and the gap between convenient and correct widens as the application grows and the load increases.&lt;/p&gt;
&lt;p&gt;The developers who use EF Core well in production are not the ones who know the most features. They are the ones who ask the right questions before writing each query: is this data going to be modified? What related data does this operation actually need? What happens to this record when it is deleted? How many database round trips is this operation making?&lt;/p&gt;
&lt;p&gt;Those questions have good answers. Getting into the habit of asking them before writing the code, rather than after a performance problem surfaces, is what makes the difference.&lt;/p&gt;
&lt;h2 id=&quot;closing-thought&quot;&gt;CLOSING THOUGHT&lt;/h2&gt;
&lt;p&gt;EF Core is a powerful tool and a forgiving one. It will let you build something that works without understanding any of what is in this post. The cost of that forgiveness shows up later, in query logs, in memory profiles, and in data states that are difficult to explain. The patterns here are not about using EF Core correctly in an academic sense. They are about using it in a way you will not regret six months into a production system.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/32896/entity-framework-core-patterns-i-actually-use-in-production</guid>
<pubDate>Wed, 28 Oct 2026 18:49:25 +0000</pubDate>
</item>
<item>
<title>I Built ArchShield: An AI Architecture &amp; Security Baseline Assistant.</title>
<link>https://coderlegion.com/32441/i-built-archshield-an-ai-architecture-security-baseline-assistant</link>
<description>&lt;p&gt;&lt;em&gt;This post is my submission for &lt;a rel=&quot;nofollow&quot; href=&quot;https://dev.to/deved/build-apps-with-google-ai-studio&quot;&gt;DEV Education Track: Build Apps with Google AI Studio&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;h3 id=&quot;what-i-built&quot;&gt;What I Built&lt;/h3&gt;
&lt;p&gt;I built &lt;strong&gt;ArchShield&lt;/strong&gt;, an AI-powered architecture visualizer and security baseline assistant for developers.&lt;/p&gt;
&lt;p&gt;The goal was to turn a natural-language software architecture description into a structured, interactive architecture diagram while also checking for basic security controls across the detected components.&lt;/p&gt;
&lt;p&gt;The main prompt I used was:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Build a web application called ArchShield that analyzes a software architecture described in natural language. Identify components such as frontend, backend/API, database, cache, authentication, storage, external services, AI/LLM providers, and CDN/WAF. Visualize the architecture as an interactive multi-tier technical diagram with communication paths and network exposure. Add a basic security baseline review that distinguishes CONFIRMED, NOT MENTIONED, POTENTIAL CONCERN, and NOT APPLICABLE controls. Provide actionable security recommendations and integrate Imagen to generate a visual architectural blueprint.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I then iterated on the generated application to refine the architecture visualization, security analysis, error handling, and generated blueprint experience.&lt;/p&gt;
&lt;h3 id=&quot;demo&quot;&gt;Demo&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Live application:&lt;/strong&gt; &lt;a rel=&quot;nofollow&quot; href=&quot;https://archshield.ai.studio&quot;&gt;https://archshield.ai.studio&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;ArchShield can:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Interpret architecture descriptions using Gemini&lt;/li&gt;
&lt;li&gt;Detect frontend, backend, databases, caches, authentication, storage, external services, AI/LLM providers, and edge components&lt;/li&gt;
&lt;li&gt;Render an interactive multi-tier architecture diagram&lt;/li&gt;
&lt;li&gt;Inspect individual architecture components&lt;/li&gt;
&lt;li&gt;Perform a basic security baseline review&lt;/li&gt;
&lt;li&gt;Generate actionable security recommendations&lt;/li&gt;
&lt;li&gt;Generate a visual architecture blueprint using Imagen&lt;/li&gt;
&lt;li&gt;Export architecture and security information as Markdown and JSON&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/x7m1vvarar3d2vwkm3uc.png&quot; alt=&quot;ArchShield architecture visualization&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/qgsjhmx2qh21xb3to6v1.png&quot; alt=&quot;ArchShield security baseline&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/0c8bk9idizogv8ew9j7o.png&quot; alt=&quot;ArchShield generated architecture blueprint&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;my-experience&quot;&gt;My Experience&lt;/h3&gt;
&lt;p&gt;This track was a great demonstration of how quickly an AI-assisted development workflow can move from an idea to a working application.&lt;/p&gt;
&lt;p&gt;What surprised me most was not the initial code generation, but the iterative workflow. Gemini could generate a functional application quickly, but getting the result to behave exactly as intended still required testing, inspecting failures, and refining the instructions.&lt;/p&gt;
&lt;p&gt;I also ran into an XML parsing issue during the process. Instead of treating the generated application as finished once it worked in the preview, I used the error output to guide another iteration and corrected the generated implementation.&lt;/p&gt;
&lt;p&gt;The project also made me think about the difference between generating an application and engineering one. A working UI is only part of the result. Clear boundaries, predictable behavior, useful security guidance, and honest handling of uncertainty matter just as much.&lt;/p&gt;
&lt;p&gt;ArchShield deliberately treats &lt;code&gt;NOT MENTIONED&lt;/code&gt; differently from &lt;code&gt;POTENTIAL CONCERN&lt;/code&gt;: the absence of evidence is not automatically treated as evidence of a vulnerability.&lt;/p&gt;
&lt;p&gt;Overall, the track gave me a practical look at Google AI Studio's Build workflow and how Gemini can act as an implementation partner while still requiring engineering judgment, testing, and iteration from the developer.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/32441/i-built-archshield-an-ai-architecture-security-baseline-assistant</guid>
<pubDate>Mon, 26 Oct 2026 01:35:09 +0000</pubDate>
</item>
<item>
<title>Mastering React Performance: Optimizations That Actually Matter 🚀</title>
<link>https://coderlegion.com/32367/mastering-react-performance-optimizations-that-actually-matter</link>
<description>&lt;h3 id=&quot;introduction&quot;&gt;Introduction&lt;/h3&gt;
&lt;p&gt;React is a powerful JavaScript library, but as applications grow, performance bottlenecks can arise. Unnecessary re-renders, inefficient state management, and large bundle sizes can lead to slow applications.&lt;/p&gt;
&lt;p&gt;This guide covers practical performance optimizations that actually matter. Whether you’re working on a small project or a large-scale application, these best practices will help you write faster, scalable, and efficient React applications.&lt;/p&gt;
&lt;p&gt;By the end of this blog, you’ll learn:&lt;br&gt;
✅ Why React apps slow down&lt;br&gt;
✅ Best practices for optimizing re-renders&lt;br&gt;
✅ How to efficiently manage state&lt;br&gt;
✅ How to optimize large lists&lt;br&gt;
✅ lazy loading for faster load times&lt;/p&gt;
&lt;p&gt;Let’s dive in! 🚀&lt;/p&gt;
&lt;hr&gt;
&lt;h4 id=&quot;1-understanding-react-s-rendering-behavior&quot;&gt;1️⃣ Understanding React’s Rendering Behavior&lt;/h4&gt;
&lt;p&gt;Before optimizing, it’s crucial to understand how React renders components.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;📌 React re-renders a component when:&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;State changes in that component.&lt;/li&gt;
&lt;li&gt;Props change from the parent component.&lt;/li&gt;
&lt;li&gt;A parent re-renders, causing child components to re-render.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;🔍 How to detect unnecessary re-renders?&lt;/strong&gt;&lt;br&gt;
Use &lt;a rel=&quot;nofollow&quot; href=&quot;https://legacy.reactjs.org/blog/2018/09/10/introducing-the-react-profiler.html&quot;&gt;React DevTools Profiler&lt;/a&gt; to analyze component renders.&lt;/p&gt;
&lt;p&gt;✅ Solution: Memoization &amp;amp; Avoiding Unnecessary Renders&lt;/p&gt;
&lt;hr&gt;
&lt;h4 id=&quot;2-optimize-component-re-renders-with-react-memo&quot;&gt;2️⃣ Optimize Component Re-Renders with &lt;code&gt;React.memo&lt;/code&gt;&lt;/h4&gt;
&lt;p&gt;By default, React re-renders a component &lt;strong&gt;when its parent renders&lt;/strong&gt;, even if props haven’t changed.&lt;/p&gt;
&lt;p&gt;🚀 Solution: Use &lt;code&gt;React.memo()&lt;/code&gt; to prevent unnecessary renders&lt;/p&gt;
&lt;p&gt;🔴 Without &lt;code&gt;React.memo&lt;/code&gt; (Unnecessary re-renders)&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import React, { useState } from &quot;react&quot;;

const MovieDetails = ({ title, director }) =&amp;gt; {
  console.log(&quot;🎬 MovieDetails component re-rendered&quot;);
  return (
    &amp;lt;div&amp;gt;
      &amp;lt;h2&amp;gt;{title}&amp;lt;/h2&amp;gt;
      &amp;lt;p&amp;gt;Directed by: {director}&amp;lt;/p&amp;gt;
    &amp;lt;/div&amp;gt;
  );
};

const App = () =&amp;gt; {
  const [count, setCount] = useState(0);

  return (
    &amp;lt;div&amp;gt;
      &amp;lt;h1&amp;gt;React.memo Example&amp;lt;/h1&amp;gt;
      &amp;lt;button onClick={() =&amp;gt; setCount(count + 1)}&amp;gt;Click Me ({count})&amp;lt;/button&amp;gt;
      &amp;lt;MovieDetails title=&quot;Inception&quot; director=&quot;Christopher Nolan&quot; /&amp;gt;
    &amp;lt;/div&amp;gt;
  );
};

export default App;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;👉 Here, &lt;code&gt;MovieDetails&lt;/code&gt; &lt;strong&gt;re-renders every time&lt;/strong&gt; the parent renders, even if props hasn't changed.&lt;/p&gt;
&lt;p&gt;🟢 Optimizing with &lt;code&gt;React.memo&lt;/code&gt;&lt;br&gt;
To prevent unnecessary re-renders, wrap &lt;code&gt;MovieDetails&lt;/code&gt; with &lt;code&gt;React.memo()&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const MovieDetails = React.memo(({ title, director }) =&amp;gt; {
  console.log(&quot;🎬 MovieDetails component re-rendered&quot;);
  return (
    &amp;lt;div&amp;gt;
      &amp;lt;h2&amp;gt;{title}&amp;lt;/h2&amp;gt;
      &amp;lt;p&amp;gt;Directed by: {director}&amp;lt;/p&amp;gt;
    &amp;lt;/div&amp;gt;
  );
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;👉 Now, &lt;code&gt;MovieDetails&lt;/code&gt; will only re-render if its props (title or director) change.&lt;/p&gt;
&lt;p&gt;Clicking the button will no longer trigger a re-render of &lt;code&gt;MovieDetails&lt;/code&gt; unless new props are passed.&lt;/p&gt;
&lt;p&gt;📌 When to use &lt;code&gt;React.memo&lt;/code&gt;?&lt;br&gt;
✅ The component receives static or rarely changing props (e.g., user profile, movie info).&lt;br&gt;
✅ The component is expensive to render (e.g., complex UI elements like charts).&lt;br&gt;
✅ The parent re-renders frequently, but child props remain the same.&lt;/p&gt;
&lt;hr&gt;
&lt;h4 id=&quot;3-minimize-re-renders-with-derived-state&quot;&gt;3️⃣ Minimize Re-Renders with Derived State&lt;/h4&gt;
&lt;p&gt;Unnecessary state updates can cause frequent re-renders, slowing down your React app. Instead of storing derived state, compute values when needed.&lt;/p&gt;
&lt;p&gt;🔴 Problem: Storing Derived State in useState (Causes Extra Re-Renders)&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const [items, setItems] = useState([...]); 
const [filteredItems, setFilteredItems] = useState([]);

useEffect(() =&amp;gt; {
  setFilteredItems(items.filter(item =&amp;gt; item.active));
}, [items]); // Extra re-render every time `items` change
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;📌 Issue:&lt;br&gt;
Every update to items triggers an unnecessary state update for &lt;code&gt;filteredItems&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;This causes an extra re-render that could have been avoided.&lt;/p&gt;
&lt;p&gt;🟢 Solution: Use Derived State Instead of Storing It&lt;/p&gt;
&lt;p&gt;&lt;code&gt;const filteredItems = items.filter(item =&amp;gt; item.active);&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;✅ Now, &lt;code&gt;filteredItems&lt;/code&gt; is computed only when needed, avoiding extra state updates and re-renders.&lt;/p&gt;
&lt;hr&gt;
&lt;h4 id=&quot;4-optimize-function-props-with-usecallback&quot;&gt;4️⃣ Optimize Function Props with &lt;code&gt;useCallback&lt;/code&gt;&lt;/h4&gt;
&lt;p&gt;Passing new function instances as props causes child components to unnecessarily re-render.&lt;/p&gt;
&lt;p&gt;🔴 Problem: Without &lt;code&gt;useCallback&lt;/code&gt; (Function re-creates on every render)&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const ParentComponent = () =&amp;gt; {
  const handleClick = () =&amp;gt; {
    console.log(&quot;Button clicked&quot;);
  };

  return &amp;lt;ButtonComponent handleClick={handleClick} /&amp;gt;;
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;👉 Even if &lt;code&gt;handleClick&lt;/code&gt; doesn’t change, it gets re-created every time &lt;code&gt;ParentComponent&lt;/code&gt; renders.&lt;/p&gt;
&lt;p&gt;🟢 Solution: Use &lt;code&gt;useCallback&lt;/code&gt; to memoize the function&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const handleClick = useCallback(() =&amp;gt; {
  console.log(&quot;Button clicked&quot;);
}, []);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;✅ Now, &lt;code&gt;handleClick&lt;/code&gt; retains the &lt;strong&gt;same reference&lt;/strong&gt;, preventing unnecessary re-renders.&lt;/p&gt;
&lt;hr&gt;
&lt;h4 id=&quot;5-optimize-expensive-computations-with-usememo&quot;&gt;5️⃣ Optimize Expensive Computations with &lt;code&gt;useMemo&lt;/code&gt;&lt;/h4&gt;
&lt;p&gt;If a component performs heavy calculations on every render, it can slow down the UI.&lt;/p&gt;
&lt;p&gt;🔴 Problem: Without &lt;code&gt;useMemo&lt;/code&gt; (Expensive re-computation)&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const heavyComputation = (num) =&amp;gt; {
  console.log(&quot;Expensive calculation running...&quot;);
  return num * 2;
};

const Component = ({ number }) =&amp;gt; {
  const result = heavyComputation(number); // Runs on every render
  return &amp;lt;p&amp;gt;Result: {result}&amp;lt;/p&amp;gt;;
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;🟢 Solution: Use &lt;code&gt;useMemo&lt;/code&gt; to cache expensive computations&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const result = useMemo(() =&amp;gt; heavyComputation(number), [number]);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;✅ &lt;code&gt;heavyComputation()&lt;/code&gt; only re-runs when &lt;code&gt;number&lt;/code&gt; changes.&lt;/p&gt;
&lt;hr&gt;
&lt;h4 id=&quot;6-optimize-lists-amp-large-data-rendering&quot;&gt;6️⃣ Optimize Lists &amp;amp; Large Data Rendering&lt;/h4&gt;
&lt;p&gt;🔍 Problem: Rendering large lists slows down UI&lt;br&gt;
Solution: Use Virtualization with &lt;code&gt;react-window&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;🔴 Without Virtualization (Slow performance)&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const List = ({ items }) =&amp;gt; {
  return (
    &amp;lt;ul&amp;gt;
      {items.map((item) =&amp;gt; (
        &amp;lt;li key={item.id}&amp;gt;{item.name}&amp;lt;/li&amp;gt;
      ))}
    &amp;lt;/ul&amp;gt;
  );
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;🟢 With Virtualization (react-window)&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import { FixedSizeList as List } from &quot;react-window&quot;;

const VirtualizedList = ({ items }) =&amp;gt; (
  &amp;lt;List height={500} itemCount={items.length} itemSize={35} width={300}&amp;gt;
    {({ index, style }) =&amp;gt; (
      &amp;lt;div style={style}&amp;gt;{items[index].name}&amp;lt;/div&amp;gt;
    )}
  &amp;lt;/List&amp;gt;
);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;✅ Only visible items are rendered, improving performance significantly.&lt;/p&gt;
&lt;hr&gt;
&lt;h4 id=&quot;7-reduce-bundle-size-with-code-splitting-amp-lazy-loading&quot;&gt;7️⃣ Reduce Bundle Size with Code Splitting &amp;amp; Lazy Loading&lt;/h4&gt;
&lt;p&gt;Large JavaScript bundles slow down initial page load.&lt;/p&gt;
&lt;p&gt;✅ Solution: Use &lt;code&gt;React.lazy()&lt;/code&gt; for dynamic imports&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const HeavyComponent = React.lazy(() =&amp;gt; import(&quot;./HeavyComponent&quot;));

const App = () =&amp;gt; (
  &amp;lt;Suspense fallback={&amp;lt;div&amp;gt;Loading...&amp;lt;/div&amp;gt;}&amp;gt;
    &amp;lt;HeavyComponent /&amp;gt;
  &amp;lt;/Suspense&amp;gt;
);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;👉 Components load only when needed, reducing bundle size.&lt;/p&gt;
&lt;hr&gt;
&lt;h4 id=&quot;conclusion-build-fast-amp-scalable-react-apps&quot;&gt;Conclusion: Build Fast &amp;amp; Scalable React Apps&lt;/h4&gt;
&lt;p&gt;Optimizing React apps is crucial for fast UI interactions and smooth performance. By implementing:&lt;br&gt;
✅ Memoization (&lt;code&gt;React.memo&lt;/code&gt;, &lt;code&gt;useCallback&lt;/code&gt;, &lt;code&gt;useMemo&lt;/code&gt;)&lt;br&gt;
✅ Efficient state management&lt;br&gt;
✅ Virtualization for large lists&lt;br&gt;
✅ Lazy loading for faster page loads&lt;/p&gt;
&lt;p&gt;You’ll significantly improve performance! 🚀&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;💡 What performance tricks do you use in React?&lt;/strong&gt; Drop a comment below! 👇&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/32367/mastering-react-performance-optimizations-that-actually-matter</guid>
<pubDate>Sun, 25 Oct 2026 05:57:59 +0000</pubDate>
</item>
<item>
<title>How I Built My Reading Companion App, And How I Went Around The Storage Layer.</title>
<link>https://coderlegion.com/31308/how-i-built-my-reading-companion-app-and-how-i-went-around-the-storage-layer</link>
<description>&lt;p&gt;Building tools for yourself is arguably the most effective way to level up as a software engineer. There is no product manager handing down arbitrary specs, no artificial constraints, and most importantly, no room to ignore bugs when you are the primary end user.&lt;/p&gt;
&lt;p&gt;Recently, I shipped the working version of my book review and jotting system. What started as a personal fix for passive reading evolved into a full development sprint involving branding pivots, state management headaches, and deep dives into browser persistence.&lt;/p&gt;
&lt;p&gt;Here is the technical breakdown of how the build came together, the naming hurdle, and the architectural issues I ran into while handling local storage and book rendering.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The Core Problem: Passive Reading&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Most reading workflows in tech are broken. We buy books, skim through hundreds of pages, bookmark a few passages, and two weeks later struggle to articulate a single foundational concept from the text.&lt;/p&gt;
&lt;p&gt;Highlighters and generic note applications scatter thoughts across disconnected interfaces. I needed a focused, low-latency workspace that forced active retention—a structured workflow to capture raw notes, link chapters, synthesize main arguments, and track reading velocity without friction.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The Naming Conflict: Pivoting Early&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;In early development, the working title for the project ran straight into a trademark and existing namespace collision. An established tool in the developer/reading productivity space was already operating under the same name.&lt;/p&gt;
&lt;p&gt;It is easy to get emotionally attached to a project name and stall progress. But early on, engineering priorities need to override branding.&lt;/p&gt;
&lt;p&gt;I decoupled the product identity from the codebase:&lt;/p&gt;
&lt;p&gt;Focused 100% of the compute on getting the core MVP functional.&lt;/p&gt;
&lt;p&gt;Rebranded cleanly once the interaction architecture stabilized.&lt;/p&gt;
&lt;p&gt;Code functionality first. Marketing and branding second.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The MVP Architecture: Why Local Storage First?&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Before wiring up a remote database and authentication layer, I wanted an ultra-fast, offline-first client build to validate the reading UX immediately. I opted for local client storage to achieve zero-latency read/write operations during testing.&lt;/p&gt;
&lt;p&gt;However, treating browser localStorage as a makeshift application database quickly revealed several classic pitfalls.&lt;/p&gt;
&lt;p&gt;Trap 1: The Serialization and Deserialization Overhead&lt;/p&gt;
&lt;p&gt;In a document-heavy application, notes are not just flat strings; they are structured data trees containing chapter indices, timestamps, nested quotes, and tags, because localStorage only accepts DOMString values, every update required parsing and serializing massive JSON strings.&lt;br&gt;
As notes accumulated across multiple books, calling JSON.stringify(libraryState) on every keystroke in auto-saving note inputs led to noticeable UI thread stuttering.&lt;/p&gt;
&lt;p&gt;The Fix:&lt;br&gt;
I debounced the persistence logic and separated the global library state from the active note editor state. Instead of writing the entire collection on every character entry, updates were held in component state and flushed to storage only after a specified inactivity threshold:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The Boss Fight: Displaying the Book and Syncing State&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The most frustrating technical challenge was rendering the selected book dynamically alongside its associated notes and progress tracking without desynchronizing the view.&lt;/p&gt;
&lt;p&gt;The Problem: Ghost Renders and Stale Data&lt;/p&gt;
&lt;p&gt;When selecting a book from the shelf to open in the active reading workspace, the app frequently displayed stale metadata or threw runtime exceptions:&lt;/p&gt;
&lt;p&gt;Hydration Mismatches: If the user refreshed the page while viewing a specific book ID, the reader route loaded before localStorage had re-hydrated the global state, causing empty display cards or TypeError: Cannot read properties of undefined.&lt;/p&gt;
&lt;p&gt;Key Synchronization in Component Trees: Modifying notes inside a sub-component wasn't reflecting back in the parent book shelf overview without a manual page reload.&lt;/p&gt;
&lt;p&gt;How I Resolved It:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Resilient Lookup and Fallback Boundaries&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Instead of relying on fragile index lookups (books[index]), I switched to unique hash keys (UUID) and wrapped the book viewer in strict boundary guards:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Atomic Storage Updates via Custom Hooks&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;To solve the stale data synchronization bug across decoupled views, I centralized all storage read/write operations inside a custom hook (usePersistentStorage). This hook acts as a single source of truth, synchronizing local React state with native browser storage events. This clean abstraction eliminated direct, uncontrolled calls to localStorage across nested components and fixed the book display rendering bugs once and for all.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;What’s Next: Migrating to the Relational Cloud&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Building and battle-testing the client MVP using local storage achieved its primary goal: it validated the user experience under heavy daily reading sessions. I finished one full book using the tool and am actively managing three more on the platform.&lt;/p&gt;
&lt;p&gt;However, local storage is inherently limited:&lt;/p&gt;
&lt;p&gt;It is locked to a single browser profile and device.&lt;/p&gt;
&lt;p&gt;It caps out around 5MB.&lt;/p&gt;
&lt;p&gt;It offers zero data durability if browser data is cleared.&lt;/p&gt;
&lt;p&gt;The next engineering phase is migrating this data model into a scalable backend. I have opted for Supabase (PostgreSQL) to handle user authentication, relational table modeling for book-to-note foreign key relationships, and row-level security.&lt;/p&gt;
&lt;p&gt;Key Takeaways&lt;/p&gt;
&lt;p&gt;Don't let vanity blockers halt development: If your project name is taken, decouple your logic and keep shipping.&lt;/p&gt;
&lt;p&gt;Treat local storage with respect: Avoid raw JSON parsing on hot render paths; debounce writes and structure your schema early.&lt;/p&gt;
&lt;p&gt;Dogfood your software: You find the nastiest UI and state bugs only when you use your own build under realistic daily pressure.&lt;/p&gt;
&lt;p&gt;The live version is up, stable, and actively handling notes. Now, onto the data layer.&lt;br&gt;
This is the live link: &lt;a rel=&quot;nofollow&quot; href=&quot;https://sovari.netlify.app/&quot;&gt;https://sovari.netlify.app/&lt;/a&gt;&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/31308/how-i-built-my-reading-companion-app-and-how-i-went-around-the-storage-layer</guid>
<pubDate>Fri, 16 Oct 2026 08:18:52 +0000</pubDate>
</item>
<item>
<title>🚦 EDA Without Governance: A Fast Track to Enterprise Chaos</title>
<link>https://coderlegion.com/31224/eda-without-governance-a-fast-track-to-enterprise-chaos</link>
<description>&lt;p&gt;In today's digital enterprise, Event-Driven Architecture (EDA) has become a preferred architectural pattern for building scalable, loosely coupled, and real-time systems. By enabling applications to publish and subscribe to events independently, EDA provides the flexibility and agility required to support modern business demands.&lt;/p&gt;
&lt;p&gt;However, there is a critical truth that many organizations discover only after their event ecosystem starts to grow:&lt;/p&gt;
&lt;p&gt;⚠️ The success of EDA depends less on technology and more on governance.&lt;/p&gt;
&lt;p&gt;Without proper governance, an event bus can quickly evolve into a chaotic integration landscape where dependencies are unclear, ownership becomes fragmented, and changes introduce widespread downstream impacts.&lt;/p&gt;
&lt;p&gt;🌐 EDA Is More Than Event Distribution&lt;/p&gt;
&lt;p&gt;EDA is fundamentally a distribution design that allows applications to publish and consume events across the enterprise.&lt;/p&gt;
&lt;p&gt;✅ New publishers can share information with multiple consumers.&lt;br&gt;
✅ New consumers can subscribe to existing event streams.&lt;br&gt;
✅ Applications remain loosely coupled and independently deployable.&lt;/p&gt;
&lt;p&gt;But this flexibility comes with responsibility.&lt;/p&gt;
&lt;p&gt;An event published today may be consumed by numerous applications, either directly or indirectly. As adoption grows, every design decision becomes part of a shared enterprise contract.&lt;/p&gt;
&lt;p&gt;Without governance, what starts as flexibility can quickly become complexity.&lt;/p&gt;
&lt;p&gt;⚠️ The Hidden Risk of Uncontrolled Growth&lt;/p&gt;
&lt;p&gt;When onboarding new applications into an event-driven ecosystem, governance standards and processes should be established before implementation begins.&lt;/p&gt;
&lt;p&gt;Without these controls:&lt;/p&gt;
&lt;p&gt;🔴 Topic structures become inconsistent.&lt;br&gt;
🔴 Applications subscribe to unnecessary events.&lt;br&gt;
🔴 Event routing becomes difficult to understand and maintain.&lt;br&gt;
🔴 Sensitive data may be exposed to unauthorized consumers.&lt;br&gt;
🔴 Small changes trigger large-scale application rework.&lt;/p&gt;
&lt;p&gt;The impact is not limited to event broker configurations.&lt;/p&gt;
&lt;p&gt;A seemingly simple change to an event topic, payload structure, or routing pattern may require modifications across multiple consuming applications, testing cycles, operational procedures, and business processes.&lt;/p&gt;
&lt;p&gt;In large-scale enterprises, a single event redesign can affect dozens of interconnected systems.&lt;/p&gt;
&lt;p&gt;🔄 Governance Must Cover the Entire Lifecycle&lt;/p&gt;
&lt;p&gt;EDA governance is not a one-time architecture review.&lt;/p&gt;
&lt;p&gt;It should span the entire delivery lifecycle:&lt;/p&gt;
&lt;p&gt;📋 Design&lt;br&gt;
👨💻 Development&lt;br&gt;
🧪 Testing&lt;br&gt;
🚀 Deployment&lt;br&gt;
⚙️ Production Operations&lt;/p&gt;
&lt;p&gt;Any governance gap can result in:&lt;/p&gt;
&lt;p&gt;❌ Increased technical debt&lt;br&gt;
❌ Integration failures&lt;br&gt;
❌ Security and compliance risks&lt;br&gt;
❌ Operational complexity&lt;br&gt;
❌ Higher maintenance costs&lt;/p&gt;
&lt;p&gt;The most successful event-driven organizations balance architectural flexibility with disciplined governance.&lt;/p&gt;
&lt;p&gt;🎯 Key Areas of EDA Governance&lt;/p&gt;
&lt;p&gt;🏷️ 1. Topic Naming Standards&lt;/p&gt;
&lt;p&gt;Topics are the foundation of event discoverability and maintainability.&lt;/p&gt;
&lt;p&gt;Every topic should follow a mandatory naming standard that clearly identifies:&lt;/p&gt;
&lt;p&gt;✅ Business domain&lt;br&gt;
✅ Business capability&lt;br&gt;
✅ Event type&lt;br&gt;
✅ Environment&lt;/p&gt;
&lt;p&gt;A consistent naming convention helps architects, developers, and support teams quickly understand event purpose and ownership.&lt;/p&gt;
&lt;p&gt;Good governance starts with naming things correctly.&lt;/p&gt;
&lt;p&gt;📬 2. Queue Subscription Governance&lt;/p&gt;
&lt;p&gt;Applications should subscribe only to events that are required for their business functions.&lt;/p&gt;
&lt;p&gt;Governance should ensure:&lt;/p&gt;
&lt;p&gt;✅ Controlled subscription approval&lt;br&gt;
✅ Consumer registration and ownership&lt;br&gt;
✅ Dependency visibility&lt;br&gt;
✅ Periodic subscription reviews&lt;/p&gt;
&lt;p&gt;This prevents unnecessary event consumption and reduces future change impacts.&lt;/p&gt;
&lt;p&gt;Not every application needs every event.&lt;/p&gt;
&lt;p&gt;🔀 3. Event Flow Design&lt;/p&gt;
&lt;p&gt;A well-designed event flow enables systems to evolve without disrupting the broader ecosystem.&lt;/p&gt;
&lt;p&gt;Governance should enforce:&lt;/p&gt;
&lt;p&gt;✅ Standard routing principles&lt;br&gt;
✅ Clear producer and consumer responsibilities&lt;br&gt;
✅ Traceable event paths&lt;br&gt;
✅ Flexible addition and removal of components&lt;/p&gt;
&lt;p&gt;A properly governed event flow allows organizations to grow their architecture while maintaining control and predictability.&lt;/p&gt;
&lt;p&gt;Flexibility should be intentional, not accidental.&lt;/p&gt;
&lt;p&gt;🔐 4. Data Governance and Access Control&lt;/p&gt;
&lt;p&gt;Not every consumer should have access to every event.&lt;/p&gt;
&lt;p&gt;Organizations must ensure applications can subscribe only to authorized events, especially when they contain:&lt;/p&gt;
&lt;p&gt;🔒 Personally Identifiable Information (PII)&lt;br&gt;
🔒 Customer information&lt;br&gt;
🔒 Financial transactions&lt;br&gt;
🔒 Business-sensitive data&lt;/p&gt;
&lt;p&gt;Strong governance ensures:&lt;/p&gt;
&lt;p&gt;✅ Data privacy&lt;br&gt;
✅ Regulatory compliance&lt;br&gt;
✅ Security controls&lt;br&gt;
✅ Consumer authorization&lt;/p&gt;
&lt;p&gt;The principle should be simple:&lt;/p&gt;
&lt;p&gt;🔑 Subscribe only to the data you are authorized to consume.&lt;/p&gt;
&lt;p&gt;🏆 Governance Determines EDA Success&lt;/p&gt;
&lt;p&gt;Many organizations focus heavily on event brokers, event meshes, streaming platforms, and messaging technologies.&lt;/p&gt;
&lt;p&gt;While these technologies are important, they are not what ultimately determines the success of an Event-Driven Architecture.&lt;/p&gt;
&lt;p&gt;The true differentiator is governance.&lt;/p&gt;
&lt;p&gt;Well-defined standards, ownership models, review processes, security controls, and lifecycle management enable organizations to scale their event ecosystem confidently and sustainably.&lt;/p&gt;
&lt;p&gt;Without governance, flexibility becomes chaos.&lt;/p&gt;
&lt;p&gt;With governance, EDA becomes a strategic enabler of enterprise agility, innovation, and business growth.&lt;/p&gt;
&lt;p&gt;💡 Final Thought&lt;/p&gt;
&lt;p&gt;🚦 EDA without governance is simply message transportation.&lt;/p&gt;
&lt;p&gt;🚀 EDA with governance becomes sustainable enterprise architecture.&lt;/p&gt;
&lt;p&gt;The question is not whether your organization should adopt Event-Driven Architecture.&lt;/p&gt;
&lt;p&gt;The real question is:&lt;/p&gt;
&lt;p&gt;Do you have the governance needed to keep it from becoming enterprise chaos?&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/31224/eda-without-governance-a-fast-track-to-enterprise-chaos</guid>
<pubDate>Thu, 15 Oct 2026 16:03:25 +0000</pubDate>
</item>
<item>
<title>🚀 Managing MCP Servers in VS Code? Explore SEOSiri MCP Suite Manager!</title>
<link>https://coderlegion.com/31223/managing-mcp-servers-in-vs-code-explore-seosiri-mcp-suite-manager</link>
<description>&lt;p&gt;As AI-powered development evolves, managing Model Context Protocol (MCP) integrations is becoming an important part of building connected developer workflows. A well-organized MCP setup can help developers connect AI tools with external services, tools, and data sources.&lt;/p&gt;
&lt;p&gt;🛠️ Meet SEOSiri MCP Suite Manager&lt;/p&gt;
&lt;p&gt;Explore our VS Code extension for MCP server management and developer workflows.&lt;/p&gt;
&lt;p&gt;💡 Why explore MCP management?&lt;br&gt;
🔹 Developer Productivity: Organize your MCP-related development workflow within VS Code.&lt;br&gt;
🔹 AI Tool Integration: Explore MCP server configurations for compatible AI clients and development environments.&lt;br&gt;
🔹 Workflow Optimization: Simplify the process of working with MCP server setups.&lt;br&gt;
🔹 Developer Ecosystem: Build toward more connected AI-assisted development workflows.&lt;br&gt;
🔹 Project Opportunities: Get in touch with SEOSiri for development, optimization, and digital marketing support.&lt;br&gt;
&lt;img src=&quot;https://coderlegion.com/?qa=blob&amp;amp;qa_blobid=13321562692722168591&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;🔗 Visit the SEOSiri Visual Studio Marketplace:&lt;br&gt;
&lt;a rel=&quot;nofollow&quot; href=&quot;https://marketplace.visualstudio.com/publishers/seosiri&quot;&gt;https://marketplace.visualstudio.com/publishers/seosiri&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;📦 Get MCP Suite Manager:&lt;br&gt;
&lt;a rel=&quot;nofollow&quot; href=&quot;https://marketplace.visualstudio.com/items?itemName=SEOSiri.seosiri-vscode-mcp-manager&quot;&gt;https://marketplace.visualstudio.com/items?itemName=SEOSiri.seosiri-vscode-mcp-manager&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;🤝 Need help developing, optimizing, or marketing your developer product? DM SEOSiri to discuss your project and potential collaboration.&lt;/p&gt;
&lt;p&gt;The future of AI development depends not only on capable models, but also on the tools and integrations that connect them.&lt;/p&gt;
&lt;p&gt;Build. Integrate. Optimize. Make it discoverable.&lt;/p&gt;
&lt;h2 id=&quot;mcp-modelcontextprotocol-vscode-aidevelopment-developertools-seosiri&quot;&gt;MCP #ModelContextProtocol #VSCode #AIDevelopment #DeveloperTools #SEOSiri&lt;/h2&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/31223/managing-mcp-servers-in-vs-code-explore-seosiri-mcp-suite-manager</guid>
<pubDate>Thu, 15 Oct 2026 15:41:32 +0000</pubDate>
</item>
<item>
<title>Evening the Terrain &amp; the construction of Road Infrastructure | UE5 Devlog</title>
<link>https://coderlegion.com/31123/evening-the-terrain-the-construction-of-road-infrastructure-ue5-devlog</link>
<description>&lt;p&gt;In this devlog, I continue shaping Kazuhara Resort by evening out the terrain and building its road infrastructure in Unreal Engine 5. The focus is on preparing the ground and creating routes that connect the resort’s different areas.&lt;/p&gt;
&lt;p&gt;I showcase the Flatten and Smooth tools to level uneven surfaces and soften transitions, the Ramp tool to connect different elevations, and the Landscape Paint tool to define the road surfaces. These changes help establish the resort’s layout and make the terrain more practical for further construction.&lt;/p&gt;
</description>
<category>Videos</category>
<guid isPermaLink="true">https://coderlegion.com/31123/evening-the-terrain-the-construction-of-road-infrastructure-ue5-devlog</guid>
<pubDate>Wed, 14 Oct 2026 23:23:34 +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>AI Slop Is the Second Ship</title>
<link>https://coderlegion.com/30827/ai-slop-is-the-second-ship</link>
<description>&lt;p&gt;As the story goes, Theseus sailed to Crete, killed the Minotaur, and brought home the young Athenians who had been sent there to be fed to it. The ship that carried them had thirty oars. Athens decided it was too important to lose.&lt;/p&gt;
&lt;p&gt;So the city kept it. Every year it sailed to Delos on a sacred voyage, and that voyage mattered so much that Athens paused all executions until the ship came back. Socrates spent his last month alive waiting for it.&lt;/p&gt;
&lt;p&gt;Your ship is less glamorous. It is a &quot;retry module for the payments service&quot;. It wraps calls to the payment provider, waits a little longer after each failure, and gives up after a few retries.&lt;/p&gt;
&lt;p&gt;One line in it matters more than the rest. It refuses to retry a charge unless the request carries an idempotent key, a tag that lets the provider spot a repeat. Someone added that line after a bad week, when a flaky network turned one payment into two.&lt;/p&gt;
&lt;p&gt;The module works. The team builds on it. It becomes too important to lose.&lt;/p&gt;
&lt;h3 id=&quot;the-first-plank-rots&quot;&gt;The first plank rots&lt;/h3&gt;
&lt;p&gt;Wood does not last forever. When a plank on Theseus's ship rotted, the shipwrights pulled it out and fitted a new one. Plutarch describes them &quot;putting in new and stronger timber in their place.&quot; The crew knew which plank had gone, and why.&lt;/p&gt;
&lt;p&gt;Your module gets its first rotten plank too. An agent refactors the backoff into something cleaner. You read the diff. You understand it. You approve it.&lt;/p&gt;
&lt;p&gt;Then the logging. Then the tests. Then the part you were proudest of, because the agent's version reads better. Each change is small, and each one is reviewed. A year later, almost nothing you typed is left.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Every plank replaced, one sensible change at a time.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Is it still your module?&lt;/p&gt;
&lt;p&gt;The philosophers asked the same thing about the ship. I think the answer is the same in both cases. &lt;strong&gt;Yes.&lt;/strong&gt; The ship never stopped sailing, and every plank was replaced by people who knew what the ship was for. Your module never stopped running, and every change passed through someone who knew why the idempotent line was there, and kept it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Replacing planks was never the problem.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Scott Robinson has named the version of this that goes wrong, &lt;a rel=&quot;nofollow&quot; href=&quot;https://unstack.io/theseus-slop&quot;&gt;Theseus Slop&lt;/a&gt;: a codebase replaced until you own it without understanding it. That failure is real. But the old story has a stranger second half.&lt;/p&gt;
&lt;h3 id=&quot;someone-collects-the-old-planks&quot;&gt;Someone collects the old planks&lt;/h3&gt;
&lt;p&gt;In 1655, Thomas Hobbes added a character to the paradox.&lt;/p&gt;
&lt;p&gt;Suppose someone followed the shipwrights and kept every plank they threw away. Suppose he fitted those planks back together, in the same order, and built a ship out of them.&lt;/p&gt;
&lt;p&gt;Now Athens has two ships.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;One has the voyage. The other has the wood.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Your codebase gets its second ship too.&lt;/p&gt;
&lt;p&gt;A new service needs to call the same payment provider. A teammate, short on time, asks an agent for &quot;a retry wrapper like the one in payments.&quot; The agent does not copy your module. It assembles one from the planks it learned on: thousands of retry wrappers from public code, tutorials and answers. None of them were there for your team's bad week.&lt;/p&gt;
&lt;p&gt;The new module compiles. It has exponential backoff, a maximum attempt count, clean logging. From the dock it looks exactly like yours. The tests pass, because nobody wrote a test for a line nobody knew should exist.&lt;/p&gt;
&lt;p&gt;It has no check on idempotency. Not because anyone removed it. Because nobody aboard ever knew it belonged there.&lt;/p&gt;
&lt;h3 id=&quot;the-second-ship-goes-to-sea&quot;&gt;The second ship goes to sea&lt;/h3&gt;
&lt;p&gt;Hobbes's second ship never sailed anywhere. It did not need to. Its whole problem was that it claimed to be the ship that had been to Delos.&lt;/p&gt;
&lt;p&gt;Your second ship does sail. The first afternoon the provider is slow, it retries every charge that timed out. Some of those had already gone through. Customers get billed twice. The team opens the file. Nobody can say why it retries charges, because nobody ever decided that it should.&lt;/p&gt;
&lt;p&gt;That is how I have started to think about the word slop.&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;AI slop is not the ship with every plank replaced. It is the second ship.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;John David Pressman has a line in his essay &lt;a rel=&quot;nofollow&quot; href=&quot;https://minihf.com/posts/2025-10-02-on-slop/&quot;&gt;On Slop&lt;/a&gt; that fits it exactly: &quot;Slop is generated from the authors prior without new thinking or evidence.&quot;&lt;/p&gt;
&lt;p&gt;&quot;AI wrote it&quot; is not the test. Your original module was also rewritten by an agent, plank by plank, and it is still your ship. The Athenians did not grow their own timber either. New wood always comes from somewhere.&lt;/p&gt;
&lt;p&gt;The difference is the voyage. One module was changed by people who knew what each plank was holding up. The other was assembled from the average of every retry wrapper on the internet, and it shipped because it looked like a ship.&lt;/p&gt;
&lt;h3 id=&quot;the-plank-test&quot;&gt;The plank test&lt;/h3&gt;
&lt;p&gt;Both ships look identical from the dock. You cannot tell them apart by reading the wood. You can tell them apart by asking about it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Point at any plank and ask why it is there.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;If someone can answer without reverse-engineering it, the plank belongs to Theseus's ship. If the only honest answer is &quot;the model put it there,&quot; it belongs to the second ship.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;&lt;/th&gt;&lt;th&gt;Theseus's ship &lt;/th&gt;&lt;th&gt;The second ship&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;Who chose each plank &lt;/td&gt;&lt;td&gt;The crew that sails it &lt;/td&gt;&lt;td&gt;Nobody&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Where it has been &lt;/td&gt;&lt;td&gt;To Delos and back, every year &lt;/td&gt;&lt;td&gt;Nowhere&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;em&gt;In the codebase&lt;/em&gt; &lt;/td&gt;&lt;td&gt; &lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Backoff between attempts &lt;/td&gt;&lt;td&gt;Rewritten by an agent, reviewed, understood &lt;/td&gt;&lt;td&gt;Generated, never questioned&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Idempotency check &lt;/td&gt;&lt;td&gt;Added after the bad week, kept through every rewrite &lt;/td&gt;&lt;td&gt;Missing&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&quot;Why is this here?&quot; &lt;/td&gt;&lt;td&gt;Someone can answer &lt;/td&gt;&lt;td&gt;&quot;The model put it there&quot;&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The test does not care how many planks were replaced, or who cut them. It only cares whether somebody chose each one.&lt;/p&gt;
&lt;p&gt;A few second-ship planks will slip into any codebase. The trouble starts when they stop being rare, because each one is a part nobody knows how to repair when it cracks at sea.&lt;/p&gt;
&lt;h3 id=&quot;somebody-has-to-keep-sailing-it&quot;&gt;Somebody has to keep sailing it&lt;/h3&gt;
&lt;p&gt;The Athenians never answered the philosophers. They kept sailing the ship to Delos every year, and that was answer enough.&lt;/p&gt;
&lt;p&gt;I think it is the only answer available to us too. The model will keep supplying planks faster than any of us can cut them. A codebase stays ours as long as someone aboard knows why each one went in.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Replace as many planks as you like. Just do not let one in that nobody can explain.&lt;/em&gt;&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/30827/ai-slop-is-the-second-ship</guid>
<pubDate>Mon, 12 Oct 2026 14:37:46 +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>Jev vs OpenAI Decisions API: A Follow Up</title>
<link>https://coderlegion.com/30782/jev-vs-openai-decisions-api-a-follow-up</link>
<description>&lt;p&gt;In my recent &lt;a rel=&quot;nofollow&quot; href=&quot;https://coderlegion.com/27909/jev-by-typesafe-ai-hype-tool-or-threat&quot;&gt;article&lt;/a&gt; about Jev by TypeSafe AI, I questioned whether its speed, cost, and confidence scores offered enough advantages over conventional LLMs with Structured Outputs.&lt;/p&gt;
&lt;p&gt;And here is an interesting development.&lt;/p&gt;
&lt;p&gt;On October 6, OpenAI announced its Decisions API became available in public beta (see the announcement and the API doc links below).&lt;/p&gt;
&lt;p&gt;Powered by GPT-6 Luna, it provides:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Typed decisions: predicates, predefined choices, and numeric scores.&lt;/li&gt;
&lt;li&gt;Probability and confidence estimates.&lt;/li&gt;
&lt;li&gt;Text and image inputs.&lt;/li&gt;
&lt;li&gt;Claimed ~10× faster responses than the Responses API.&lt;/li&gt;
&lt;li&gt;$0.10 per million input tokens, with no output-token charges.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The functionality is remarkably similar to Jev. OpenAI now offers a specialized API for essentially the same tasks, with comparable pricing.&lt;/p&gt;
&lt;p&gt;I would be interested to see independent benchmarks comparing the two APIs, particularly on accuracy, latency, cost, and confidence calibration.&lt;/p&gt;
&lt;p&gt;And this development supports my original conclusion about Jev. We already had much of this functionality with Structured Outputs. Specialized decision APIs may make it faster and cheaper, especially at scale.&lt;/p&gt;
&lt;h4 id=&quot;pricing-high-load-sla&quot;&gt;Pricing, High Load, SLA&lt;/h4&gt;
&lt;p&gt;Let's start with pricing. Jev costs $0.042 per million input tokens, while OpenAI Decisions API costs $0.10. So Jev is about &lt;strong&gt;2.4 times cheaper&lt;/strong&gt;. Neither charges for output tokens.&lt;/p&gt;
&lt;p&gt;Looking at the upper capacity limits, Jev supports 6 million input tokens per minute, compared with up to 180 million for GPT-6 Luna. That's a 30-fold difference, although the OpenAI figure has yet to be confirmed for Decisions API.&lt;/p&gt;
&lt;p&gt;On the infrastructure side, Jev is still expanding its GPU capacity, and its limits may change. OpenAI already offers enterprise capacity reservations, so they may be available for Decisions API.&lt;/p&gt;
&lt;p&gt;Finally, Jev has a clear pricing advantage, while OpenAI appears better positioned for high-load workloads and potentially better SLA. Whether the price advantage is sufficient vs OpenAI's solutions remains an open question. OpenAI definitely has greater bargaining power, capital, and infrastructure resources. If the niche is attractive, competitors will need more advantages than just pricing to survive in it.&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://community.openai.com/t/decisions-api-is-now-available-in-public-beta/1403877&quot;&gt;Decisions API is now available in Public Beta&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://developers.openai.com/api/docs/guides/decisions&quot;&gt;The API Official Docs&lt;/a&gt;&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/30782/jev-vs-openai-decisions-api-a-follow-up</guid>
<pubDate>Mon, 12 Oct 2026 07:51:10 +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>Building Practical Python Tools That Solve Real World Problems</title>
<link>https://coderlegion.com/30214/building-practical-python-tools-that-solve-real-world-problems</link>
<description>&lt;p&gt;Python is one of those technologies that makes it possible to turn an everyday problem into a working solution with relatively little code.&lt;/p&gt;
&lt;p&gt;For me, Python development is not only about writing programs or learning new frameworks. &lt;strong&gt;It is about finding problems that people deal with every day and building tools that make those problems easier to solve.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Many businesses still spend hours doing repetitive tasks manually. Copying information between spreadsheets, preparing reports, processing files, checking records, sending data to different systems, or updating databases may seem like small tasks individually. But when they are repeated every day, they can consume a significant amount of time.&lt;/p&gt;
&lt;p&gt;This is where I enjoy using Python.&lt;/p&gt;
&lt;h3 id=&quot;why-i-focus-on-practical-python-development&quot;&gt;Why I Focus on Practical Python Development&lt;/h3&gt;
&lt;p&gt;I enjoy building software that has a clear purpose.&lt;/p&gt;
&lt;p&gt;Instead of creating applications simply because a technology is interesting, I prefer projects where the result can actually help someone work faster, reduce errors, or manage information more efficiently.&lt;/p&gt;
&lt;p&gt;Python provides a great environment for this because it can be used for many different types of problems.&lt;/p&gt;
&lt;p&gt;I work with technologies such as &lt;strong&gt;Python, Tkinter, CustomTkinter, SQLite, APIs, Pandas, OpenPyXL, and automation tools&lt;/strong&gt; to build practical applications and scripts.&lt;/p&gt;
&lt;p&gt;Sometimes the solution is a small automation script.&lt;/p&gt;
&lt;p&gt;Sometimes it is a desktop application.&lt;/p&gt;
&lt;p&gt;Sometimes it involves processing hundreds or thousands of rows from an Excel file.&lt;/p&gt;
&lt;p&gt;And sometimes the solution requires connecting different systems through an API.&lt;/p&gt;
&lt;p&gt;The technology can change, but the goal remains the same:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Build something useful that solves a real problem.&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id=&quot;turning-repetitive-work-into-automation&quot;&gt;Turning Repetitive Work Into Automation&lt;/h3&gt;
&lt;p&gt;One of the areas I enjoy most is automation.&lt;/p&gt;
&lt;p&gt;Repetitive work is everywhere.&lt;/p&gt;
&lt;p&gt;A person may need to open an Excel file, check certain values, filter information, update another spreadsheet, create a report, and repeat the same process every day.&lt;/p&gt;
&lt;p&gt;If a task follows predictable rules, there is a good chance that Python can help automate at least part of it.&lt;/p&gt;
&lt;p&gt;For example, a Python automation tool can:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Read data from Excel files&lt;/li&gt;
&lt;li&gt;Process and clean information&lt;/li&gt;
&lt;li&gt;Find specific records&lt;/li&gt;
&lt;li&gt;Generate reports&lt;/li&gt;
&lt;li&gt;Update databases&lt;/li&gt;
&lt;li&gt;Communicate with APIs&lt;/li&gt;
&lt;li&gt;Rename or organize files&lt;/li&gt;
&lt;li&gt;Perform calculations&lt;/li&gt;
&lt;li&gt;Create new Excel reports&lt;/li&gt;
&lt;li&gt;Reduce repetitive manual operations&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The interesting part is not simply writing the script.&lt;/p&gt;
&lt;p&gt;The real challenge is understanding &lt;strong&gt;how the existing process works&lt;/strong&gt; and then deciding how technology can improve it.&lt;/p&gt;
&lt;p&gt;A good automation solution should make work simpler without creating unnecessary complexity.&lt;/p&gt;
&lt;h3 id=&quot;working-with-excel-data&quot;&gt;Working With Excel Data&lt;/h3&gt;
&lt;p&gt;Excel is still heavily used in many businesses.&lt;/p&gt;
&lt;p&gt;Even when a company has other systems, Excel often remains part of the daily workflow.&lt;/p&gt;
&lt;p&gt;This is why working with Excel programmatically is a useful skill.&lt;/p&gt;
&lt;p&gt;With Python and libraries such as &lt;strong&gt;Pandas and OpenPyXL&lt;/strong&gt;, it is possible to process large amounts of spreadsheet data much more efficiently.&lt;/p&gt;
&lt;p&gt;For example, imagine someone receives a spreadsheet containing thousands of records.&lt;/p&gt;
&lt;p&gt;Manually checking every row can take hours.&lt;/p&gt;
&lt;p&gt;A Python application could read the file, validate the information, identify missing values, filter records based on specific conditions, perform calculations, and generate a cleaned or summarized version.&lt;/p&gt;
&lt;p&gt;The goal is not necessarily to replace Excel.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The goal is to remove unnecessary manual work around Excel.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;That distinction is important.&lt;/p&gt;
&lt;p&gt;Technology should fit into the business process rather than force people to completely change how they work.&lt;/p&gt;
&lt;h3 id=&quot;building-simple-desktop-applications&quot;&gt;Building Simple Desktop Applications&lt;/h3&gt;
&lt;p&gt;Not every business problem needs a web application.&lt;/p&gt;
&lt;p&gt;Sometimes a simple desktop application is the better solution.&lt;/p&gt;
&lt;p&gt;This is where &lt;strong&gt;Tkinter and CustomTkinter&lt;/strong&gt; become useful.&lt;/p&gt;
&lt;p&gt;With these tools, I can build graphical interfaces that allow users to interact with Python applications without needing to run commands from a terminal.&lt;/p&gt;
&lt;p&gt;A user might need to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Select an Excel file&lt;/li&gt;
&lt;li&gt;Enter some information&lt;/li&gt;
&lt;li&gt;Click a button&lt;/li&gt;
&lt;li&gt;Process the data&lt;/li&gt;
&lt;li&gt;View results&lt;/li&gt;
&lt;li&gt;Export a report&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Instead of requiring technical knowledge, a graphical interface can make the tool much easier to use.&lt;/p&gt;
&lt;p&gt;I particularly enjoy this part of development because it combines programming with usability.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;A tool is more valuable when the person using it does not need to understand how the code works.&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id=&quot;sqlite-for-lightweight-data-management&quot;&gt;SQLite for Lightweight Data Management&lt;/h3&gt;
&lt;p&gt;When an application needs to store structured information, SQLite can be a practical choice.&lt;/p&gt;
&lt;p&gt;It is lightweight, simple to configure, and does not require a separate database server for many desktop and small-business applications.&lt;/p&gt;
&lt;p&gt;For example, a Python application could use SQLite to store:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Customer information&lt;/li&gt;
&lt;li&gt;Product records&lt;/li&gt;
&lt;li&gt;Transaction history&lt;/li&gt;
&lt;li&gt;Application settings&lt;/li&gt;
&lt;li&gt;Processed files&lt;/li&gt;
&lt;li&gt;User activity&lt;/li&gt;
&lt;li&gt;Reports or generated records&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Combining Python with SQLite makes it possible to create useful applications without introducing unnecessary infrastructure.&lt;/p&gt;
&lt;p&gt;For small and medium-sized tools, this can be a very effective approach.&lt;/p&gt;
&lt;h3 id=&quot;connecting-applications-with-apis&quot;&gt;Connecting Applications With APIs&lt;/h3&gt;
&lt;p&gt;Modern applications rarely work completely on their own.&lt;/p&gt;
&lt;p&gt;Businesses often use multiple systems, and those systems need to exchange information.&lt;/p&gt;
&lt;p&gt;APIs make this possible.&lt;/p&gt;
&lt;p&gt;With Python, I can work with APIs to retrieve information from external services, send data to another system, or connect different parts of a workflow.&lt;/p&gt;
&lt;p&gt;For example, an automation tool might retrieve information from an API, process it using Python, store important data in SQLite, and finally generate an Excel report.&lt;/p&gt;
&lt;p&gt;This creates a workflow where multiple technologies work together.&lt;/p&gt;
&lt;p&gt;The interesting part is seeing how these pieces can be connected to create something useful.&lt;/p&gt;
&lt;h3 id=&quot;learning-through-projects&quot;&gt;Learning Through Projects&lt;/h3&gt;
&lt;p&gt;One thing I have learned while improving my Python skills is that projects teach differently from tutorials.&lt;/p&gt;
&lt;p&gt;A tutorial can explain how a library works.&lt;/p&gt;
&lt;p&gt;But a real project forces you to think about things that tutorials sometimes do not cover.&lt;/p&gt;
&lt;p&gt;What happens when the input file is empty?&lt;/p&gt;
&lt;p&gt;What if a column is missing?&lt;/p&gt;
&lt;p&gt;What if the API does not respond?&lt;/p&gt;
&lt;p&gt;What if the user enters invalid information?&lt;/p&gt;
&lt;p&gt;What happens when thousands of records need to be processed?&lt;/p&gt;
&lt;p&gt;These situations require problem-solving.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Writing code is only one part of development. Understanding what can go wrong is another important part.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;That is why I prefer learning by building.&lt;/p&gt;
&lt;p&gt;Every project gives me an opportunity to experiment, make mistakes, debug problems, improve the code, and understand Python more deeply.&lt;/p&gt;
&lt;h3 id=&quot;from-small-scripts-to-useful-applications&quot;&gt;From Small Scripts to Useful Applications&lt;/h3&gt;
&lt;p&gt;Many useful applications start as very small ideas.&lt;/p&gt;
&lt;p&gt;A simple script might begin with:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;&quot;I don't want to do this manually anymore.&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;That idea can eventually become a complete application.&lt;/p&gt;
&lt;p&gt;For example:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Manual process → Python script → improved workflow → graphical interface → database → complete business tool&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The technology grows as the problem requires it.&lt;/p&gt;
&lt;p&gt;This approach helps avoid overengineering.&lt;/p&gt;
&lt;p&gt;Instead of starting with a complicated architecture, I prefer starting with the actual problem and building what is necessary.&lt;/p&gt;
&lt;h3 id=&quot;the-importance-of-understanding-the-business-problem&quot;&gt;The Importance of Understanding the Business Problem&lt;/h3&gt;
&lt;p&gt;Being a developer is not only about knowing programming languages.&lt;/p&gt;
&lt;p&gt;A developer also needs to understand why something needs to be built.&lt;/p&gt;
&lt;p&gt;Imagine someone asks for an application that automatically processes an Excel file.&lt;/p&gt;
&lt;p&gt;Before writing code, there are several questions worth asking.&lt;/p&gt;
&lt;p&gt;What information is inside the file?&lt;/p&gt;
&lt;p&gt;How often is the process performed?&lt;/p&gt;
&lt;p&gt;What mistakes happen during the manual process?&lt;/p&gt;
&lt;p&gt;What should happen when data is missing?&lt;/p&gt;
&lt;p&gt;What should the final output look like?&lt;/p&gt;
&lt;p&gt;Who will use the application?&lt;/p&gt;
&lt;p&gt;How much time should the automation save?&lt;/p&gt;
&lt;p&gt;These questions help turn a vague request into a practical solution.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The best technical solution is not always the most complicated one. It is the one that solves the actual problem effectively.&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id=&quot;what-i-am-continuing-to-learn&quot;&gt;What I Am Continuing to Learn&lt;/h3&gt;
&lt;p&gt;Python development is a continuous learning process.&lt;/p&gt;
&lt;p&gt;There is always another library, framework, technique, or concept to explore.&lt;/p&gt;
&lt;p&gt;I am continuously improving my understanding of Python and experimenting with different ways to build better tools.&lt;/p&gt;
&lt;p&gt;My interests include automation, data processing, desktop applications, APIs, databases, and practical business software.&lt;/p&gt;
&lt;p&gt;I also enjoy exploring how different technologies can work together.&lt;/p&gt;
&lt;p&gt;For me, learning becomes more meaningful when I can apply it to a real project.&lt;/p&gt;
&lt;p&gt;Instead of only asking, &quot;How does this technology work?&quot;&lt;/p&gt;
&lt;p&gt;I like asking:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&quot;What problem can I solve with it?&quot;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;That question changes the way I approach development.&lt;/p&gt;
&lt;h3 id=&quot;building-technology-around-people&quot;&gt;Building Technology Around People&lt;/h3&gt;
&lt;p&gt;Automation should not make work more complicated.&lt;/p&gt;
&lt;p&gt;It should make work easier.&lt;/p&gt;
&lt;p&gt;A successful tool does not necessarily need hundreds of features. Sometimes a small application that saves someone 30 minutes every day can be more valuable than a large system filled with features that nobody uses.&lt;/p&gt;
&lt;p&gt;That is the type of development I enjoy.&lt;/p&gt;
&lt;p&gt;I want to build tools that are practical, understandable, and useful.&lt;/p&gt;
&lt;p&gt;Whether it is a Python script that automates a repetitive task, a CustomTkinter application that simplifies a workflow, a SQLite database that organizes information, or a Pandas/OpenPyXL solution that processes large spreadsheets, the objective is the same:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Use technology to solve problems.&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id=&quot;looking-ahead&quot;&gt;Looking Ahead&lt;/h3&gt;
&lt;p&gt;My journey with Python is still continuing.&lt;/p&gt;
&lt;p&gt;There are many areas I want to explore further, improve my development practices, and apply what I learn to larger and more useful projects.&lt;/p&gt;
&lt;p&gt;I want to keep building software that connects programming with real business needs.&lt;/p&gt;
&lt;p&gt;The more I work on practical projects, the more I understand that development is not simply about writing code.&lt;/p&gt;
&lt;p&gt;It is about &lt;strong&gt;observing problems, understanding requirements, experimenting with solutions, learning from mistakes, and continuously improving the final result.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;That is what keeps Python development interesting for me.&lt;/p&gt;
&lt;p&gt;There will always be another repetitive task to automate, another workflow to improve, another problem to solve, and another idea to turn into a working application.&lt;/p&gt;
&lt;p&gt;And that is exactly what I enjoy about being a Python-focused developer.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Keep learning. Keep building. Keep solving real problems with code.&lt;/strong&gt;&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/30214/building-practical-python-tools-that-solve-real-world-problems</guid>
<pubDate>Thu, 08 Oct 2026 05:09:45 +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>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>Git Submodules: When and How</title>
<link>https://coderlegion.com/29811/git-submodules-when-and-how</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;#use-cases&quot;&gt;Use Cases&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;#submodules-subtrees-mono--or-multirepo&quot;&gt;Submodules, Subtrees, Mono- or Multirepo?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;#a-practical-setup-example&quot;&gt;A Practical Setup Example&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;#forkdev&quot;&gt;fork.dev&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;#terminal&quot;&gt;Terminal&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;#daily-workflow&quot;&gt;Daily Workflow&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;Git &lt;a rel=&quot;nofollow&quot; href=&quot;https://git-scm.com/docs/git-submodule&quot;&gt;submodules&lt;/a&gt; are a built-in Git mechanism for composing repositories at fixed commits. Unlike a monorepo, separate repositories, or worktrees, they let a root repository record exact revisions of independently owned submodules.&lt;/p&gt;
&lt;p&gt;The basic idea is simple: the root repository records a nested repository at an exact commit. The root repository stores the path and remote URL in &lt;code&gt;.gitmodules&lt;/code&gt;, and records the submodule commit as a &lt;code&gt;gitlink&lt;/code&gt;. A clone can later reconstruct the same combination of repositories.&lt;/p&gt;
&lt;h3 id=&quot;use-cases&quot;&gt;Use Cases&lt;/h3&gt;
&lt;p&gt;Briefly: use a submodule when a root repository must record an approved revision of code maintained elsewhere. Here are some useful applications of submodules:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Keep private documentation and deployment configuration in the root repository, with public source code in a submodule.&lt;/li&gt;
&lt;li&gt;Pin the exact revision of a shared library maintained and released separately.&lt;/li&gt;
&lt;li&gt;Record the approved revisions of independently released services or tools.&lt;/li&gt;
&lt;li&gt;Pin a reviewed commit from a maintained fork of an external project.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In product development, the root repository represents the product release context: its documentation, configuration, integration decisions, and approved component revisions. Each submodule represents a component with its own codebase, maintainers, and delivery cycle.&lt;/p&gt;
&lt;h3 id=&quot;submodules-subtrees-mono--or-multirepo-&quot;&gt;Submodules, Subtrees, Mono- or Multirepo?&lt;/h3&gt;
&lt;p&gt;Here are some key distinctions, briefly.&lt;/p&gt;
&lt;p&gt;A submodule keeps source independent while the root repository records the exact revision used by the product. It fits when public source and private product material must remain separate but form one reproducible product revision.&lt;/p&gt;
&lt;p&gt;A subtree copies external source into the root repository. Use it when the root repository should own that source.&lt;/p&gt;
&lt;p&gt;A monorepo works well when code, access rules, and release cadence belong together. &lt;a rel=&quot;nofollow&quot; href=&quot;https://nx.dev/&quot;&gt;Nx&lt;/a&gt; and similar monorepo tools allow for task execution, caching, project boundaries, and releases conveniently from the single root repository.&lt;/p&gt;
&lt;p&gt;A multirepo gives each component its own repository, ownership, and history, but does not by itself record which revisions belong together.&lt;/p&gt;
&lt;h3 id=&quot;a-practical-setup-example&quot;&gt;A Practical Setup Example&lt;/h3&gt;
&lt;p&gt;Consider a product with private documentation and deployment configuration, and public source code maintained in a separate repository.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Root repository:&lt;/strong&gt; private; contains private material and one or more submodule references.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Submodule:&lt;/strong&gt; public; contains source code and is its own source of truth.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;root/
├── .gitmodules
├── docs/                    # private specifications and operational material
├── deployment/              # private deployment configuration
└── submodule/               # separately owned source repository
    ├── README.md
    └── src/
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The root repository tracks &lt;code&gt;.gitmodules&lt;/code&gt; and the exact commit recorded at &lt;code&gt;submodule/&lt;/code&gt;. The files inside &lt;code&gt;submodule/&lt;/code&gt; belong to the submodule repository.&lt;/p&gt;
&lt;h4 id=&quot;fork-dev&quot;&gt;fork.dev&lt;/h4&gt;
&lt;p&gt;I use here the &lt;a rel=&quot;nofollow&quot; href=&quot;https://fork.dev/&quot;&gt;Fork&lt;/a&gt; Git client. The setup via terminal is in the next section.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Create the local folder structure:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;mkdir root
cd root
mkdir docs deployment submodule
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;In Fork, create a Git repository in &lt;code&gt;root/&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Open &lt;code&gt;submodule/&lt;/code&gt; in Fork and create a separate Git repository there. Add an initial file such as &lt;code&gt;README.md&lt;/code&gt;, commit it, and keep this repository independent from &lt;code&gt;root/&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Create two empty repositories in your Git hosting service, for example: private: &lt;code&gt;https://github.com/example/root.git&lt;/code&gt;, public: &lt;code&gt;https://github.com/example/submodule.git&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;In Fork, add &lt;code&gt;https://github.com/example/root.git&lt;/code&gt; as the &lt;code&gt;origin&lt;/code&gt; remote of the &lt;code&gt;root/&lt;/code&gt; repository.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Add &lt;code&gt;https://github.com/example/submodule.git&lt;/code&gt; as the &lt;code&gt;origin&lt;/code&gt; remote of the &lt;code&gt;submodule/&lt;/code&gt; repository, then push its initial commit.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Return to the &lt;code&gt;root/&lt;/code&gt; repository. In the sidebar, right-click &lt;strong&gt;Submodules&lt;/strong&gt; → &lt;strong&gt;Add New Submodule&lt;/strong&gt;. Enter repository URL: &lt;code&gt;https://github.com/example/submodule.git&lt;/code&gt;, local path: &lt;code&gt;submodule&lt;/code&gt;. Fork registers the existing local repository as the submodule.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Commit &lt;code&gt;.gitmodules&lt;/code&gt; and the new &lt;code&gt;submodule&lt;/code&gt; gitlink in the root repository, then push the root repository to its private &lt;code&gt;origin&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 id=&quot;terminal&quot;&gt;Terminal&lt;/h4&gt;
&lt;p&gt;The same setup from a terminal, creating both repositories locally first and then attaching their remotes:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# Create the local folder structure.
mkdir root
cd root
mkdir docs deployment submodule

# Initialize the private root repository.
git init -b main
git remote add origin https://github.com/example/root.git

# Initialize the public submodule repository.
cd submodule
git init -b main
echo &quot;# Submodule&quot; &amp;gt; README.md
git add README.md
git commit -m &quot;Initial commit&quot;
git remote add origin https://github.com/example/submodule.git
git push -u origin main

# Return to the root repository and register the existing local repository
# as a submodule.
cd ..
git submodule add --force https://github.com/example/submodule.git submodule

# Commit the root repository state.
git add docs deployment .gitmodules submodule
git commit -m &quot;Add submodule&quot;
git push -u origin main
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;daily-workflow&quot;&gt;Daily Workflow&lt;/h3&gt;
&lt;p&gt;Treat the submodule commit as a dependency lock. Change code in the submodule repository, then deliberately update the root repository's pointer.&lt;/p&gt;
&lt;p&gt;With that workflow, the repositories keep their separate ownership, history, and visibility, while the root repository still records the exact combination that belongs to the system.&lt;/p&gt;
&lt;p&gt;Example:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Doble-click on Submodules-&amp;gt;submodule repository to open its tab. Add its source files, stage them, commit them, and push them to the public &lt;code&gt;origin&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Return to the root repository. Add private files under &lt;code&gt;docs/&lt;/code&gt; and &lt;code&gt;deployment/&lt;/code&gt;. Stage &lt;code&gt;docs/&lt;/code&gt;, &lt;code&gt;deployment/&lt;/code&gt;, commit them, and push them to the private &lt;code&gt;origin&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;For private documentation or configuration changes, work in the root repository directly. For source changes, commit and push them in the submodule first, then commit the changed submodule reference in the root repository.&lt;/p&gt;
&lt;p&gt;When cloning later, use recursive initialization:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;git clone --recurse-submodules &amp;lt;root-repository-url&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For an existing clone, use:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;git submodule update --init --recursive
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;References: &lt;a rel=&quot;nofollow&quot; href=&quot;https://git-scm.com/docs/git-submodule&quot;&gt;Git submodule documentation&lt;/a&gt; and &lt;a rel=&quot;nofollow&quot; href=&quot;https://git-scm.com/book/en/v2/Git-Tools-Submodules&quot;&gt;Git submodules guide&lt;/a&gt;.&lt;/p&gt;
</description>
<category>Tutorials</category>
<guid isPermaLink="true">https://coderlegion.com/29811/git-submodules-when-and-how</guid>
<pubDate>Mon, 05 Oct 2026 06:47:37 +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>
<item>
<title>Is Open source dead?</title>
<link>https://coderlegion.com/29509/is-open-source-dead</link>
<description>&lt;p&gt;Google's 2027 verification mandate will break F-Droid. Let's coordinate a technical response.&lt;/p&gt;
&lt;p&gt;I recently got my app accepted on F-Droid. A week later, I got a cold email warning me about Google's developer verification rollout:&lt;/p&gt;
&lt;p&gt;Enforcement began September 30, 2026 in Brazil, Indonesia, Singapore, and Thailand. It expands globally in 2027 to all install sources, including F-Droid and direct APKs. It requires a $25 Play Console account plus a government photo ID. Organizations also need a D-U-N-S number, which takes about 28 days.&lt;/p&gt;
&lt;p&gt;F-Droid, EFF, FSFE, the Software Freedom Conservancy, and others have formed the Keep Android Open coalition and are formally opposing this. But opposition alone won't keep apps installable.&lt;/p&gt;
&lt;p&gt;The Real Problem&lt;/p&gt;
&lt;p&gt;The open-source mobile ecosystem is fragmented into pieces that don't cohere.&lt;/p&gt;
&lt;p&gt;On the OS layer, we have /e/OS, iodéOS, GrapheneOS, CalyxOS, and LineageOS. The gap is that there's no unified onboarding and each ROM is its own island.&lt;/p&gt;
&lt;p&gt;On attestation, we have UnifiedAttestation from Volla, Murena, iodé, and Apostrophy. The gap is that it's early stage and banks and digital IDs still don't work reliably.&lt;/p&gt;
&lt;p&gt;On Google API compatibility, we have microG and ReVanced. The gap is that Play Integrity checks still break many apps.&lt;/p&gt;
&lt;p&gt;On app distribution, we have F-Droid, App Lounge, and Aurora Store. The gap is that users don't know where to go and discovery is poor.&lt;/p&gt;
&lt;p&gt;On cloud and services, we have Murena Workspace and Volla Cloud. The gap is that there's no seamless migration path from Google.&lt;/p&gt;
&lt;p&gt;On AI, we have Odysseus by PewDiePie and Mozilla Thunderbolt. The gap is that local-first AI is new and there's no OS-level integration.&lt;/p&gt;
&lt;p&gt;The combined market share of all de-Googled phones is less than one per thousand.&lt;/p&gt;
&lt;p&gt;That's not a technology problem. That's an integration and UX problem.&lt;/p&gt;
&lt;p&gt;What I'm Proposing&lt;/p&gt;
&lt;p&gt;A coordinated compatibility and integration effort driven by app developers, people like us who ship on F-Droid and want our apps to work everywhere.&lt;/p&gt;
&lt;p&gt;Concrete asks:&lt;/p&gt;
&lt;p&gt;First, a compatibility testing matrix. A shared spreadsheet or repo where developers log things like &quot;My app works on /e/OS but breaks on GrapheneOS because of X.&quot; Aggregate this so ROM maintainers and microG devs can prioritize fixes.&lt;/p&gt;
&lt;p&gt;Second, UnifiedAttestation early adopters. The consortium is actively looking for first movers. If your app needs attestation for banking, auth, or secure login, integrate it now and report back. This is the single biggest blocker for mainstream adoption.&lt;/p&gt;
&lt;p&gt;Third, a microG bug bounty pool. Pool small contributions to fund fixes for the most common Play Services breakages. Even $500 targeted at one bug can unblock thousands of users.&lt;/p&gt;
&lt;p&gt;Fourth, an F-Droid onboarding guide. A single, canonical, up-to-date guide for normal users on how to leave Google without losing your apps. Written for non-technical people. Hosted somewhere permanent.&lt;/p&gt;
&lt;p&gt;Fifth, a regulatory pressure tracker. Track EU DMA enforcement, US antitrust remedies, and any other government action. Make it easy for developers to file comments or contact regulators.&lt;/p&gt;
&lt;p&gt;What I'm NOT Proposing&lt;/p&gt;
&lt;p&gt;Another ROM. Another app store. Another foundation. A &quot;Google killer.&quot;&lt;/p&gt;
&lt;p&gt;We don't need more pieces. We need the existing pieces to talk to each other.&lt;/p&gt;
&lt;p&gt;The Uncomfortable Question&lt;/p&gt;
&lt;p&gt;Do we build bridges by integrating with UnifiedAttestation and working within the system to make de-Googled phones viable, or do we fight the system by refusing attestation entirely, accepting that banking apps won't work, and targeting only the privacy-hardcore niche?&lt;/p&gt;
&lt;p&gt;GrapheneOS argues the Play Integrity API should be regulated out of existence rather than making another system where companies permit their own products while disallowing others.&lt;/p&gt;
&lt;p&gt;Both positions are defensible. But they lead to different products, different users, and different strategies. We need to pick one, or explicitly support both tracks.&lt;/p&gt;
&lt;p&gt;What I Want From This Thread&lt;/p&gt;
&lt;p&gt;If you're an F-Droid developer, what breaks when you test on de-Googled ROMs? What fixes have you found?&lt;/p&gt;
&lt;p&gt;If you're a ROM maintainer, what do you need from app developers?&lt;/p&gt;
&lt;p&gt;If you're a microG contributor, what's the highest-impact bug right now?&lt;/p&gt;
&lt;p&gt;If you're a user, what's the one thing that made you go back to stock Android?&lt;/p&gt;
&lt;p&gt;If you're none of the above but care, what's missing from this list?&lt;/p&gt;
&lt;p&gt;I'll compile responses into a public document and post it back here. If there's enough interest, I'll set up a repo and a Matrix or Discord space to coordinate.&lt;/p&gt;
&lt;p&gt;Let's stop waiting for someone else to fix this.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/29509/is-open-source-dead</guid>
<pubDate>Fri, 02 Oct 2026 11:49:03 +0000</pubDate>
</item>
<item>
<title>How I Use Automation and Coding to Solve Everyday Problems</title>
<link>https://coderlegion.com/29484/how-i-use-automation-and-coding-to-solve-everyday-problems</link>
<description>&lt;p&gt;As an automation engineer who enjoys writing code, exploring new technologies, and finding smarter ways to solve everyday problems, I believe automation is about more than just writing scripts. It is about understanding how things work, identifying repetitive tasks, and finding practical ways to make those tasks faster, easier, and more reliable.&lt;/p&gt;
&lt;p&gt;My interest in coding comes from curiosity. Whenever I see a task that requires the same steps to be repeated again and again, I start thinking about whether there is a way to automate it. Sometimes, a small script is enough to save time. Other times, the solution requires understanding a complete workflow, testing different approaches, and improving the code until everything works as expected.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;How I Approach Automation in My Work&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;When I work on an automation task, I usually begin by understanding the problem. Before writing any code, I try to identify which steps are repetitive, where mistakes might happen, and what the expected result should look like.&lt;/p&gt;
&lt;p&gt;For example, imagine a task that requires collecting information from multiple files, organizing the data, and preparing a report. Doing everything manually can take a lot of time, especially when the same process needs to be repeated daily.&lt;/p&gt;
&lt;p&gt;Instead of performing every step by hand, I look for opportunities to use scripts that can process the information, organize the results, and generate the output automatically.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;My general approach involves a few important steps:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Understand the problem: Identify the task, its requirements, and the result I want to achieve.&lt;/p&gt;
&lt;p&gt;Plan the workflow: Break the process into smaller steps that are easier to understand and automate.&lt;/p&gt;
&lt;p&gt;Write the code: Develop a script or program that handles the repetitive work.&lt;/p&gt;
&lt;p&gt;Test the solution: Check whether the code produces the expected results and handles different situations.&lt;/p&gt;
&lt;p&gt;Debug and improve: Find errors, fix problems, and make the solution more efficient and reliable.&lt;/p&gt;
&lt;p&gt;This approach helps me focus on solving the actual problem instead of simply writing code without a clear purpose.&lt;/p&gt;
&lt;p&gt;Turning Repetitive Tasks into Automated Workflows&lt;/p&gt;
&lt;p&gt;One of the things I enjoy most about automation is seeing how a small piece of code can simplify a complicated process.&lt;/p&gt;
&lt;p&gt;For example, automation can help with organizing files, processing spreadsheets, checking data, generating reports, sending notifications, or connecting different applications. These tasks may seem simple individually, but performing them manually every day can consume valuable time.&lt;/p&gt;
&lt;p&gt;Using scripts and suitable tools, many of these processes can be handled automatically.&lt;/p&gt;
&lt;p&gt;Python is particularly useful for this type of work because it offers libraries for file handling, data processing, APIs, and many other common automation tasks. Depending on the requirements, I can explore different tools and programming approaches to find a solution that fits the problem.&lt;/p&gt;
&lt;p&gt;However, I also understand that automation is not always about removing every human step. Some tasks still require human review, especially when decisions depend on context or when the results need to be verified. My goal is to automate the repetitive parts while keeping the overall process understandable and dependable.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The Challenges I Face While Coding&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Automation does not always work perfectly on the first attempt. Sometimes, a script fails because the input data has an unexpected format. In other cases, an API returns an error, a website changes its structure, or a process behaves differently than expected.&lt;/p&gt;
&lt;p&gt;These situations are a normal part of software development.&lt;/p&gt;
&lt;p&gt;When something goes wrong, I try to understand the reason instead of simply changing the code until the error disappears. I check error messages, examine the data, test individual functions, and make changes step by step.&lt;/p&gt;
&lt;p&gt;I also pay attention to edge cases. A script that works with one example may fail when it encounters an empty file, missing values, duplicate records, or an unexpected response.&lt;/p&gt;
&lt;p&gt;Learning to handle these situations is an important part of building reliable automation.&lt;/p&gt;
&lt;p&gt;For me, debugging is not just about fixing errors. It is an opportunity to understand the system better and improve the quality of my work.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Learning New Technologies Through Practical Projects&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Technology continues to evolve, and there is always something new to explore. I enjoy experimenting with programming tools, automation frameworks, APIs, and other technologies that can help solve practical problems.&lt;/p&gt;
&lt;p&gt;Rather than learning everything only through theory, I prefer applying new concepts to small projects. Building something gives me a clearer understanding of how the different parts work together.&lt;/p&gt;
&lt;p&gt;A simple project might involve automating a spreadsheet, creating a script to organize files, connecting two applications through an API, or developing a tool that reduces manual data entry.&lt;/p&gt;
&lt;p&gt;Even when a project does not work as expected, the experience helps me learn something valuable.&lt;/p&gt;
&lt;p&gt;I believe developers grow through continuous practice. Every project introduces new challenges, and every challenge creates an opportunity to improve problem-solving skills.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;How I Make Automation More Reliable&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Writing a script that works once is one thing. Creating an automation process that continues to work correctly is another.&lt;/p&gt;
&lt;p&gt;I try to think beyond the initial result and consider what might happen when the script encounters an error, receives unexpected input, or runs multiple times.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Some important practices include:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Using clear and maintainable code.&lt;/p&gt;
&lt;p&gt;Validating input data before processing it.&lt;/p&gt;
&lt;p&gt;Handling exceptions and recording useful error messages.&lt;/p&gt;
&lt;p&gt;Testing the workflow with different inputs.&lt;/p&gt;
&lt;p&gt;Avoiding unnecessary complexity.&lt;/p&gt;
&lt;p&gt;Reviewing the results to make sure the automation is doing the intended job.&lt;/p&gt;
&lt;p&gt;These practices help make automation easier to understand, maintain, and improve over time.&lt;/p&gt;
&lt;p&gt;I also believe that a useful automation solution should be practical. A complicated system is not always better than a simple script. The right solution depends on the problem, the available tools, and the needs of the people using it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;My Experience and What I Want to Learn Next&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;My interest in automation and coding continues to motivate me to experiment, build small projects, and discover more efficient ways to work. I enjoy the process of taking an idea, breaking it down into manageable steps, and turning it into something useful.&lt;/p&gt;
&lt;p&gt;I also recognize that learning never really stops in technology. There are always new tools to explore, better coding practices to discover, and different approaches to solving familiar problems.&lt;/p&gt;
&lt;p&gt;Going forward, I want to continue improving my programming skills, exploring modern automation techniques, and building projects that address real-world challenges. I am particularly interested in understanding how different technologies can work together to create smoother workflows and reduce unnecessary manual effort.&lt;/p&gt;
&lt;p&gt;For me, the most rewarding part of coding is not simply seeing a script run successfully. It is knowing that the solution serves a purpose, saves time, or makes someone's work a little easier.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Final Thoughts&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Being an automation engineer means approaching everyday tasks with curiosity and looking for opportunities to improve the way things work. It requires patience, logical thinking, experimentation, and a willingness to learn from mistakes.&lt;/p&gt;
&lt;p&gt;I do not believe every problem needs a complicated technical solution. Sometimes, the smallest improvement can make the biggest practical difference.&lt;/p&gt;
&lt;p&gt;My goal is to keep learning, keep experimenting, and keep turning ideas into useful solutions through code and automation.&lt;/p&gt;
&lt;p&gt;I would also love to hear from other developers and automation engineers: What is one repetitive task you have automated with code, and how did it change the way you work?&lt;/p&gt;
&lt;p&gt;Let's share ideas, learn from each other's experiences, and discover new ways to make everyday work more efficient.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/29484/how-i-use-automation-and-coding-to-solve-everyday-problems</guid>
<pubDate>Fri, 02 Oct 2026 04:09:31 +0000</pubDate>
</item>
<item>
<title>SMS AI Engine</title>
<link>https://coderlegion.com/29405/sms-ai-engine</link>
<description>&lt;p&gt;The Story Behind &lt;a rel=&quot;nofollow&quot; href=&quot;http://sms-ai-delta.lovable.app&quot;&gt;SMS AI&lt;/a&gt;&lt;br&gt;
A lot of general AI frontends have notable latency issues when streaming code blocks or rendering markdown. I wanted to see if I could optimize the processing structure to bring the time-to-first-token down under 100ms.&lt;br&gt;
SMS AI is a lightweight, edge-delivered multimodal workspace built to handle text generation, code refactoring, and document compilation without the usual lag.&lt;br&gt;
Engineering &amp;amp; Key Features&lt;br&gt;
• Low-latency streaming: Custom routing layers keep the time-to-first-token under &amp;lt; 100ms.&lt;br&gt;
• Code execution &amp;amp; syntax rendering: Generates, refactors, and explains code arrays in standard languages (C++, Python, JS, etc.) with clean syntax highlighting.&lt;br&gt;
• RAG &amp;amp; Context Parsing: Supports direct uploads of heavy files (.pdf, .docx, .txt) to isolate data strings and extract instant contextual summaries.&lt;br&gt;
• Live Web-Aware Search: Features a web-retrieval module that crawls live data layers and appends exact source citations.&lt;br&gt;
• Platform Stability: Built on redundant infrastructure maintaining a stable 99.99% uptime profile.&lt;br&gt;
System Architecture Preview&lt;br&gt;
Current Infrastructure Allocation&lt;br&gt;
The system is open for production testing:&lt;br&gt;
• Free Plan ($0/mo): 10 daily streaming chat messages. No account setup or registration required to test the core logic.&lt;br&gt;
• Pro Plan ($20/mo): Unlimited processing queues, priority model speeds, and raw CSV data export formats.&lt;br&gt;
The project was fully deployed by ~M_Sameer. If you spot any edge-case bugs, latency drops, or parsing issues, drop a comment below. I am actively tuning the performance profiles.&lt;br&gt;
Thanks for taking a look.&lt;/p&gt;
</description>
<category>Launches</category>
<guid isPermaLink="true">https://coderlegion.com/29405/sms-ai-engine</guid>
<pubDate>Thu, 01 Oct 2026 12:17:18 +0000</pubDate>
</item>
<item>
<title>How I Use Python in My Development Workflow</title>
<link>https://coderlegion.com/29396/how-i-use-python-in-my-development-workflow</link>
<description>&lt;p&gt;As a Full Stack Developer, I believe learning new technologies helps me solve problems in different ways. Along with C#, .NET Core, SQL Server, and Angular, Python is another useful language for backend development, automation, and simplifying repetitive tasks.&lt;/p&gt;
&lt;p&gt;I use Python to explore ways to automate everyday work, process data, build scripts, and connect different tools through APIs. Sometimes, a small Python script can save time by handling a task that would otherwise require repetitive manual effort.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;What I Find Interesting About Python&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Python has a readable syntax, a large ecosystem of libraries, and many practical applications. It can be used for web development, data processing, testing, and automation. I also find it useful when experimenting with new ideas or creating small tools to solve specific problems.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;How Python Fits into My Development Work&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;When working on web applications, I think about how different technologies can work together. For example, a .NET application can handle core business logic while a Python script processes data or automates a separate workflow. REST APIs can help connect these components when needed.&lt;/p&gt;
&lt;p&gt;Of course, choosing the right technology depends on the project requirements. I don't believe every problem needs a new language or framework. Sometimes, improving the existing code is the simplest solution.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;My Learning Mindset&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;For me, software development is about more than mastering one programming language. It's about understanding problems, exploring possible solutions, and choosing tools that make the work more efficient and maintainable.&lt;/p&gt;
&lt;p&gt;I'm interested in learning how other developers use Python in their everyday workflows.&lt;/p&gt;
&lt;p&gt;How do you use Python in your projects? Do you mainly use it for automation, backend development, data processing, or something else?&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/29396/how-i-use-python-in-my-development-workflow</guid>
<pubDate>Thu, 01 Oct 2026 11:17:07 +0000</pubDate>
</item>
<item>
<title>The Harness Converges. Rules Decide What It Converges On.</title>
<link>https://coderlegion.com/29131/the-harness-converges-rules-decide-what-it-converges-on</link>
<description>&lt;p&gt;You point the agent at the repo. It plans. It writes. It runs the tests, reads the failures, fixes them, and hands you back something that works.&lt;/p&gt;
&lt;p&gt;You read the diff. It is fine.&lt;/p&gt;
&lt;p&gt;It is also not how anyone on the team would have written it.&lt;/p&gt;
&lt;p&gt;That gap is the thing I keep coming back to, and I am aware that saying so in late 2026 sounds like relitigating an argument everybody finished having. Rule files were a 2025 conversation. Writing about them now feels like showing up to a meeting a year late with slides nobody asked for.&lt;/p&gt;
&lt;h3 id=&quot;it-was-not-settled-it-went-out-of-fashion-&quot;&gt;It was not settled. It went out of fashion.&lt;/h3&gt;
&lt;p&gt;The harnesses got genuinely good in that year. Planning loops. Verification passes. Sub-agents that go off and come back. Context windows large enough that the old economy of what to include stopped feeling urgent.&lt;/p&gt;
&lt;p&gt;All of that carries an implicit promise. A capable enough agent reads your project and infers your project. Pointing it at a rules directory starts to look like manual labour left over from a weaker era.&lt;/p&gt;
&lt;p&gt;So attention moved to the interesting layer. That is a reasonable thing for attention to do.&lt;/p&gt;
&lt;p&gt;But nobody disproved the groundwork. It just stopped being the part worth talking about.&lt;/p&gt;
&lt;h3 id=&quot;a-harness-closes-the-loop-it-does-not-supply-the-target-&quot;&gt;A harness closes the loop. It does not supply the target.&lt;/h3&gt;
&lt;p&gt;Slop is not the agent being incompetent. That is the easy explanation, and it is the wrong one.&lt;/p&gt;
&lt;p&gt;Slop is the agent converging, competently, on the average of everything it has ever read, because nothing in the repo told it which of the plausible answers was the one you wanted.&lt;/p&gt;
&lt;p&gt;A verification loop tests output against a target. A planning loop sequences work toward a target. Neither of them produces the target. If it does not come from you, it comes from the distribution, and the distribution's opinion about your error handling is the median of every tutorial ever written.&lt;/p&gt;
&lt;p&gt;Which means every improvement to the harness raises the cost of skipping the groundwork rather than lowering it. A weak harness that guesses wrong wanders, and you notice. A strong harness that guesses wrong arrives somewhere quickly, confidently, with passing tests wrapped around it.&lt;/p&gt;
&lt;p&gt;Seneca got there about two thousand years early.&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Our plans miscarry because they have no aim. When a man does not know what harbour he is making for, no wind is the right wind.&lt;/p&gt;
&lt;p&gt;Seneca, &lt;em&gt;Letters to Lucilius&lt;/em&gt;, Letter 71 (tr. Gummere)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The harness is the wind. It keeps getting stronger. The harbour is still yours to name.&lt;/p&gt;
&lt;h3 id=&quot;the-decision-is-not-what-to-write-it-is-when-it-loads-&quot;&gt;The decision is not what to write. It is when it loads.&lt;/h3&gt;
&lt;p&gt;I have twenty-two rules across the two generators I maintain. Thirteen for the frontend one, nine for the backend one.&lt;/p&gt;
&lt;p&gt;The content of each rule is the obvious part. The part that actually shapes the output is when each one enters the model's context. That decision has exactly three shapes:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;export type CursorRuleMeta =
  | { alwaysApply: true }
  | { alwaysApply: false }
  | { globs: string[] };
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Three states is not a limitation I worked around. It is the whole design surface, and every rule I wrote had to land in one of them.&lt;/p&gt;
&lt;p&gt;Architecture and security are always on. Anything an agent changes has to be checkable against structural intent and against known vulnerability shapes, and the cost of either being absent once is higher than the context they burn on every single turn.&lt;/p&gt;
&lt;p&gt;Design and styling are not like that. They are irrelevant until you touch a component, at which point they matter completely. So they load on the files they govern and stay quiet otherwise.&lt;/p&gt;
&lt;p&gt;Git conventions and pre-commit behaviour are the third kind. Following them is not mandatory on every turn. Paying for them on every turn is waste. They sit off by default and come in when the work is actually about commits.&lt;/p&gt;
&lt;p&gt;None of those were hard calls. Once the question is asked, the answer is usually obvious. The failure is not answering it badly. The failure is never asking it, and letting a single instructions file load every rule on every turn by default.&lt;/p&gt;
&lt;h3 id=&quot;every-rule-went-through-the-same-three-questions&quot;&gt;Every rule went through the same three questions&lt;/h3&gt;
&lt;p&gt;Does a wrong answer here cost more than the context it burns on every turn? Then it is always on.&lt;/p&gt;
&lt;p&gt;Is it only true when the work touches particular files? Then it loads on those files and nowhere else.&lt;/p&gt;
&lt;p&gt;Is it advisory rather than mandatory? Then it stays off until the work is about that thing.&lt;/p&gt;
&lt;p&gt;Seven of the thirteen frontend rules, in file order:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;export const RULE_CURSOR_METADATA: Record&amp;lt;string, CursorRuleMeta&amp;gt; = {
  architecture: { alwaysApply: true },
  components: {
    globs: ['**/*.tsx', '**/*.vue', '**/*.svelte', 'src/components/**/*'],
  },
  'errors-logging': { alwaysApply: true },
  security: { alwaysApply: true },
  environment: {
    globs: ['.env*', 'src/config/**/*', 'vite.config.*', 'next.config.*'],
  },
  'git-conventions': { alwaysApply: false },
  'pre-commit': { alwaysApply: false },
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Across the full thirteen it comes out as three always on, eight scoped, two off. That split is the whole output of those three questions, and it is the part that never shows up in a post about writing better prompts.&lt;/p&gt;
&lt;h3 id=&quot;a-rule-that-cannot-be-ported-was-never-a-rule&quot;&gt;A rule that cannot be ported was never a rule&lt;/h3&gt;
&lt;p&gt;I did not write those rules for one editor. I treated the rule set as a small DSL and built adapters that emit it to five different tools, because teams do not converge on one tool.&lt;/p&gt;
&lt;p&gt;Designing for five targets from the start forced a discipline I would recommend even to someone who only ever uses one.&lt;/p&gt;
&lt;p&gt;A neutral rule survives translation. It lands in every target and still means the same thing. A rule that is too specific does not, and the reason is always the same. It is not encoding intent. It is encoding mechanism. A file path. A config shape. A framework's current API. Something true about the setup rather than true about the work.&lt;/p&gt;
&lt;p&gt;Mechanism is the part that changes. It changes when you upgrade the framework, when you move a directory, when the vendor renames a field. Intent survives all of that, which is why intent is the thing worth writing down and mechanism is the thing that quietly rots in a rules folder nobody has opened since it was written.&lt;/p&gt;
&lt;p&gt;So there is a test here you can run without my tool and without my adapters.&lt;/p&gt;
&lt;p&gt;Take a rule and move it to a different agent. If it stops making sense, you wrote down your setup instead of your intent. Rewrite it one level up and it ports.&lt;/p&gt;
&lt;p&gt;It also gives the harness something to converge on. Mechanism tells the agent how your project is wired. Intent tells it what you are trying to get right, and that is the only one of the two a verification loop can aim at.&lt;/p&gt;
&lt;h3 id=&quot;this-is-evidence-it-is-not-proof-&quot;&gt;This is evidence. It is not proof.&lt;/h3&gt;
&lt;p&gt;I have not measured a quality delta. There is no benchmark where the same repo with and without rules produced a countable difference, and I am not going to construct one after the fact to make this read stronger.&lt;/p&gt;
&lt;p&gt;What I have is a design argument and a shipped implementation of it. Twenty-two rules, three activation states, five targets, and the reasoning behind every classification sitting in a file anyone can open.&lt;/p&gt;
&lt;p&gt;That difference matters, and I would rather name it than let the code fences imply something they do not support.&lt;/p&gt;
&lt;h3 id=&quot;boring-is-not-the-same-as-optional&quot;&gt;Boring is not the same as optional&lt;/h3&gt;
&lt;p&gt;The harness is going to keep getting better. The planning will get sharper, the verification will get tighter, and the layer I am describing here will keep looking like the unglamorous part a sufficiently good agent should not need.&lt;/p&gt;
&lt;p&gt;It will not stop needing it. Faster convergence toward an unspecified target is just a faster way to arrive somewhere you did not choose.&lt;/p&gt;
&lt;p&gt;I do not think the interesting question is whether rules still matter. The better question is why we keep treating the part that supplies intent as the part that will eventually automate itself.&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Awaiting your feedback!&lt;br&gt;
&lt;a rel=&quot;nofollow&quot; href=&quot;https://jeelvankhede.github.io/frontend-ai-starter-recipes/&quot;&gt;&lt;strong&gt;F&lt;/strong&gt;rontend &lt;strong&gt;A&lt;/strong&gt;i starter &lt;strong&gt;RE&lt;/strong&gt;cipes&lt;/a&gt;&lt;br&gt;
&lt;a rel=&quot;nofollow&quot; href=&quot;https://jeelvankhede.github.io/backend-ai-starter-recipes/&quot;&gt;&lt;strong&gt;B&lt;/strong&gt;ackend &lt;strong&gt;A&lt;/strong&gt;i starter &lt;strong&gt;RE&lt;/strong&gt;cipes&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/29131/the-harness-converges-rules-decide-what-it-converges-on</guid>
<pubDate>Tue, 29 Sep 2026 17:25:27 +0000</pubDate>
</item>
<item>
<title>Software Development Is More Than Just Writing Code</title>
<link>https://coderlegion.com/29054/software-development-is-more-than-just-writing-code</link>
<description>&lt;p&gt;Being a software developer is not just about writing code or learning new programming languages. For me, it's about solving problems, exploring new ideas, and finding practical ways to turn those ideas into something useful.&lt;/p&gt;
&lt;p&gt;Every project brings a different challenge. Sometimes the solution works exactly as expected, while other times a small mistake can take hours to find. These moments are a normal part of development, and they often teach me more than simply following a tutorial or reading documentation.&lt;/p&gt;
&lt;p&gt;I believe the real growth of a developer comes from experimenting, making mistakes, and learning how to solve problems independently.&lt;/p&gt;
&lt;p&gt;Learning Through Real Projects&lt;/p&gt;
&lt;p&gt;One thing I enjoy most about software development is that there is always something new to learn. Technology changes quickly, development tools continue to improve, and new approaches make it possible to solve old problems in different ways.&lt;/p&gt;
&lt;p&gt;Working on projects helps me understand how things work beyond the surface. Writing code is only one part of the process. Understanding requirements, planning a solution, debugging unexpected behavior, and improving existing code are equally important.&lt;/p&gt;
&lt;p&gt;I've learned that a solution doesn't have to be complicated to be effective. Sometimes, a simple and well-organized approach is much easier to maintain than a complex implementation.&lt;/p&gt;
&lt;p&gt;The Challenges That Make Developers Better&lt;/p&gt;
&lt;p&gt;Every developer encounters bugs, confusing errors, unexpected results, and situations where the first solution doesn't work.&lt;/p&gt;
&lt;p&gt;Instead of seeing these moments as failures, I see them as opportunities to understand the problem more deeply. Debugging teaches patience, problem-solving develops logical thinking, and reviewing old code helps identify better ways to approach future projects.&lt;/p&gt;
&lt;p&gt;Good developers don't know every answer. They keep learning, ask questions, and know how to work toward finding the answer.&lt;/p&gt;
&lt;p&gt;I also believe that writing code that works is only the beginning. Writing code that other developers can understand, maintain, and improve is an important part of building reliable software.&lt;/p&gt;
&lt;p&gt;Sharing Knowledge and Learning Together&lt;/p&gt;
&lt;p&gt;Software development is a field where learning from others can make a real difference. A problem that takes hours to solve alone might become much easier after reading another developer's experience or discovering a different approach.&lt;/p&gt;
&lt;p&gt;That's one reason I enjoy connecting with other developers, sharing useful tips, discussing technical challenges, and learning from different perspectives.&lt;/p&gt;
&lt;p&gt;Everyone has a different journey. Some developers enjoy backend architecture, others focus on frontend experiences, and many explore databases, automation, cloud technologies, or artificial intelligence. Each perspective brings something valuable to the community.&lt;/p&gt;
&lt;p&gt;Looking Ahead&lt;/p&gt;
&lt;p&gt;My goal is to keep improving my skills, build useful projects, explore new technologies, and become better at solving real-world problems.&lt;/p&gt;
&lt;p&gt;I don't believe learning ever truly ends in software development. There will always be a new tool to explore, an existing skill to improve, or a different way to approach a challenge.&lt;/p&gt;
&lt;p&gt;For me, progress isn't only about how much code I write. It's also about how much I understand, how effectively I solve problems, and how much value I can create through the things I build.&lt;/p&gt;
&lt;p&gt;Keep learning, keep experimenting, and keep building. Every small improvement is a step forward.&lt;/p&gt;
&lt;p&gt;I'd love to hear from other developers: What's one important lesson you've learned from a project that didn't go as planned?&lt;/p&gt;
&lt;p&gt;Let's share our experiences and learn from each other.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/29054/software-development-is-more-than-just-writing-code</guid>
<pubDate>Tue, 29 Sep 2026 12:04:13 +0000</pubDate>
</item>
<item>
<title>What Building a Social Platform Taught Me About Software Discovery</title>
<link>https://coderlegion.com/28957/what-building-a-social-platform-taught-me-about-software-discovery</link>
<description>&lt;p&gt;When I started building BitVoices, I knew why I wanted it to exist before I knew exactly what it needed to be. The idea grew naturally out of the mission behind NotableBIT: amplifying Black excellence in tech. I wanted Black technologists to have a place where we could be visible, share what we were building, celebrate achievements, publish articles, make connections and see more of ourselves represented in technology.&lt;/p&gt;
&lt;p&gt;At the time, my mental model for an online community was pretty straightforward. A social platform seemed like the natural way to create that space, so the first version of BitVoices took shape around profiles, posts, comments, reactions, follows, notifications, videos, news and a feed. It looked like a social network because that's what I understood an online community platform to be at the time. What I didn't understand yet was that building the software would become part of discovering what the software was supposed to be.&lt;/p&gt;
&lt;h3 id=&quot;when-the-software-doesn-t-feel-right&quot;&gt;When the software doesn't feel right&lt;/h3&gt;
&lt;p&gt;The more I built BitVoices, the more it started to feel like everything else. One of the things that really started bothering me was the news feed. I found myself spending time every day curating articles about Black people in technology so there would be fresh content on the platform, but almost nobody was reading them and, realistically, they probably weren't going to. I could continue finding more content and adding more features, but neither answered the question that had started bothering me: &lt;strong&gt;What is BitVoices actually supposed to be?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;I started asking more fundamental questions. Why am I building this? What am I expecting builders to actually do when they get here? What should they get from being here? What should happen because BitVoices exists that wouldn't otherwise happen?&lt;/p&gt;
&lt;p&gt;I spoke with people about it. Low Key, my ChatGPT agent, and I had many conversations about it. I also started looking at other modern platforms to understand what they were doing and how they were thinking about community. I wasn't necessarily looking for features to copy. I was trying to understand what fit what I was building and, probably more importantly, what didn't.&lt;/p&gt;
&lt;p&gt;As I got clearer about the purpose, I started seeing possibilities that weren't obvious when I was thinking primarily about building a social network. The question was becoming less about what content should be in the feed and more about what a builder should be able to accomplish by being part of BitVoices. That distinction changed the software.&lt;/p&gt;
&lt;h3 id=&quot;the-product-changed-as-my-understanding-changed&quot;&gt;The product changed as my understanding changed&lt;/h3&gt;
&lt;p&gt;Builder HQ began shifting the center of BitVoices away from the feed and toward the builder. I had started moving away from the &quot;feed-first&quot; assumptions behind the original platform and thinking about a different set of questions. Instead of primarily thinking about what someone wants to post or consume, I was thinking about what they're building, what they need, what they can offer another builder, what needs their attention and who in the network might be able to help them.&lt;/p&gt;
&lt;p&gt;I was also thinking more deeply about what community could actually mean in this context. What could builders offer each other beyond reading and reacting to one another's posts? What relationships could form because two people found each other here? Could builders with complementary skills decide to work together on something? Could a team form? Could a company eventually come out of a relationship that started because two builders met on BitVoices? I didn't know what would happen, and I still don't, but those possibilities were much closer to what I wanted the platform to enable.&lt;/p&gt;
&lt;p&gt;The original social features didn't need to disappear. Posts, comments, reactions, articles and connections still serve a purpose. What changed was my understanding of their role. They were no longer the organizing idea behind the product. The network itself, and the relationships that could develop within it, had become much more important.&lt;/p&gt;
&lt;p&gt;That also changed how I thought about Hubs. Builders don't only need a network of people. Sometimes they need a place to gather around a particular interest, technology, business model or geography. That thinking eventually led to Hubs such as AI Builders, SaaS Builders and Tulsa Builders.&lt;/p&gt;
&lt;p&gt;Even there, the discovery continued. The interesting problem wasn't simply how to add groups to the platform. It was understanding what gives builders a reason to participate once they're there. That has led me to think about things like prompts and Hub Check-ins that can create rhythm, accountability and collaboration without turning every community into a formal program.&lt;/p&gt;
&lt;p&gt;Somewhere through this process I arrived at a phrase that now influences how I think about BitVoices: &lt;strong&gt;Community is culture, not features.&lt;/strong&gt; That's different from saying features don't matter. Obviously they do. I'm still building software. But I can build profiles, feeds, messaging, reactions, groups and every other feature we associate with online communities and still not create a community worth participating in. The features have to support the culture, relationships and purpose of the community rather than becoming substitutes for them.&lt;/p&gt;
&lt;h3 id=&quot;i-was-listening-to-the-software&quot;&gt;I was listening to the software&lt;/h3&gt;
&lt;p&gt;Months later, I finally found language for what had been happening. I had been listening to the software. I don't mean that in some mystical sense where the application suddenly developed an opinion about its product roadmap. 😂 I mean that the process of building, operating and observing the software was producing information that I didn't have when I started.&lt;/p&gt;
&lt;p&gt;The news nobody was reading was information. My frustration with spending time curating it was information. Conversations with people were information. Watching how the platform was being used was information. Even the fact that I kept returning to the same questions about BitVoices' purpose was information.&lt;/p&gt;
&lt;p&gt;Building in public amplified that process because BitVoices was publicly live in both forms. People saw the earlier feed-centered social platform, and they've been able to watch it change as my understanding of the product has changed. I've been talking about what I'm building while simultaneously building and operating it. I didn't disappear for a year, figure everything out and then unveil the answer. I was discovering BitVoices while BitVoices was already alive. That's what I've started thinking about as &lt;strong&gt;software discovery&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;We usually put discovery before implementation. We gather requirements, understand the problem, make architectural and product decisions, and then build the software. I still believe that work matters. Software discovery isn't an argument for skipping it and figuring everything out while you're coding. It's an acknowledgement that discovery doesn't necessarily stop when construction begins.&lt;/p&gt;
&lt;p&gt;Once an idea becomes working software, you can interact with assumptions that previously existed only in your head, a document or a mockup. You can operate the thing. Other people can use it. You can see where you're spending effort that isn't producing value. You can notice where the software technically works but somehow doesn't fit the purpose you thought you understood.&lt;/p&gt;
&lt;p&gt;The implementation itself becomes another source of information. That's what happened with BitVoices. I didn't discover that the original social features were bad ideas. BitVoices still has a feed, profiles, posts, articles, comments, reactions and many of the things I started building in the beginning. I discovered that I had mistaken some of the mechanisms of an online community for the community itself.&lt;/p&gt;
&lt;p&gt;I'm still discovering what BitVoices is becoming. The difference is that now I recognize discovery as part of building it. Requirements, conversations and research can tell me a lot about what software should be, but sometimes I also need to build enough of it to hear what the software has to say.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/28957/what-building-a-social-platform-taught-me-about-software-discovery</guid>
<pubDate>Mon, 28 Sep 2026 16:01:20 +0000</pubDate>
</item>
<item>
<title>The Part of Shipping Software That Doesn't Show Up in a Portfolio Screenshot</title>
<link>https://coderlegion.com/28813/the-part-of-shipping-software-that-doesnt-show-up-in-a-portfolio-screenshot</link>
<description>&lt;p&gt;A few weeks into building a live finance dashboard for a client, I learned something that had nothing to do with writing code.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;What I built&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The brief was simple to state and harder to deliver: a dashboard connected directly to the client's Excel workbook — no exports, no &quot;send me the latest file,&quot; no waiting on me every time a number changed. Edit the sheet, save it, and the dashboard reflects it within seconds.&lt;/p&gt;
&lt;p&gt;Under the hood, it's a &lt;strong&gt;Python (Flask)&lt;/strong&gt; backend that reads straight from the workbook on every request, paired with a frontend that polls for changes and re-renders automatically. On top of that, an interactive map view lets the client click into any project location and see its P&amp;amp;L, purchases, and profitability pulled live from the sheet — no manual recalculation, no stale numbers.&lt;/p&gt;
&lt;p&gt;The last step was packaging it so the client — who isn't technical — could just double-click one file and have it work. &lt;strong&gt;No Python install, no terminal, no code in sight.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Where it actually got hard&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Technically, everything worked. Locally, on my machine, the dashboard ran exactly as intended.&lt;/p&gt;
&lt;p&gt;Then I packaged it into a standalone &lt;strong&gt;.exe&lt;/strong&gt; to hand off — and Windows flagged it as potentially unsafe before it ever reached the client.&lt;/p&gt;
&lt;p&gt;My first instinct was to assume something was wrong with the code. It wasn't. The actual cause was &lt;strong&gt;Smart App Control — a Windows 11 feature&lt;/strong&gt; that's cautious by default about any unsigned, unrecognized executable, regardless of what's inside it. A freshly built app with no signing history and no reputation looks suspicious to it, even when it's completely benign.&lt;/p&gt;
&lt;p&gt;Knowing why it happened didn't make it &lt;em&gt;&quot;shippable,&quot;&lt;/em&gt; though. I couldn't just tell the client &lt;em&gt;&quot;ignore the warning.&quot;&lt;/em&gt; I submitted the file to Microsoft for analysis through their official review process, and the status sat at Pending for a stretch — there's no way to force that along, it just takes as long as it takes. When the analyst's response came back, it confirmed the file had been incorrectly blocked and had been remediated so it would no longer trigger the warning.&lt;/p&gt;
&lt;p&gt;Only then was it actually ready to hand to someone else's computer.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The takeaway&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;If I ship more builds of this app over time, the same thing could happen again — a new build means a new file hash, and a new file hash means zero reputation, all over again. The durable fix, I learned, is code-signing: a certificate from a trusted authority that lets Windows (and Microsoft's own analysts) recognize the publisher quickly instead of treating every new build as an unknown.&lt;/p&gt;
&lt;p&gt;But the bigger lesson wasn't technical. It was this: building something and shipping something to a real, non-technical user are two different bars. The first one is the part that shows up in a portfolio screenshot. The second one — the packaging, the trust signals, the &lt;em&gt;&quot;will this actually work on someone else's machine without me standing next to them&quot;&lt;/em&gt; — is invisible until it isn't, and it's just as much a part of the job.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/28813/the-part-of-shipping-software-that-doesnt-show-up-in-a-portfolio-screenshot</guid>
<pubDate>Sun, 27 Sep 2026 16:15:09 +0000</pubDate>
</item>
<item>
<title>How to Improve Visibility and Control with E-Indenting Logistics Software</title>
<link>https://coderlegion.com/28312/how-to-improve-visibility-and-control-with-e-indenting-logistics-software</link>
<description>&lt;p&gt;Raising a transport indent used to mean picking up the phone- calling three or four transporters, saying the same load details again and again, waiting for callbacks and writing numbers on a notepad somewhere. By the time the truck's booked, half your morning's gone and you're still not sure if the rate was fair.&lt;/p&gt;
&lt;p&gt;This is still how a lot of Indian manufacturers handle freight today- phone calls, whatsApp messages scattered across chats, no proper record of who quoted what. It works, technically but it's slow and there's zero real visibility into whether you're paying a fair price.&lt;/p&gt;
&lt;p&gt;Dealing with this? Started looking into e-indenting logistics software as a fix? Good, you're thinking the right way. Let's talk about what's broken in the old process and how digital indenting actually improves visibility and control over your freight work.&lt;/p&gt;
&lt;h3 id=&quot;why-manual-indenting-leaves-you-blind&quot;&gt;Why Manual Indenting Leaves You Blind&lt;/h3&gt;
&lt;p&gt;Raising an indent by hand means the whole process lives in someone's head or it's scattered across calls and messages. There's no one place to see which loads are pending or which transporters quoted what. Or how rates moved over the past few months.&lt;/p&gt;
&lt;p&gt;This lack of visibility creates real problems. You don't really know if today's rate is fair- compared to last month or compared to what another transporter might've offered. Without proper records, every talk starts from scratch, no data backing up whether it's a good deal.&lt;/p&gt;
&lt;p&gt;Control suffers too, honestly. When indenting happens over calls and scattered messages, it's hard to keep any standard process. Different people on your team handle indents differently, some push harder and some just accept the first quote without comparing anything at all.&lt;/p&gt;
&lt;p&gt;This is exactly the gap proper e-indenting logistics software closes. Instead of info sitting in someone's head, or spread across a dozen WhatsApp chats, everything sits in one place- visible, trackable and same across your whole team.&lt;/p&gt;
&lt;h3 id=&quot;what-e-indenting-actually-means&quot;&gt;What E-Indenting Actually Means&lt;/h3&gt;
&lt;p&gt;E-indenting is basically the digital version of raising a transport indent. The request you send out with your load, route, vehicle type, and delivery time. Instead of calling around, you create this digitally, with everything laid out clearly in one format.&lt;/p&gt;
&lt;p&gt;Sounds like a small change but it shifts a lot underneath. Once your indent's digital, it can reach all your approved transporters at once, instead of calling each one and repeating the same details every single time.&lt;/p&gt;
&lt;p&gt;The digital format also means everything gets recorded from the start- who you sent it to, when and what specs you asked for. All logged automatically, instead of relying on someone's memory of a call from three days back.&lt;/p&gt;
&lt;p&gt;Good e-indenting logistics software makes this whole thing take just a couple of minutes. Set your origin, destination, load type, and timeline then send it to your transporter network instantly, no more rounds of calls.&lt;/p&gt;
&lt;h3 id=&quot;how-this-improves-rate-visibility&quot;&gt;How This Improves Rate Visibility&lt;/h3&gt;
&lt;p&gt;Once indents go digital, you start building a real history of rates over time. Something genuinely hard to track when it's all scattered phone calls, this history becomes genuinely useful for future decisions.&lt;/p&gt;
&lt;p&gt;You can actually see how rates for one route moved over months. Whether you're paying more than before, whether that rise makes sense given fuel prices or seasonal demand or whether you're just being overcharged because nobody tracked the trend.&lt;/p&gt;
&lt;p&gt;This kind of benchmark data just doesn't exist with manual work. Every talk happens on its own, no reference point for what's fair, digital records fix this. You get an actual paper trail to check before agreeing to any rate.&lt;/p&gt;
&lt;p&gt;E-indenting logistics software benchmark right in. Tracking bid history, average lane rates, and pricing trends across all your routes, so you always know if what you're paying is actually fair.&lt;/p&gt;
&lt;h3 id=&quot;better-control-through-structured-bidding&quot;&gt;Better Control Through Structured Bidding&lt;/h3&gt;
&lt;p&gt;Beyond visibility, proper e-indenting logistics software also brings real structure to how you pick transporters. Instead of picking whoever calls back first, or quotes lowest with no context, you get a clear comparison laid out properly.&lt;/p&gt;
&lt;p&gt;This means seeing multiple transporter bids side by side. Along with their past performance, delivery record, and pricing history, all in one dashboard. Instead of going with your gut, or picking based on who you spoke to last, you compare real data first.&lt;/p&gt;
&lt;p&gt;This structured way also means you keep control over who actually bids on your loads. Instead of opening things up to random transporters, you invite your own verified network. Keeping the whole thing within trusted relationships, while still getting competitive pricing through the bidding.&lt;/p&gt;
&lt;p&gt;This is really where e-indenting logistics software earns its value beyond just going paperless. It brings real structure and control to a process that used to run almost entirely on random calls and personal relationships.&lt;/p&gt;
&lt;h3 id=&quot;reducing-dependency-on-brokers-and-middlemen&quot;&gt;Reducing Dependency On Brokers And Middlemen&lt;/h3&gt;
&lt;p&gt;A lot of manual freight work ends up going through brokers. Partly because it's easier than calling several transporters individually every time a load needs moving but brokers add a commission layer that eats into your margins, without adding much real value beyond just linking you to transporters.&lt;/p&gt;
&lt;p&gt;With proper e-indenting logistics software, you send indents straight to your own transporter network- cutting out this middleman layer completely, your transporters bid directly, you see the rates directly, no broker fee sitting quietly between you and the real transport cost.&lt;/p&gt;
&lt;p&gt;This genuinely adds up over time- removing broker fees, plus competitive bidding pushing rates toward what the market's really offering, often means real savings. Sometimes 20 to 30 percent lower than what manual talks through brokers used to cost.&lt;/p&gt;
&lt;p&gt;This is a big reason manufacturers are moving toward e-indenting logistics software now, not just for ease. The money saved genuinely adds up once you cut out needless middlemen from the whole process.&lt;/p&gt;
&lt;h3 id=&quot;building-a-complete-digital-audit-trail&quot;&gt;Building A Complete Digital Audit Trail&lt;/h3&gt;
&lt;p&gt;One thing manual indenting genuinely can't give you is a proper audit trail- phone calls leave no record, whatsApp messages get lost in scrolling. Then comes audit time, or when someone questions a past call, there's often nothing solid to point back to.&lt;/p&gt;
&lt;p&gt;Digital indenting fixes this fully. Every bid, every contract, every delivery confirmation gets logged with timestamps, building a full record your finance team and logistics team can both check whenever needed- no digging through old messages and no relying on memory.&lt;/p&gt;
&lt;p&gt;This matters a lot for compliance too. Especially with GST paperwork and other rules Indian manufacturers need to track properly, having everything digital genuinely makes this simpler than piecing together paper trails after the fact.&lt;/p&gt;
&lt;p&gt;Good e-indenting logistics software builds this audit trail automatically, right as part of the normal workflow. So you're not scrambling to rebuild records later, everything's already there organised and ready whenever you need it.&lt;/p&gt;
&lt;h3 id=&quot;conclusion-making-the-shift-without-disrupting-existing-relationships&quot;&gt;Conclusion: Making The Shift Without Disrupting Existing Relationships&lt;/h3&gt;
&lt;p&gt;A common worry manufacturers have, switching to digital indenting might mean losing trusted relationships built with transporters over years. Honestly, this doesn't have to happen at all with the right setup.&lt;/p&gt;
&lt;p&gt;Good &lt;a rel=&quot;nofollow&quot; href=&quot;https://imbibe.in/ithaul/&quot;&gt;e-indenting logistics software&lt;/a&gt; lets you invite only your existing, trusted transporters to bid. Keeping your relationships intact, while still adding the structure and pricing benefits of a proper bidding process, nobody outside your approved network gets near your loads.&lt;/p&gt;
&lt;p&gt;This closed network way means you get all the perks, visibility, control, better rates, without opening things up to strangers or disrupting relationships that've worked well for years.&lt;/p&gt;
&lt;p&gt;Tired of raising indents over endless calls with zero real visibility into your freight rates? &lt;br&gt;
&lt;strong&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://imbibe.in/ithaul/&quot;&gt;ItHaul by Imbibe Tech&lt;/a&gt;&lt;/strong&gt; offers proper e-indenting logistics software built specifically for Indian manufacturers. Post your requirement in under two minutes, invite your trusted transporter network, and watch rates drop through real competition, all with a full digital audit trail. Don't keep running freight procurement blind.  &lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/28312/how-to-improve-visibility-and-control-with-e-indenting-logistics-software</guid>
<pubDate>Wed, 23 Sep 2026 11:02:02 +0000</pubDate>
</item>
<item>
<title>Building Email Aliases with Amazon SES: Lessons from EpicMail.org</title>
<link>https://coderlegion.com/27964/building-email-aliases-with-amazon-ses-lessons-from-epicmail-org</link>
<description>&lt;p&gt;You create an email address for a project. A month later, the project ends, but the email keeps coming.&lt;/p&gt;
&lt;p&gt;Having a separate address helped you organize the mail. Being able to switch that address off would make it much more useful.&lt;/p&gt;
&lt;p&gt;That is the idea behind managed aliases in EpicMail. Instead of giving users another mailbox to maintain, we wanted to give them more control over the addresses feeding the mailbox they already have.&lt;/p&gt;
&lt;p&gt;I'm Serge, the founder of EpicMail. We use Amazon SES for our built-in email service. Building aliases around it led to some useful product and engineering lessons, especially about forwarding, identity, and what a successful send actually means.&lt;/p&gt;
&lt;h3 id=&quot;decide-what-an-alias-promises&quot;&gt;Decide what an alias promises&lt;/h3&gt;
&lt;p&gt;Consider these example addresses:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;john+project [AT] epicmail.org
john+shopping [AT] epicmail.org
john+newsletters [AT] epicmail.org
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;They all belong to the same base mailbox, but each can represent a different relationship. A project has an end date. A shopping address might become noisy. Newsletters might be more useful in another account.&lt;/p&gt;
&lt;p&gt;We chose aliases that users explicitly create and manage. Once created, an alias can receive mail, appear as the sending address, show activity, be blocked, or forward to another connected account the user owns.&lt;/p&gt;
&lt;p&gt;An arbitrary suffix does not automatically become an active EpicMail address. That is a deliberate product choice: the aliases area should show the addresses the user has chosen to use.&lt;/p&gt;
&lt;p&gt;There is also a privacy distinction worth making clear. Plus addresses expose the base address. They are useful for organization and control, but they are not anonymous email masks. Someone who sees &lt;code&gt;john+shopping [AT] epicmail.org&lt;/code&gt; can also see &lt;code&gt;john&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&quot;know-what-ses-provides&quot;&gt;Know what SES provides&lt;/h3&gt;
&lt;p&gt;SES gives developers sending and receiving capabilities. The user experience around an alias is something the application still has to define.&lt;/p&gt;
&lt;p&gt;For straightforward sending, a verified domain identity can cover addresses beneath that domain without individually verifying each address. AWS documents the details and exceptions in its &lt;a rel=&quot;nofollow&quot; href=&quot;https://docs.aws.amazon.com/ses/latest/dg/creating-identities.html&quot;&gt;identity verification guide&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Receiving is a separate setup. It requires a supported receiving region, the appropriate MX records, receipt rules, and permissions for the resources involved. The &lt;a rel=&quot;nofollow&quot; href=&quot;https://docs.aws.amazon.com/ses/latest/dg/receiving-email-receipt-rules-console-walkthrough.html&quot;&gt;AWS receipt-rule walkthrough&lt;/a&gt; covers those pieces.&lt;/p&gt;
&lt;p&gt;Those capabilities make a feature like this possible. They do not decide whether an alias should work automatically, which account should receive a forward, or what a user should see after blocking an address.&lt;/p&gt;
&lt;p&gt;Writing down those behaviors before building the screens makes the implementation much easier to reason about.&lt;/p&gt;
&lt;h3 id=&quot;test-the-whole-conversation&quot;&gt;Test the whole conversation&lt;/h3&gt;
&lt;p&gt;Receiving a message is only the start. A user also expects to reply without accidentally switching identities.&lt;/p&gt;
&lt;p&gt;For an alias feature, a useful acceptance test is a complete conversation:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Send a message to the alias from another mailbox.&lt;/li&gt;
&lt;li&gt;Open it and reply using the alias.&lt;/li&gt;
&lt;li&gt;Check the address the other person sees.&lt;/li&gt;
&lt;li&gt;Reply back and confirm the conversation returns to the intended mailbox.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Do the same with a message sent directly from the alias. It is easy to make the compose selector look correct while overlooking what happens at the other end.&lt;/p&gt;
&lt;p&gt;BCC deserves a test too. The visible &lt;code&gt;To&lt;/code&gt; and &lt;code&gt;Cc&lt;/code&gt; headers do not describe every legitimate delivery. AWS distinguishes envelope recipients from message headers in its &lt;a rel=&quot;nofollow&quot; href=&quot;https://docs.aws.amazon.com/ses/latest/dg/receiving-email-notifications-contents.html&quot;&gt;receiving-notification documentation&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;These are useful tests even if you use a different email provider. The recipient experiences the entire exchange, not just your compose window.&lt;/p&gt;
&lt;h3 id=&quot;forwarding-changes-the-inbox-experience&quot;&gt;Forwarding changes the inbox experience&lt;/h3&gt;
&lt;p&gt;Suppose someone forwards an alias to another account they have also connected to EpicMail. If both copies remain in their inboxes, a unified view can show the same message twice.&lt;/p&gt;
&lt;p&gt;The forward worked, but the resulting experience is annoying.&lt;/p&gt;
&lt;p&gt;We added an auto-archive option alongside alias forwarding. It lets the user keep the original stored in the EpicMail mailbox while clearing it from Inbox after the forward is accepted for sending. Archiving preserves the message without keeping it in the way.&lt;/p&gt;
&lt;p&gt;It also makes the setting easier to explain. The user chooses where they want to read the message, while retaining a copy in the original mailbox.&lt;/p&gt;
&lt;p&gt;A forwarding feature should answer that practical question, not leave the user to discover the duplicate-copy problem afterward.&lt;/p&gt;
&lt;h3 id=&quot;use-honest-status-language&quot;&gt;Use honest status language&lt;/h3&gt;
&lt;p&gt;Email has more than one meaning of &quot;sent.&quot;&lt;/p&gt;
&lt;p&gt;Your sending service can accept a message before the destination server receives it. Even acceptance by a destination server does not guarantee that a person will find it in their inbox or read it.&lt;/p&gt;
&lt;p&gt;SES provides separate &lt;a rel=&quot;nofollow&quot; href=&quot;https://docs.aws.amazon.com/ses/latest/dg/monitor-sending-activity-using-notifications-sns.html&quot;&gt;delivery, bounce, and complaint notifications&lt;/a&gt;. Those events help describe different outcomes; they should not be collapsed into an unsupported promise that every accepted message reached a person's inbox.&lt;/p&gt;
&lt;p&gt;This affects product language as much as infrastructure. A count of forwarded messages needs a clear meaning. An error needs to tell the user whether they should take action. A success message should describe the stage the application actually knows about.&lt;/p&gt;
&lt;p&gt;For our auto-archive option, the boundary is acceptance for sending. It is not a guarantee of final delivery, and a later bounce does not automatically restore an already archived original. That is a useful limitation to state plainly.&lt;/p&gt;
&lt;h3 id=&quot;treat-suppression-as-part-of-deliverability&quot;&gt;Treat suppression as part of deliverability&lt;/h3&gt;
&lt;p&gt;SES suppression lists are another reason an address can look valid without a message being delivered as expected.&lt;/p&gt;
&lt;p&gt;AWS distinguishes bounce and complaint reasons, and its account-level suppression list applies to an AWS account in a particular region. The &lt;a rel=&quot;nofollow&quot; href=&quot;https://docs.aws.amazon.com/ses/latest/dg/sending-email-suppression-list.html&quot;&gt;suppression-list documentation&lt;/a&gt; explains the behavior and available controls.&lt;/p&gt;
&lt;p&gt;For developers, the lesson is to understand the reason and scope before changing suppression settings. A complaint carries different meaning from a delivery failure. Removing an entry is not a universal fix for deliverability, and changing your own account's list does not control another sender's system.&lt;/p&gt;
&lt;p&gt;This is part of the operational knowledge needed around email features, even when the interface itself is small.&lt;/p&gt;
&lt;h3 id=&quot;make-control-easy-to-find&quot;&gt;Make control easy to find&lt;/h3&gt;
&lt;p&gt;A useful aliases screen should answer ordinary questions quickly:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;What did I create this address for?&lt;/li&gt;
&lt;li&gt;Is it still active?&lt;/li&gt;
&lt;li&gt;Where does its mail go?&lt;/li&gt;
&lt;li&gt;Has it been used?&lt;/li&gt;
&lt;li&gt;How do I stop receiving mail through it?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In EpicMail, aliases have an area for management and activity, with search and paging as the list grows. The sending identity is available from the compose window, where that choice belongs.&lt;/p&gt;
&lt;p&gt;The aliases share the original mailbox's received-message allowance. Creating more addresses does not create additional mailboxes or additional allowances. Keeping that relationship clear avoids surprises later.&lt;/p&gt;
&lt;h3 id=&quot;why-we-added-this-to-epicmail&quot;&gt;Why we added this to EpicMail&lt;/h3&gt;
&lt;p&gt;EpicMail brings existing accounts into one place: &lt;strong&gt;A unified inbox for Gmail, Outlook, Yahoo, iCloud, IMAP, and POP3.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Managed aliases add another way to organize incoming mail, specifically through the optional paid &lt;code&gt;@epicmail.org&lt;/code&gt; inbox. They are included with that inbox without a separate fee per alias, and share its 5,000-message received allowance per usage period. This feature does not create aliases at a user's connected Gmail, Outlook, or other provider account.&lt;/p&gt;
&lt;p&gt;There is a walkthrough on the &lt;a rel=&quot;nofollow&quot; href=&quot;https://epicmail.org/email-aliases?utm_source=coderlegion&amp;amp;utm_medium=referral&amp;amp;utm_campaign=epicmail_aliases_2026&amp;amp;utm_content=ses_aliases_article&quot;&gt;EpicMail email aliases page&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The design question I would leave other developers with is this: when someone no longer wants to use an address, how easily can they take control of it? That answer can make an alias much more useful than a different spelling of the same inbox.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/27964/building-email-aliases-with-amazon-ses-lessons-from-epicmail-org</guid>
<pubDate>Mon, 21 Sep 2026 14:02:27 +0000</pubDate>
</item>
<item>
<title>Jev by TypeSafe AI: Hype, Tool or Threat?</title>
<link>https://coderlegion.com/27909/jev-by-typesafe-ai-hype-tool-or-threat</link>
<description>&lt;p&gt;A few days ago, TypeSafe AI introduced &lt;a rel=&quot;nofollow&quot; href=&quot;https://typesafe.ai/blog/introducing-system-one-models-and-jev&quot;&gt;Jev&lt;/a&gt;, a new model designed to make fast, structured decisions that software can use directly.&lt;/p&gt;
&lt;p&gt;I read the announcement and had two questions: Is Jev more of a hype? Is it really useful for me as a backend engineer? And does it present a new threat of replacing me as a software engineer? Jev also reminded me of the OpenAI REST API &quot;structured outputs&quot; endpoints. I wondered whether Jev would offer any advantages over OpenAI.&lt;/p&gt;
&lt;p&gt;Here is what I found.&lt;/p&gt;
&lt;h3 id=&quot;what-does-jev-actually-do&quot;&gt;What Does Jev Actually Do&lt;/h3&gt;
&lt;p&gt;Jev (by TypeSafe AI) belongs to a class of narrowly specialized models that provide structured outputs rather than just prose. It accepts application state as input and returns structured decisions. The possible outputs and their types are defined in advance.&lt;/p&gt;
&lt;p&gt;For example, an application could provide information about a customer and ask Jev to classify the customer's churn risk, determine which support department should handle a request, or score a potential business opportunity. The application receives predefined values that it can use in its business logic.&lt;/p&gt;
&lt;p&gt;Very similar to OpenAI &quot;structured outputs&quot;, I think.&lt;/p&gt;
&lt;p&gt;TypeSafe AI claims that Jev provides several advantages:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Response times of 70–500 ms.&lt;/li&gt;
&lt;li&gt;Input costs of $0.042 per million tokens.&lt;/li&gt;
&lt;li&gt;Guaranteed adherence to predefined output types.&lt;/li&gt;
&lt;li&gt;Calibrated probabilities and confidence scores accompanying its decisions.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The company also claims substantial improvements in speed and cost compared with existing LLMs performing similar tasks.&lt;/p&gt;
&lt;p&gt;These are the company's published results. Jev is currently in early access, and I haven't tested it myself.&lt;/p&gt;
&lt;h3 id=&quot;jev-vs-openai-structured-outputs&quot;&gt;Jev vs OpenAI Structured Outputs&lt;/h3&gt;
&lt;p&gt;I have already worked with OpenAI's API to implement structured classification and decision-making workflows. OpenAI introduced Structured Outputs in August 2024. With &lt;code&gt;strict: true&lt;/code&gt;, developers can define a JSON Schema and receive responses conforming to that schema.&lt;/p&gt;
&lt;p&gt;We can already classify incoming data, select values from predefined enums, extract structured information, and use the results in application code. ([OpenAI][2]). Jev provides essentially the same capabilities for these use cases.&lt;/p&gt;
&lt;p&gt;Jev's underlying generation mechanism (it generates its decision outputs in parallel and provides associated probabilities - confidence scores) is interesting, but there is little reason to judge Jev's advantage based on its implementation details.&lt;/p&gt;
&lt;p&gt;The practical question for a backend engineer is whether Jev produces decisions of comparable accuracy, faster and at a lower cost, for the same workload. They claim it has an edge in this. But with OpenAI Structured Outputs we can select the seriously cheaper model still very capable of producing structured outputs with a large focused (cached) context.&lt;/p&gt;
&lt;h3 id=&quot;jev-useful-in-real-backend-systems-&quot;&gt;Jev: Useful in Real Backend Systems?&lt;/h3&gt;
&lt;p&gt;At one innovation analytics startup, I worked on backend architecture and LLM-powered ingestion and scoring pipelines. The system needed to process incoming information, classify it using controlled vocabularies, and produce structured results that could be used by subsequent application components.&lt;/p&gt;
&lt;p&gt;I implemented deterministic validation gates to check model outputs before allowing the code to proceed through the workflow. Looking at Jev's capabilities, I can see where it could fit into this architecture. For example, I could use it to classify incoming records or calculate particular scores.&lt;/p&gt;
&lt;p&gt;I had great results with &lt;a rel=&quot;nofollow&quot; href=&quot;https://developers.openai.com/api/docs/models/gpt-4o-mini&quot;&gt;GPT-4o Mini&lt;/a&gt; (quite cheaper than GPT Luna that TypeSafe AI compares Jev with) with latencies well within 100ms and costs like $0.15 per 1,000 requests of ~2K prompt and ~700 bytes JSON response.&lt;/p&gt;
&lt;p&gt;Separately, Jev's confidence scores may seem to be useful for workflows where an application needs to decide whether to accept a model's decision or send the case for further processing. But these scores can be equally misleading requiring additional deterministic validation gates, thus adding the complexity to a workflow.&lt;/p&gt;
&lt;p&gt;My conclusion is Jev deserves some A/B testing under realistic production workloads vs OpenAI structured outputs before any decision. With millions of requests it may offer an advantage. As well its confidence scores may be used in some cases.&lt;/p&gt;
&lt;h3 id=&quot;a-tool-or-an-engineer-s-replacement-&quot;&gt;A Tool Or an Engineer's Replacement.&lt;/h3&gt;
&lt;p&gt;This was my original concern. A usual senior backend engineer's work involves designing application architecture, domain models and service boundaries, implementing APIs, working with databases, and building distributed systems, and often, implementing business workflows that involve classification, scoring, and other decisions based on incoming data.&lt;/p&gt;
&lt;p&gt;Jev could automate some decision-making operations. As well as OpenAI structured outputs does. But hardly presents any threat to the entire engineering process. So the answer is &quot;a tool&quot;, that has had alternatives for already ~2 years.&lt;/p&gt;
&lt;h3 id=&quot;my-conclusion&quot;&gt;My Conclusion&lt;/h3&gt;
&lt;p&gt;After examining Jev and comparing it with OpenAI's existing Structured Outputs, I see it as a potentially useful addition to my backend engineering toolkit with advantages that have yet to be proven for production systems already using OpenAI structured outputs.&lt;/p&gt;
&lt;p&gt;For now, I am interested in seeing how Jev performs on real business workloads and whether its claimed performance improvements justify replacing existing LLM calls in production systems.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/27909/jev-by-typesafe-ai-hype-tool-or-threat</guid>
<pubDate>Mon, 21 Sep 2026 09:42:28 +0000</pubDate>
</item>
<item>
<title>Two-Year Reflection on Methodox and the Development of Divooka</title>
<link>https://coderlegion.com/27868/two-year-reflection-on-methodox-and-the-development-of-divooka</link>
<description>&lt;blockquote&gt;&lt;p&gt;Still going strong, and hopefully we will be ready soon.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;the-divooka-story&quot;&gt;The Divooka Story&lt;/h3&gt;
&lt;p&gt;It's surprising to count—it has already been so many years since the first prototype in 2022, the later attempted updates in 2023/2024, the first &quot;beta&quot; in September 2024 that we later downplayed to something closer to an &quot;alpha&quot;, the internal builds throughout 2025, the much greater feature expansion towards 2026, and now, over the past few months, what I hope will be the &quot;final&quot; major round of architectural refactoring around the runtime, file format, package management, and features needed for professional use. I just wish I didn't age at the same rate as my software concept.&lt;/p&gt;
&lt;p&gt;I spent a lot of time earlier this year, particularly around June, polishing DSLs—Domain-Specific Libraries—because without a useful set of higher-level capabilities, Divooka is JUST a language. A language can be technically interesting while still leaving the user to build everything themselves; the point of Divooka is eventually to provide useful computational building blocks at a much higher level. The areas I've chosen to focus on are naturally the areas I know reasonably well—not necessarily the areas where I would claim to be the world's greatest expert, but areas where I think we can make genuinely useful tools for everyday work: data processing, procedural terrain generation, graphics and media work, automation, and office productivity.&lt;/p&gt;
&lt;p&gt;The procedural context in particular took a lot of effort to conceptualize. I wanted to address several problems I have experienced with node-based systems such as Unreal Engine's Blueprint: large procedural graphs can become difficult to restructure, reuse and reason about, while certain programming patterns are awkward to express visually in the first place. Divooka's procedural context is my attempt to retain the visibility of graph-based programming without pretending that traditional programming concepts such as loops, branches, functions and structured control flow somehow stopped being useful. The move towards a compiled runtime was another major jump, and really only came together around April and May 2026. Our Mandelbrot benchmark makes the motivation rather obvious: compilation changed the performance characteristics dramatically compared with execution through the older editor/runtime paths. Fully embracing compilation has been a major undertaking, but I increasingly see it as essential if Divooka is going to combine very high-level programming with acceptable performance. More details and the limitations of the benchmark are published in the GitHub repository linked below.&lt;/p&gt;
&lt;p&gt;The file format, runtime and standardization work has been another recurring problem. This is also why we have deliberately slowed down on declaring a final version of DiDS or other Divooka Open Standards. The formats have changed A LOT as the architecture has matured, and publishing something as &quot;final&quot; before we are prepared to support it for years would create more problems than it solves. I don't see much value in pretending the specification is frozen merely for the satisfaction of calling it 1.0. Stability matters, interoperability matters, and eventually backward compatibility matters—but those things are much easier to promise after the underlying system has actually settled.&lt;/p&gt;
&lt;p&gt;So where are we going? General-purpose at the core, high utility and high performance, while keeping a super-high-level interface closer to the experience of scripting languages than traditional application development. Nodes where nodes make things clearer, code where code makes things clearer, and enough interoperability underneath that users are not trapped inside an isolated visual-programming island. For people already familiar with programming language design, scripting systems and visual development environments, that probably explains most of the idea. At this point, I am content with that.&lt;/p&gt;
&lt;h3 id=&quot;internal-and-financial&quot;&gt;Internal and Financial&lt;/h3&gt;
&lt;p&gt;The financial side became more challenging as we moved through 2026. We are now entering our third year and completing our second, and naturally the resources that felt relatively abundant when I started the company in 2024 are more constrained today. Legal work, IP, infrastructure, hiring and contracting all cost real money, and founder time has a cost even when it doesn't appear as a salary on an income statement. I also did not exactly enter this adventure after ten years of living like a monk and accumulating an infinite personal runway. At some point every bootstrapped founder has to ask the uncomfortable but necessary question: how long can this continue before the business itself has to carry more of the weight?&lt;/p&gt;
&lt;p&gt;That doesn't mean I suddenly regret doing it. I am still relatively young—a little over 30—but two years is long enough that the startup can no longer be treated as a temporary experiment detached from the rest of life. I have to think about long-term financial stability, savings, retirement and everything else that continues existing regardless of whether somebody decides to build a visual programming system. There is no magical funding event I can responsibly assume will arrive and solve those questions for me, so one way or another the company has to become increasingly sustainable on its own. In that sense, commercialization is becoming more important, not because the R&amp;amp;D suddenly matters less, but because the only durable way to continue doing the R&amp;amp;D is to build a business capable of supporting it.&lt;/p&gt;
&lt;p&gt;I doubt I would particularly enjoy going back to conventional corporate life, although occasionally I do miss parts of it: working in those skyscrapers, looking down at people and cars twenty floors below as little moving dots, the view of Lake Ontario, interesting corporate events, colleagues with very different expertise, and simply being part of something much larger. But it is fundamentally different from being one's own boss. In a large organization I could sometimes lose sight of why a particular piece of work existed in the first place; here, almost every decision is connected directly to something we eventually put in front of users. Maybe one day a smaller studio environment would suit me too, but there is something uniquely rewarding about being able to make a decision, build the thing, and then see something made by us actually published.&lt;/p&gt;
&lt;p&gt;That is something to think about. We need to act soon—not out of panic, but because after two years, sustainability deserves to be treated as part of the engineering problem rather than something outside of it.&lt;/p&gt;
&lt;h3 id=&quot;noise-splashes-and-small-gadgets&quot;&gt;Noise, Splashes and Small Gadgets&lt;/h3&gt;
&lt;p&gt;The development process also spun off a surprising number of smaller products. Some were experiments, some became quite handy, and some have already gone through several iterations of their own. I don't really see these as completely separate from Divooka: in many cases they came from technologies, libraries, ideas or workflows developed while working on the larger system, and they give us smaller places to test whether those pieces are actually useful outside the environment in which they were invented.&lt;/p&gt;
&lt;p&gt;Released / scheduled: Fantasy Planet Painter, A Detective Story and Divooka Explore.&lt;/p&gt;
&lt;p&gt;Smaller apps: Methodox Threads, Methodox Voxel Sandbox, and Transcriber—which has been surprisingly popular as a free tool.&lt;/p&gt;
&lt;p&gt;It sometimes feels like we spent a lot of time on each of these, and then when I actually count the list it somehow doesn't look like that much. Such is software development.&lt;/p&gt;
&lt;h3 id=&quot;public-facing&quot;&gt;Public-Facing&lt;/h3&gt;
&lt;p&gt;On the product-readiness side, we've updated the official Methodox website, added dedicated websites for the main products, greatly expanded the Methodox Wiki, and gone through migrations away from systems such as Wiki.js and WordPress in the process. The public surface of a software company is itself a substantial product: documentation, downloads, storefronts, licensing information, release notes, product pages and examples all have to remain reasonably synchronized with software that is changing underneath them. That work is less glamorous than implementing a compiler or graph runtime, but without it there isn't really a product that an outside person can confidently understand or use.&lt;/p&gt;
&lt;p&gt;The CI, packaging and deployment side has improved as well. Those processes had remained painfully manual for much longer than I would have liked, and ironing out the development pipeline has been one of those improvements that is difficult to advertise but immediately makes everything else easier. Releasing software should eventually become boring. If every build and publication feels like a special event requiring a checklist of mysterious manual steps, something in the engineering process is still unfinished.&lt;/p&gt;
&lt;h3 id=&quot;lessons&quot;&gt;Lessons&lt;/h3&gt;
&lt;p&gt;Looking back to my reflection from July 8 last year, the burden from training, external obligations and general management has been reduced considerably. A surprising amount of time last year disappeared into accounting and tax work, which was not exactly why I founded a software company, but I suppose—and I must believe—that doing something three times makes the fourth much less intimidating. More importantly, I think I now have a better understanding of what actually makes a &quot;product&quot;, especially the amount of work between something technically functioning and something polished enough to release, document, announce and support. A feature being implemented is not the same thing as a product being finished. That distinction sounds obvious until you repeatedly experience how much work lives between those two points.&lt;/p&gt;
&lt;p&gt;One thing I still don't do enough is &quot;engage with the community&quot;. I understand why people keep emphasizing it, and I don't think building completely in isolation is healthy, but my view has become more nuanced over the past year: community engagement becomes substantially more useful when there is something coherent for that community to engage with. Feedback on a moving architectural target can easily turn into noise for both sides. Divooka Explore's Early Access period should give us a better mechanism for that interaction, because users can increasingly react to a functioning system rather than an abstract promise.&lt;/p&gt;
&lt;p&gt;Things such as cloud services through Methodox One, AI integration through projects such as Chat Gateway, and various other interesting ideas people suggested all pulled my attention in different directions. In retrospect, the core runtime was simply not ready enough for that level of expansion, even towards the middle of 2026. It was really only over the past month or so that I began feeling that the foundation was taking good shape and, more importantly, starting to feel robust and trustworthy. The runtime took an unexpectedly long time to develop even though I am no stranger to making scripting systems: EasyShell worked well for what it was designed to do, and I have been using Pure in one form or another since my time at OTPP. But Divooka is a fundamentally larger undertaking. Creating a new syntax, graph model, execution system, editor, IDE, packages, interoperability layer and application model from the ground up is very different from building a convenient language layer on top of an existing interface. We absolutely use existing foundations—Roslyn, .NET, C#, Python and their enormous ecosystems—but there are still a remarkable number of edges that become our responsibility the moment we try to make them feel like one coherent system.&lt;/p&gt;
&lt;p&gt;Launching the product has always been a priority, but during roughly the first year and a half I have to admit that I spent too much attention on what I thought I was supposed to be doing as an &quot;entrepreneur&quot;: conferences, investor calls, chasing VC conversations, networking because people said networking was important, and generally talking about building the company while I was still trying to build the thing the company exists to make. Some friends still tell me I haven't talked to enough people, and they may well have a point. But I've also learned that there is a limit to how much external motion is useful before it becomes noise. Right now I care much more about making Divooka real, solid and usable. There will always be more people to talk to afterwards.&lt;/p&gt;
&lt;h3 id=&quot;the-ai-gain&quot;&gt;The AI Gain&lt;/h3&gt;
&lt;p&gt;I am finally starting to officially &quot;use AI&quot; in a much more meaningful way—more precisely, coding agents. I have used ChatGPT on a near-daily basis for quite a long time, while also being fairly critical of media coverage portraying every new model as immediately revolutionary. For a long time I simply didn't find the reality matched the hype. That has changed considerably through the middle of 2026, and especially since late July. Coding agents have become useful enough that I can no longer dismiss them as an unreliable novelty: for certain kinds of implementation, research, repetitive work and review, they are genuinely efficient.&lt;/p&gt;
&lt;p&gt;That does not mean I suddenly believe an AI is going to independently build Divooka for us. In practice it feels much more like a reduction in the cost of certain kinds of labour. It can generate, inspect, compare, research and iterate very quickly, but somebody still has to decide what ought to exist, whether the proposed architecture makes sense, whether the output actually works, and whether any of it belongs in the product. If anything, human capacity has become a more obvious bottleneck: judgment, prioritization and verification do not disappear merely because implementation becomes cheaper. The useful question is no longer &quot;is AI useful?&quot; but &quot;which work should we delegate to it, which work should we keep doing ourselves, and how do we verify the difference?&quot; That is a much more practical question, and one I expect will continue changing rapidly.&lt;/p&gt;
&lt;h3 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;The one thing I would most like to see now is Divooka properly released. But my philosophy throughout this project has been that some steps simply cannot be skipped. That is part of the reason I quit my job and went deep into R&amp;amp;D in the first place: if the only objective had been to ship something quickly, there were much easier products I could have chosen to build.&lt;/p&gt;
&lt;p&gt;I still look forward to the day when I can produce a fully illustrated, properly written &lt;em&gt;Introduction to Visual Programming with Divooka&lt;/em&gt;. In my head I can already see it as an entire textbook covering a way of thinking about programs that is still poorly represented in conventional computer science education—not &quot;visual programming&quot; as a collection of toy blocks, and not simply a diagrammatic wrapper around scripting, but a serious treatment of graphs as a practical programming medium alongside text. If we can eventually build a system rich enough that such a book genuinely deserves to exist, I think that will be an achievement in its own right.&lt;/p&gt;
&lt;p&gt;And I think that is worth taking the time to get right.&lt;/p&gt;
&lt;h3 id=&quot;references&quot;&gt;References&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Methodox Official Website: &lt;a rel=&quot;nofollow&quot; href=&quot;https://methodox.io&quot;&gt;https://methodox.io&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Divooka Official Website: &lt;a rel=&quot;nofollow&quot; href=&quot;https://divooka.com&quot;&gt;https://divooka.com&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Methodox Wiki: &lt;a rel=&quot;nofollow&quot; href=&quot;https://wiki.methodox.io&quot;&gt;https://wiki.methodox.io&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Methodox Apps: &lt;a rel=&quot;nofollow&quot; href=&quot;https://methodox.app&quot;&gt;https://methodox.app&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Steam Divooka Explore page: &lt;a rel=&quot;nofollow&quot; href=&quot;https://store.steampowered.com/app/4422920/Divooka_Explore/&quot;&gt;https://store.steampowered.com/app/4422920/Divooka_Explore/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Steam Fantasy Planet Painter page: &lt;a rel=&quot;nofollow&quot; href=&quot;https://store.steampowered.com/app/3336490/Fantasy_Planet_Painter/&quot;&gt;https://store.steampowered.com/app/3336490/Fantasy_Planet_Painter/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Itch.io Store Front: &lt;a rel=&quot;nofollow&quot; href=&quot;https://methodox.itch.io&quot;&gt;https://methodox.itch.io&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Mandelbrot Benchmark: &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/MethodoxTech/DivookaBenchmark_Mandelbrot&quot;&gt;https://github.com/MethodoxTech/DivookaBenchmark_Mandelbrot&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/27868/two-year-reflection-on-methodox-and-the-development-of-divooka</guid>
<pubDate>Sun, 20 Sep 2026 22:00:11 +0000</pubDate>
</item>
<item>
<title>HindSite Project</title>
<link>https://coderlegion.com/27866/hindsite-project</link>
<description>&lt;h2 id=&quot;the-code-doesn-t-tell-the-whole-story&quot;&gt;The Code Doesn't Tell the Whole Story&lt;/h2&gt;
&lt;p&gt;I've been building software for more than 20 years, and one thing I've learned is that understanding a software system means understanding more than the code sitting in the repository. The implementation tells us what exists today, but it doesn't necessarily tell us everything that happened while the software was being built.&lt;/p&gt;
&lt;p&gt;Why was one approach chosen over another? What assumptions did we start with? What did we try that didn't work? What changed once implementation began? What did we discover along the way? Some of that information might survive in issues, pull requests, commit messages, documentation, or conversations, but much of it doesn't. Eventually, we're left with the software and have to work backward from what remains.&lt;/p&gt;
&lt;p&gt;I've become much more aware of this problem as I've incorporated AI agents into my software development workflow. An agent can inspect a repository, read the code, analyze the architecture, review a diff, and trace an implementation. Those capabilities are becoming remarkably good. But there is still context I have as the builder that isn't necessarily represented in any of those artifacts. The agent can see what was built without always having access to the context surrounding how the software became what it is.&lt;/p&gt;
&lt;p&gt;That's the problem I've been exploring with &lt;strong&gt;HindSite&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;HindSite is a developer tool for reconstructing and preserving the story of software construction. The current product centers around something I call a &lt;strong&gt;Build Story&lt;/strong&gt;. A Build Story uses evidence produced during development to reconstruct what happened during a piece of software construction. The builder can review that reconstruction, correct or refine it, reflect on what was learned, and preserve the resulting context as part of the history of the system.&lt;/p&gt;
&lt;p&gt;The distinction between evidence and reflection is important to me. I'm not trying to create another development activity tracker or automatically manufacture documentation from everything a developer does. A Git commit can tell us that something changed. A pull request can contain a discussion about the implementation. A sequence of development activity can help reconstruct what happened. But the builder may be the only person who can explain why something mattered, what changed in their understanding, or what they would want another builder to know later.&lt;/p&gt;
&lt;p&gt;Interestingly, HindSite itself has changed as I've built it.&lt;/p&gt;
&lt;p&gt;My original thinking was much more centered on understanding the repository and codebase. That made sense to me because that's where we tend to look when we need to understand an existing software system. But while building HindSite, I began to recognize that the development workflow itself contains useful evidence. Before HindSite could help explain the deeper reasoning behind a system, it first needed to become better at understanding what actually happened while that system was being built.&lt;/p&gt;
&lt;p&gt;That realization moved the initial product toward &lt;strong&gt;workflow comprehension&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;The process has also reinforced something I believe about software construction in general: sometimes you have to let the software tell you what it is. Architecture and product design require intention, but building also produces information. You make assumptions, implement them, observe the result, and sometimes discover that the problem is slightly different from the one you thought you were solving.&lt;/p&gt;
&lt;p&gt;There's a bit of irony in realizing that HindSite has been discovered through the same process it's intended to help preserve.&lt;/p&gt;
&lt;p&gt;Today, the core Build Story workflow exists. A builder can capture evidence, reconstruct the development activity, review the reconstruction, reflect on what happened, and produce an exportable record of the Build Story. There is still technical work underway, including remaining integration and application cleanup, so HindSite is &lt;strong&gt;pre-alpha&lt;/strong&gt; and I'm intentionally not opening the workspace to users yet. I'd rather finish the foundation than invite people into something before I believe it's ready for them.&lt;/p&gt;
&lt;h3 id=&quot;see-hindsite-in-action&quot;&gt;See HindSite in Action&lt;/h3&gt;
&lt;p&gt;I put together a short video showing the idea behind HindSite and what I've been building:&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://www.youtube.com/watch?v=pSNrKKkUT-s&quot;&gt;&lt;img src=&quot;https://img.youtube.com/vi/pSNrKKkUT-s/maxresdefault.jpg&quot; alt=&quot;HindSite — Introduction and Preview&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://www.youtube.com/watch?v=pSNrKKkUT-s&quot;&gt;Watch the HindSite introduction and preview on YouTube&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;get-involved&quot;&gt;Get Involved&lt;/h3&gt;
&lt;p&gt;I'm sharing HindSite here now because CoderLegion is a community of developers, and I'm interested in finding builders who recognize the problem before I begin inviting people into the alpha.&lt;/p&gt;
&lt;p&gt;If you've returned to your own software months later and had to reconstruct why something was done, inherited a system where the implementation survived but much of its context didn't, or discovered that an AI agent could understand your code without understanding everything you know about the system, then you're experiencing some of the territory HindSite is exploring.&lt;/p&gt;
&lt;p&gt;The alpha waitlist is available through the project website linked above. For now, I'm continuing to build, observe, and learn what HindSite needs to become.&lt;/p&gt;
</description>
<category>Launches</category>
<guid isPermaLink="true">https://coderlegion.com/27866/hindsite-project</guid>
<pubDate>Sun, 20 Sep 2026 20:51:34 +0000</pubDate>
</item>
<item>
<title>Building Trucker Suite From Problems I Actually Faced on the Road</title>
<link>https://coderlegion.com/27691/building-trucker-suite-from-problems-i-actually-faced-on-the-road</link>
<description>&lt;p&gt;For more than eight years, commercial trucking was my day-to-day reality.&lt;/p&gt;
&lt;p&gt;Long hours. Appointment windows. Detention. Deadhead miles. Dispatch messages. Pay calculations. Hours-of-service limits. Trying to remember what time I arrived at a shipper while also keeping track of what a load was actually worth.&lt;/p&gt;
&lt;p&gt;One thing that always bothered me was how fragmented everything felt. Disjointed. Scattered. &lt;/p&gt;
&lt;p&gt;A driver might use one app for navigation, another for logs, notes on their phone for arrival times, a calculator for load value, screenshots for dispatch information, and maybe a spreadsheet or notepad to keep track of weekly pay. And alot of times, these things all reside in the drivers head, bouncing around uncertainly.&lt;/p&gt;
&lt;p&gt;None of those tools are necessarily bad on their own.&lt;/p&gt;
&lt;p&gt;The problem is that they often don't work together.&lt;/p&gt;
&lt;p&gt;That frustration eventually became the starting point for &lt;strong&gt;Trucker Suite&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Starting With a Problem I Already Knew&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;When I started working on Trucker Suite, I wasn't trying to invent some theoretical problem that might exist.&lt;/p&gt;
&lt;p&gt;I had already lived it.&lt;/p&gt;
&lt;p&gt;If I arrived at a receiver and had to wait several hours, I wanted an easy way to record when I arrived, when detention started, and what that detention might be worth.&lt;/p&gt;
&lt;p&gt;If dispatch sent me a load, I wanted to be able to look at the mileage and pay and quickly answer a simple question:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Is this load actually worth taking?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;And at the end of the week, I wanted to know what my dispatched miles and projected pay looked like without reconstructing everything from old messages.&lt;/p&gt;
&lt;p&gt;Those became some of the first systems inside Trucker Suite.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;DockClock&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;DockClock was one of the earliest features.&lt;/p&gt;
&lt;p&gt;Its purpose is simple: track time spent at a shipper or receiver and make detention easier to document.&lt;/p&gt;
&lt;p&gt;That sounds straightforward until you start building it.&lt;/p&gt;
&lt;p&gt;Suddenly you have to think about:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;arrival timestamps &lt;/li&gt;
&lt;li&gt;editable times &lt;/li&gt;
&lt;li&gt;detention start thresholds      &lt;/li&gt;
&lt;li&gt;elapsed-time calculations &lt;/li&gt;
&lt;li&gt;different stages of a stop &lt;/li&gt;
&lt;li&gt;user mistakes  &lt;/li&gt;
&lt;li&gt;saving the information between sessions &lt;/li&gt;
&lt;li&gt;updating the UI as the state changes&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Something as simple as &quot;track how long I've been here&quot; quickly becomes a state-management problem.&lt;/p&gt;
&lt;p&gt;That was one of the first lessons Trucker Suite taught me: the user only sees the timer.&lt;/p&gt;
&lt;p&gt;The developer has to think about everything that could make the timer wrong.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;LoadWorth&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;LoadWorth came from another question I asked constantly while driving:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;What is this load really worth?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;A load can look good on paper until you factor in deadhead miles, total dispatched miles, rate, time, and other variables.&lt;/p&gt;
&lt;p&gt;So LoadWorth is designed to give drivers a faster way to evaluate the economics of an individual load.&lt;/p&gt;
&lt;p&gt;Again, the visible part is relatively simple.&lt;/p&gt;
&lt;p&gt;The harder part is making sure calculations remain consistent when values change, inputs are missing, or users go back and edit something later.&lt;/p&gt;
&lt;p&gt;I've spent a surprising amount of development time fixing situations where a single value changes and something elsewhere on the screen doesn't update correctly.&lt;/p&gt;
&lt;p&gt;Those bugs are annoying.&lt;/p&gt;
&lt;p&gt;They're also where a lot of the learning happens.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Pay Week&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Pay Week takes the idea beyond a single load.&lt;/p&gt;
&lt;p&gt;The goal is to give a driver a running picture of the week:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;dispatched mileage&lt;/li&gt;
&lt;li&gt;completed loads&lt;/li&gt;
&lt;li&gt;estimated earnings&lt;/li&gt;
&lt;li&gt;recent activity&lt;/li&gt;
&lt;li&gt;load history&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Eventually I want this to connect much more deeply with active trips and driver workflows.&lt;/p&gt;
&lt;p&gt;Instead of asking drivers to manually piece together everything they've done that week, the application should already understand enough of their recent activity to provide useful information.&lt;/p&gt;
&lt;p&gt;That's the direction I'm working toward.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Why Unreal Engine?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;One unusual part of Trucker Suite is that I'm building it in &lt;strong&gt;Unreal Engine 5.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Unreal obviously isn't the first thing most people think of when they hear &quot;driver productivity app.&quot;&lt;/p&gt;
&lt;p&gt;That's part of what made the project interesting to me.&lt;/p&gt;
&lt;p&gt;I've been studying game development and working extensively with Unreal Engine, Blueprint visual scripting, widgets, data tables, structs, enums, SaveGame systems, and event-driven logic.&lt;/p&gt;
&lt;p&gt;I wanted to see how far I could push those tools outside a traditional game.&lt;/p&gt;
&lt;p&gt;Quite far, apparently.&lt;/p&gt;
&lt;p&gt;Trucker Suite has forced me to work with persistent data, calculations, configurable profiles, dynamic interfaces, timestamps, state-driven workflows, filtering, input validation, and mobile packaging.&lt;/p&gt;
&lt;p&gt;Some of those systems would be perfectly at home in a game.&lt;/p&gt;
&lt;p&gt;Others definitely wouldn't.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The Blueprint Spaghetti Problem&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Anyone who has spent enough time in Unreal Blueprints probably knows what eventually happens.&lt;/p&gt;
&lt;p&gt;You start with a clean little graph.&lt;/p&gt;
&lt;p&gt;Then you add one condition.&lt;/p&gt;
&lt;p&gt;Then another.&lt;/p&gt;
&lt;p&gt;Then a branch.&lt;/p&gt;
&lt;p&gt;Then a calculation.&lt;/p&gt;
&lt;p&gt;Then an update event.&lt;/p&gt;
&lt;p&gt;Then you zoom out and realize you've created something that looks like an electrical diagram designed by an octopus.&lt;/p&gt;
&lt;p&gt;I've been slowly cleaning that up as Trucker Suite grows.&lt;/p&gt;
&lt;p&gt;The project has made me much more conscious of separating responsibilities, using functions, keeping reusable logic together, and trying not to let one feature become a giant chain of interconnected nodes.&lt;/p&gt;
&lt;p&gt;I don't always succeed.&lt;/p&gt;
&lt;p&gt;But the graph usually looks better after the third rebuild.&lt;/p&gt;
&lt;p&gt;Usually.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Building for Drivers Instead of Guessing at Drivers&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The biggest advantage I have with Trucker Suite is that I don't have to guess what a long day in a truck looks like.&lt;/p&gt;
&lt;p&gt;I've lived it.&lt;/p&gt;
&lt;p&gt;I've sat at docks wondering whether detention was being calculated correctly.&lt;/p&gt;
&lt;p&gt;I've looked at loads with long deadheads and questioned whether the mileage made sense.&lt;/p&gt;
&lt;p&gt;I've dealt with dispatch schedules, HOS limits, appointment times, paperwork, and trying to keep track of everything while working a 12- or 14-hour day.&lt;/p&gt;
&lt;p&gt;That doesn't automatically mean every idea I have is correct.&lt;/p&gt;
&lt;p&gt;Real users still need to validate the product.&lt;/p&gt;
&lt;p&gt;Other drivers will have different workflows, different pay structures, and different priorities.&lt;/p&gt;
&lt;p&gt;That's why one of the next major stages for Trucker Suite is getting it into the hands of more working drivers and learning from what they actually do with it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Where Trucker Suite Is Going&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The current application includes the foundations of DockClock, LoadWorth, Pay Week, history, driver settings, and persistent data.&lt;/p&gt;
&lt;p&gt;The longer-term roadmap goes further.&lt;/p&gt;
&lt;p&gt;I'm working toward systems such as:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;active trip and load management &lt;/li&gt;
&lt;li&gt;manual HOS tracking &lt;/li&gt;
&lt;li&gt;trip feasibility&lt;/li&gt;
&lt;li&gt;calculations &lt;/li&gt;
&lt;li&gt;trip history and archiving &lt;/li&gt;
&lt;li&gt;expanded driver profiles&lt;/li&gt;
&lt;li&gt;tighter integration between load information and weekly pay &lt;/li&gt;
&lt;li&gt;eventual fleet-facing functionality&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The goal isn't to build another giant system that forces drivers to adapt their entire workflow around it.&lt;/p&gt;
&lt;p&gt;I'd rather build something that fits naturally into what they already do.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;What This Project Has Taught Me&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Trucker Suite started because I was frustrated with a problem.&lt;/p&gt;
&lt;p&gt;It has turned into one of the projects that has taught me the most about software development.&lt;/p&gt;
&lt;p&gt;Not because every part of it is technically revolutionary.&lt;/p&gt;
&lt;p&gt;Because every feature forces me to deal with real decisions.&lt;/p&gt;
&lt;p&gt;What happens if the user enters the wrong time?&lt;/p&gt;
&lt;p&gt;What happens if data is missing?&lt;/p&gt;
&lt;p&gt;What happens if the application closes?&lt;/p&gt;
&lt;p&gt;What happens if a calculation depends on another value that just changed?&lt;/p&gt;
&lt;p&gt;What happens when the interface looks fine on my development screen but terrible on a phone?&lt;/p&gt;
&lt;p&gt;Those questions are where the actual work lives.&lt;/p&gt;
&lt;p&gt;And every time I solve one of them, the application moves a little closer to becoming something I would have genuinely wanted sitting beside me in the truck.&lt;/p&gt;
&lt;p&gt;That's ultimately the standard I'm trying to build toward.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Trucker Suite is being developed through Vasken Interactive Studios LLC.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;I'm continuing to build, test, break, fix, and refine it as I work toward a broader driver beta and eventual commercial release.&lt;/p&gt;
&lt;p&gt;If you're a developer who's ever built software around a problem you personally dealt with, I'd be interested to hear what changed once real users got their hands on it.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/27691/building-trucker-suite-from-problems-i-actually-faced-on-the-road</guid>
<pubDate>Sat, 19 Sep 2026 05:25:55 +0000</pubDate>
</item>
<item>
<title>Frameworks Are Institutional Memory</title>
<link>https://coderlegion.com/27544/frameworks-are-institutional-memory</link>
<description>&lt;p&gt;&lt;em&gt;I stored passwords in plaintext on a floppy disk in the 1980s. Recently, a coding agent made a version of the same mistake.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;When I was running bulletin board systems in the 1980s, password management could be remarkably straightforward.&lt;/p&gt;
&lt;p&gt;A user created an account. They chose a password. The BBS needed to determine whether the password they entered later was the same one they had chosen.&lt;/p&gt;
&lt;p&gt;So you stored it.&lt;/p&gt;
&lt;p&gt;In plaintext.&lt;/p&gt;
&lt;p&gt;On a floppy disk.&lt;/p&gt;
&lt;p&gt;Hard drives were expensive and hardly universal in the hobbyist computing world I inhabited, so there was nothing strange about application data living on floppies. The important thing was that the software could retrieve the password and compare it against whatever the user typed the next time they called in.&lt;/p&gt;
&lt;p&gt;Something like this was perfectly understandable:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;KEN|swordfish
SARAH|password123
MIKE|enterprise
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There was, of course, a slight problem.&lt;/p&gt;
&lt;p&gt;The person running the BBS could read everybody's passwords.&lt;/p&gt;
&lt;p&gt;So people started solving that. Maybe the password should not be stored exactly as the user entered it. A substitution cipher or some other homegrown transformation could at least stop someone from casually opening the user file and reading every credential.&lt;/p&gt;
&lt;p&gt;Problem solved.&lt;/p&gt;
&lt;p&gt;Well. One problem solved.&lt;/p&gt;
&lt;p&gt;The transformation might be reversible. Two users choosing the same password produced the same stored value. A compromised file exposed everyone at once. Users reused those passwords elsewhere. Password recovery introduced an entirely separate collection of problems.&lt;/p&gt;
&lt;p&gt;Each solution exposed another question.&lt;/p&gt;
&lt;p&gt;Over the following decades, &quot;store a password&quot; accumulated an extraordinary amount of engineering knowledge.&lt;/p&gt;
&lt;p&gt;Today, saying an application needs authentication describes far more than comparing one string against another. It means hashing, salts, password reset, expiring tokens, single-use tokens, session invalidation, account enumeration, brute-force protection, cookie attributes, CSRF, rate limiting, and a long list of other concerns depending on the threat model.&lt;/p&gt;
&lt;p&gt;A developer can still build all of that from scratch.&lt;/p&gt;
&lt;p&gt;The more interesting question is whether they should.&lt;/p&gt;
&lt;h3 id=&quot;the-code-you-don-t-know-you-need&quot;&gt;The Code You Don't Know You Need&lt;/h3&gt;
&lt;p&gt;The first thing developers learn about frameworks is that they save us from writing code.&lt;/p&gt;
&lt;p&gt;That is true, and it undersells them badly.&lt;/p&gt;
&lt;p&gt;A mature framework does not merely contain code somebody else wrote. It contains decisions somebody else already had to make, edge cases somebody else already hit, vulnerabilities somebody else already discovered, and fixes somebody else already learned were necessary.&lt;/p&gt;
&lt;p&gt;A mature framework is partly &lt;strong&gt;institutional memory encoded as software&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Take authentication. The happy path is not conceptually difficult:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;User submits credentials
        ↓
Find account
        ↓
Check password
        ↓
Create session
        ↓
Return success
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A relatively inexperienced developer can understand that flow and implement a version of it.&lt;/p&gt;
&lt;p&gt;Production authentication is not hard because the happy path is hard. It is hard because of everything surrounding the happy path.&lt;/p&gt;
&lt;p&gt;How should the password be stored? What happens after repeated failed attempts? Can an attacker determine whether a username exists by comparing error messages? What happens to existing sessions after a password change? How long should a reset token remain valid? What happens after it is used? Can it be replayed? What attributes belong on the session cookie?&lt;/p&gt;
&lt;p&gt;The developer writing authentication for the first time does not necessarily know to ask any of that.&lt;/p&gt;
&lt;p&gt;A mature framework often does.&lt;/p&gt;
&lt;p&gt;That is a very different kind of value from saving keystrokes.&lt;/p&gt;
&lt;h3 id=&quot;abstractions-change-who-has-to-think&quot;&gt;Abstractions Change Who Has to Think&lt;/h3&gt;
&lt;p&gt;Software has always progressed partly by taking things that required specialized knowledge and putting them behind abstractions.&lt;/p&gt;
&lt;p&gt;Assembly gave me extraordinary control over what the processor did. It was also a tremendous pain. Higher-level languages let us express more sophisticated ideas without manually considering every instruction. C retained substantial control over memory and execution while being far more productive. Later languages and runtimes provided increasingly sophisticated abstractions for memory management, type safety, concurrency primitives, and networking. Libraries and frameworks moved the boundary again. Managed platforms moved it again.&lt;/p&gt;
&lt;p&gt;None of those layers made the layer underneath disappear.&lt;/p&gt;
&lt;p&gt;Your Python application still causes a processor to execute machine instructions. Your Django application still communicates over networks. Your application on a managed platform still runs on computers somewhere.&lt;/p&gt;
&lt;p&gt;What the abstraction changes is &lt;strong&gt;who has to think about those things routinely&lt;/strong&gt;.&lt;/p&gt;
&lt;h3 id=&quot;control-and-responsibility-arrive-together&quot;&gt;Control and Responsibility Arrive Together&lt;/h3&gt;
&lt;p&gt;Developers sometimes discuss control as though it were an unqualified good.&lt;/p&gt;
&lt;p&gt;C gives me more control over memory. Running directly on infrastructure gives me more control than a managed platform. Writing SQL gives me more control than an ORM. Building against browser automation primitives gives me more control than a higher-level testing abstraction.&lt;/p&gt;
&lt;p&gt;All of those statements can be true.&lt;/p&gt;
&lt;p&gt;But control has a twin that gets mentioned far less often: &lt;strong&gt;responsibility&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;If you control memory allocation, you are responsible for memory allocation. If you control your infrastructure, you are responsible for configuring, securing, observing, updating, and troubleshooting it. If you control every selector in an end-to-end test suite, you own every selector when the application changes.&lt;/p&gt;
&lt;p&gt;You could write your inventory API in C. Manage the networking, parse HTTP, handle allocation, implement routing, serialization, authentication, database connectivity, concurrency, and error handling yourself. You would have an enormous amount of control, and you would have volunteered for an enormous number of problems that Flask, FastAPI, Django, Rails, Spring, and others have spent years making uninteresting.&lt;/p&gt;
&lt;p&gt;The trade is not:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;abstraction versus control&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;It is closer to:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;less control and less responsibility versus more control and more responsibility&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Sometimes the additional control is worth the additional responsibility. Sometimes it is not.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.kenwalger.com/blog/wp-content/uploads/2026/09/mermaid-diagram-2026-09-17-091248.png&quot; alt=&quot;Flowchart showing the choice between owning complexity for greater control and engineering responsibility, or using an abstraction while accepting its opinions and constraints. Both paths lead toward building the work that differentiates the product.&quot;&gt;&lt;/p&gt;
&lt;p&gt;Using the abstraction does not make you less of an engineer. Building the lower layer yourself does not automatically make you a better one. The skill is recognizing which problems deserve your attention, while understanding what you surrendered to have the others solved for you.&lt;/p&gt;
&lt;h3 id=&quot;opinions-are-part-of-the-product&quot;&gt;Opinions Are Part of the Product&lt;/h3&gt;
&lt;p&gt;&quot;Opinionated&quot; sometimes gets used as a criticism.&lt;/p&gt;
&lt;p&gt;It certainly can be one. If a framework's opinions conflict with your application's fundamental requirements, you will spend more time fighting it than benefiting from it.&lt;/p&gt;
&lt;p&gt;But an opinion is also a decision you do not have to make. A framework with an established approach to authentication, migrations, forms, routing, validation, sessions, and project structure has removed that many questions from your team's agenda, and mature defaults usually carry years of accumulated experience with them.&lt;/p&gt;
&lt;p&gt;The other side of the bargain is that somebody else has partially decided what your options look like.&lt;/p&gt;
&lt;p&gt;Using MongoDB with Django was a long-running example. Django's data layer was built around assumptions that fit relational databases particularly well, and getting MongoDB underneath it was possible through various third-party approaches, though &quot;interesting&quot; would be a charitable description of that experience. You were making one architectural choice while using a framework whose opinions had been designed around another.&lt;/p&gt;
&lt;p&gt;What happened next supports the argument better than the complaint did. &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/mongodb/django-mongodb-backend&quot;&gt;MongoDB shipped an official Django backend&lt;/a&gt; in public preview in early 2025, and it now handles embedded models, queryable encryption, geospatial lookups, and most of what the third-party packages struggled with. The framework's opinions were not overturned. They were extended, by people willing to do the work of reconciling the document model with Django's assumptions.&lt;/p&gt;
&lt;p&gt;That took years, and it is exactly the process this whole post is about. The accumulated knowledge is the product. It just accumulates slowly.&lt;/p&gt;
&lt;p&gt;So before adopting any framework, do not look only at the complexity it removes. Ask what decisions it makes on your behalf, what assumptions are embedded in those decisions, and how painful things become when your application needs something outside them.&lt;/p&gt;
&lt;p&gt;You can dislike the choices. You can replace some of them. You can decide the framework is wrong for you. What matters is understanding the bargain.&lt;/p&gt;
&lt;h3 id=&quot;every-platform-sells-you-both&quot;&gt;Every Platform Sells You Both&lt;/h3&gt;
&lt;p&gt;Managed platforms made this visible in a way frameworks alone did not.&lt;/p&gt;
&lt;p&gt;Before them, deploying an application meant owning a considerable amount of infrastructure knowledge: somewhere to run it, a way to deploy it, process management, configuration, networking, logging, databases, scaling, and monitoring. Managed platforms did not make any of that cease to exist. They changed who had to own it.&lt;/p&gt;
&lt;p&gt;What is easy to miss is that this was never a choice between vendors. It is a choice available inside every vendor. AWS will happily sell you App Runner, Elastic Beanstalk, Lambda, or Amplify, and it will just as happily sell you EC2 instances, a VPC, load balancers, autoscaling groups, and a pile of IAM policies to assemble yourself. Google Cloud offers Cloud Run and App Engine alongside Compute Engine and GKE. Azure has App Service and Container Apps on one side, virtual machines and AKS on the other.&lt;/p&gt;
&lt;p&gt;Same provider. Same workload. Radically different amounts of responsibility.&lt;/p&gt;
&lt;p&gt;The opinionated paths get you running quickly and constrain what you can do. The assembled paths let you build nearly anything and hand you an operational surface that keeps expanding for as long as the system exists. Most real architectures contain both, which is usually the right answer.&lt;/p&gt;
&lt;p&gt;The constraint is real in either direction. If you need behavior outside the managed model, the abstraction becomes limiting, and there are plenty of workloads where owning the pieces makes more sense. That is not a failure of the abstraction. It is the trade the abstraction offered.&lt;/p&gt;
&lt;p&gt;One asymmetry is worth pricing in advance: the managed path is cheap to enter and expensive to leave. Moving off it means rebuilding the machinery the platform was quietly providing, usually under time pressure, usually at the exact moment the constraint became intolerable. That is not an argument against starting there. It is an argument for knowing which constraint would force the move before you have to make it.&lt;/p&gt;
&lt;p&gt;For most teams the question was never whether their engineers could build and operate infrastructure. It was whether infrastructure was what their customers were paying them to be good at.&lt;/p&gt;
&lt;h3 id=&quot;spend-your-complexity-budget-wisely&quot;&gt;Spend Your Complexity Budget Wisely&lt;/h3&gt;
&lt;p&gt;I think of this as a &lt;strong&gt;complexity budget&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Every team has finite attention. There are only so many engineers, so many hours, and so many systems a group can deeply understand and maintain. Complexity spends that budget.&lt;/p&gt;
&lt;p&gt;Build your own authentication and you have spent some of it on authentication. Operate your own infrastructure and you have spent some of it on infrastructure. Build a custom persistence layer and you have spent some of it on persistence.&lt;/p&gt;
&lt;p&gt;Any of those can be excellent investments when the capability differentiates your product or your requirements genuinely demand the control. Dan McKinley's &quot;&lt;a rel=&quot;nofollow&quot; href=&quot;https://boringtechnology.club/&quot;&gt;Choose Boring Technology&lt;/a&gt;&quot; makes a neighboring argument with innovation tokens: you get about three, so spend them where they matter. The complexity budget is the same instinct pointed at what you build rather than what you adopt.&lt;/p&gt;
&lt;p&gt;&quot;We could build it ourselves&quot; does not establish anything. Engineers can build lots of things.&lt;/p&gt;
&lt;p&gt;The better question is:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;Is this where we want to spend our complexity budget?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;learn-what-s-underneath-anyway&quot;&gt;Learn What's Underneath Anyway&lt;/h3&gt;
&lt;p&gt;None of this argues against learning fundamentals. Quite the opposite.&lt;/p&gt;
&lt;p&gt;Understanding HTTP makes you better at using a web framework. Understanding SQL makes you better at using an ORM. Understanding memory makes you better at diagnosing what a runtime is doing.&lt;/p&gt;
&lt;p&gt;Every abstraction eventually leaks. When it does, knowing what is underneath tells you whether you are looking at a bug, a limitation, a bad assumption, or the consequence of a trade you made a year ago.&lt;/p&gt;
&lt;p&gt;But there is a difference between &lt;strong&gt;understanding a layer&lt;/strong&gt; and &lt;strong&gt;taking responsibility for operating it&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;I do not need to fabricate a processor to understand how one works. I do not need to write an HTTP server in C to understand HTTP. I do not need to implement my own password hashing to understand why plaintext was a bad idea.&lt;/p&gt;
&lt;p&gt;Sometimes understanding a problem thoroughly is precisely why you decide not to implement it yourself.&lt;/p&gt;
&lt;h3 id=&quot;the-password-problem-never-really-went-away&quot;&gt;The Password Problem Never Really Went Away&lt;/h3&gt;
&lt;p&gt;Recently I had a coding agent implement password reset for a small application.&lt;/p&gt;
&lt;p&gt;It built the feature in about five minutes. The email arrived. The link opened. The password changed. The user logged in with the new one.&lt;/p&gt;
&lt;p&gt;Everything worked.&lt;/p&gt;
&lt;p&gt;Except the reset link worked a second time.&lt;/p&gt;
&lt;p&gt;The implementation satisfied the obvious behavior while missing the invariant underneath it: once the reset succeeds, the token has to become useless.&lt;/p&gt;
&lt;p&gt;That felt familiar.&lt;/p&gt;
&lt;p&gt;Forty years ago the question was:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Does the password let the right person log into the BBS?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Yes.&lt;/p&gt;
&lt;p&gt;Then somebody asked:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Can the sysop read everyone's password?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Oh.&lt;/p&gt;
&lt;p&gt;Years later:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Is the stored password protected?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Yes.&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Can the stored representation be reversed or cheaply cracked?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Oh.&lt;/p&gt;
&lt;p&gt;And now:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Does the password reset feature work?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Yes.&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Can I reuse the token?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Oh.&lt;/p&gt;
&lt;p&gt;The technologies change. The pattern does not. We implement the requirements we know about, and experience introduces us to the ones we did not.&lt;/p&gt;
&lt;p&gt;That is why mature abstractions matter. They carry some of that experience forward so the next developer inherits the lesson without personally reliving it. Somebody already hit the edge case. Somebody already found the vulnerability. Somebody already spent three days on the concurrency bug that only appears on Tuesdays.&lt;/p&gt;
&lt;p&gt;Which leaves an open question I do not think we have answered yet.&lt;/p&gt;
&lt;p&gt;A mature framework carries its institutional memory in its decisions. Sometimes the reasons stay attached through issues, commits, documentation, and design discussions. Often they do not, and only the resulting constraint survives. Either way, somebody encountered the problem and changed the system because of it.&lt;/p&gt;
&lt;p&gt;That is the part worth noticing. The single-use token check runs whether or not anyone remembers why it was added. The lesson is enforced rather than remembered.&lt;/p&gt;
&lt;p&gt;A model trained on a very large corpus of code has absorbed something that resembles institutional memory. But it learned from artifacts produced after those decisions were made, rather than from the decisions themselves. It can reproduce what surviving code looks like without inheriting anything that enforces why one implementation survived and another did not.&lt;/p&gt;
&lt;p&gt;Which might explain why my agent wrote a flawless happy path and dropped the invariant.&lt;/p&gt;
&lt;p&gt;Happy paths are abundant. Invariants live in the decisions.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/27544/frameworks-are-institutional-memory</guid>
<pubDate>Thu, 17 Sep 2026 18:34:00 +0000</pubDate>
</item>
<item>
<title>Your Reads Should Not Design Your Writes</title>
<link>https://coderlegion.com/27424/your-reads-should-not-design-your-writes</link>
<description>&lt;p&gt;Any API endpoint that updates part of a record has to decide what a missing field means, whether the client left it alone or wants it cleared. Most business APIs face that decision constantly, because records like customers, orders, and accounts are edited through forms and clients that rarely send everything. Few problems have left me as dizzy as this one, partly because it's hard, but mostly because of how many competing solutions keep getting promoted for it, from patch formats to field masks to change-tracking client libraries.&lt;/p&gt;
&lt;p&gt;Most of those solutions ask every client to get something subtle right, when most teams need a write surface any client can call correctly on the first try. I think the simplest way there is PUT, where every body must include every field and the server has nothing left to guess.&lt;/p&gt;
&lt;p&gt;That only works once a resource is small enough for any client to send whole, so the change that matters is to stop letting the shape of your reads design your writes. When a group of fields has a single owner, when a change starts a workflow, or when a collection's items come and go individually, that group, change, or collection gets its own path. That can look like a workaround, but I'd argue it's healthy normalization of the write surface.&lt;/p&gt;
&lt;h3 id=&quot;write-intentions-exceed-request-semantics&quot;&gt;Write Intentions Exceed Request Semantics&lt;/h3&gt;
&lt;p&gt;Each field in a write body carries one of four intents:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Leave alone.&lt;/strong&gt; The field isn't part of this change.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Set a value.&lt;/strong&gt; Including zero, empty, or false.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Clear.&lt;/strong&gt; Remove the current value.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Change one element of a collection.&lt;/strong&gt; Add or remove an item without resending the rest.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The examples use one customer, which the API reads whole:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-http&quot;&gt;GET /customers/42
{
  &quot;displayName&quot;: &quot;Ada&quot;,
  &quot;phoneNumber&quot;: &quot;555-1234&quot;,
  &quot;email&quot;: &quot;*Emails are not allowed*&quot;,
  &quot;verified&quot;: true,
  &quot;isActive&quot;: true,
  &quot;tags&quot;: [&quot;vip&quot;, &quot;beta&quot;]
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;By convention, updates to &lt;code&gt;/customers/42&lt;/code&gt; accept that same shape, whether they arrive as PATCH, PUT, or POST. JSON can express the first three intents for &lt;code&gt;phoneNumber&lt;/code&gt; by sending a value, sending &lt;code&gt;null&lt;/code&gt;, or leaving it out. But most servers read the body into a fixed model where missing and &lt;code&gt;null&lt;/code&gt; become the same empty value, so the endpoint has to pick a rule, and the rule decides which two intents survive:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;Body &lt;/th&gt;&lt;th&gt;Replace the resource &lt;/th&gt;&lt;th&gt;Skip nulls&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;&quot;phoneNumber&quot;: &quot;555-1234&quot;&lt;/code&gt; &lt;/td&gt;&lt;td&gt;Set &lt;/td&gt;&lt;td&gt;Set&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;&quot;phoneNumber&quot;: null&lt;/code&gt; &lt;/td&gt;&lt;td&gt;Clear &lt;/td&gt;&lt;td&gt;Leave alone&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;phoneNumber&lt;/code&gt; omitted &lt;/td&gt;&lt;td&gt;Clear &lt;/td&gt;&lt;td&gt;Leave alone&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Skipping nulls gives up clear, so a phone number, once set, can never be removed. Teams often work around that one field at a time, with an empty string that means clear or a separate &lt;code&gt;clearPhoneNumber&lt;/code&gt; flag, and the next nullable field breaks the same way. Replacing gives up leave alone, which is safe only while every client sends every field. A view model that loads only the fields its screen shows breaks that silently, clearing everything else on save. The fourth intent doesn't fit a single field at all, because &lt;code&gt;tags&lt;/code&gt; can only be sent whole.&lt;/p&gt;
&lt;h3 id=&quot;clients-hold-state-not-changes&quot;&gt;Clients Hold State, Not Changes&lt;/h3&gt;
&lt;p&gt;A handler can fix the server side by reading the raw document or accepting a patch format that encodes intent. But a partial update is only as correct as the client that built it, and the client has to know which fields the user touched.&lt;/p&gt;
&lt;p&gt;The form often knows, since libraries like Angular's reactive forms and React Hook Form track dirty fields. But the form doesn't send the request. A view model maps its values onto a DTO, a service layer passes that along, and a generated SDK type turns an unset property into a default or a null. By the time the request leaves, &quot;the user didn't touch this&quot; has been flattened into a value, and preserving it means threading change tracking through every layer of every client, including partners'.&lt;/p&gt;
&lt;p&gt;Tools that try to recover it each leave a gap. Microsoft's Kiota generator tracks set properties in a backing store, but only for code that writes to the SDK object directly, which a hand-written mapper doesn't. Diffing the loaded document against the outgoing one shows a mapper's defaults as changes the user never made. Only a client that snapshots its own mapped copy at load avoids that.&lt;/p&gt;
&lt;p&gt;One workaround skips change tracking. Each client sends only the fields its form owns, so the contact form sends &lt;code&gt;{ displayName, phoneNumber }&lt;/code&gt; and the handler checks which keys are present in the raw JSON. That works while every client scopes its bodies the same way.&lt;/p&gt;
&lt;p&gt;But nothing on the server enforces the grouping. The endpoint accepts &lt;code&gt;isActive&lt;/code&gt; from any caller that includes it. The handler decides field by field who may set what and what each change triggers. And a typed SDK that models the whole customer can lose presence again on the way out. The form scope is the right boundary, held in the wrong place, by convention in each client instead of by the server.&lt;/p&gt;
&lt;h3 id=&quot;patch-formats-and-design-guides-leave-the-client-problem-in-place&quot;&gt;Patch Formats and Design Guides Leave the Client Problem in Place&lt;/h3&gt;
&lt;p&gt;The standards never settled partial updates. RFC 5789, which defined PATCH in 2010, left the body format open on the expectation that &quot;no single format will be appropriate for all types of resources.&quot; Two kinds of answer grew into that gap, standardized patch formats and the API design guides large companies publish, and teams tend to adopt either one as the professional answer.&lt;/p&gt;
&lt;p&gt;JSON Patch, defined in RFC 6902, encodes all four intents explicitly, as add, remove, and replace operations addressed by path. A path into an array is an index, though, so removing &lt;code&gt;beta&lt;/code&gt; means removing &lt;code&gt;/tags/1&lt;/code&gt;, which deletes the wrong tag if someone reordered the array first, unless a &lt;code&gt;test&lt;/code&gt; operation guards it. JSON Merge Patch, from RFC 7396, is simpler and covers three, but replaces arrays whole. Neither can tell a client which fields changed, so adopting one hands every caller the change-tracking problem.&lt;/p&gt;
&lt;p&gt;Design guides carry a different risk, because they aren't neutral standards but one company's internal practice, published. Google's API Improvement Proposals do move state changes into custom methods. But their field masks are built around protobuf messages and Google's generated clients. The client still has to fill each mask with the fields that changed. A server can pin each mask to one form's fields, but that groups fields by form without giving the group a URL for gateways, logs, and ETags to key on.&lt;/p&gt;
&lt;p&gt;Even Microsoft doesn't offer its REST API Guidelines as universal. It publishes them hoping other organizations will &quot;create guidelines that are appropriate for them.&quot;&lt;/p&gt;
&lt;p&gt;Those choices fit organizations that own their clients, generate their SDKs, and employ governance teams to enforce conformance. Copied into a team without that, they can become ritual, kept because a large company published them rather than because they solve anything the team has.&lt;/p&gt;
&lt;p&gt;Partial updates still have a home in documents, data with no fixed model, rather than records. A preferences bag that gains keys every release suits JSON Merge Patch, and because nothing maps it onto a fixed model, a present, null, or absent key still carries all three intents. A large configuration document suits JSON Patch. The rest of this post is about records.&lt;/p&gt;
&lt;h3 id=&quot;the-wide-write-is-a-normalization-failure&quot;&gt;The Wide Write Is a Normalization Failure&lt;/h3&gt;
&lt;p&gt;The shared update body mirrors the read, so the write has to have an opinion about every field, including &lt;code&gt;verified&lt;/code&gt;, which no client should set, and &lt;code&gt;isActive&lt;/code&gt;, which most callers shouldn't touch.&lt;/p&gt;
&lt;p&gt;A read is allowed to be a composition, and serving the whole customer in one GET is good design, but it's a view over things with different rules. The wide write happens when that view becomes the write target, usually because one model serves both directions. It fails the way an unnormalized table does, by giving unrelated facts a single place to be written. That's a governance gap more than a technical one. Nobody decided what the write surface should be, so the read model decided for them.&lt;/p&gt;
&lt;p&gt;PUT on the wide customer doesn't rescue it. RFC 9110 defines PUT as replacing &quot;the state of the target resource&quot; with &quot;the state defined by the representation enclosed.&quot; That only works when every client can produce every field and is entitled to replace each one, which only a small resource can promise.&lt;/p&gt;
&lt;h3 id=&quot;normalize-the-write-surface&quot;&gt;Normalize the Write Surface&lt;/h3&gt;
&lt;p&gt;A normalized write surface writes each field where its owner, permission, and workflow live, while reads compose those resources into views for each screen. It's the read and write split of CQRS, with both sides left as ordinary HTTP resources.&lt;/p&gt;
&lt;h4 id=&quot;three-tests-decide-what-shares-a-resource&quot;&gt;Three Tests Decide What Shares a Resource&lt;/h4&gt;
&lt;p&gt;Two fields belong in the same writable resource only when they pass all three tests:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Same owner.&lt;/strong&gt; The same party is entitled to decide both values, whoever submits the change for them.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Same authorization scope.&lt;/strong&gt; A caller needs the same permission to change either one.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Same workflow.&lt;/strong&gt; Changing either one triggers the same consequences, or none.&lt;/li&gt;
&lt;/ul&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;Field &lt;/th&gt;&lt;th&gt;Owner &lt;/th&gt;&lt;th&gt;Authorization scope &lt;/th&gt;&lt;th&gt;Workflow &lt;/th&gt;&lt;th&gt;Write&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;displayName&lt;/code&gt; &lt;/td&gt;&lt;td&gt;Customer &lt;/td&gt;&lt;td&gt;Edit own profile &lt;/td&gt;&lt;td&gt;None &lt;/td&gt;&lt;td&gt;&lt;code&gt;PUT /customers/42/profile&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;phoneNumber&lt;/code&gt; &lt;/td&gt;&lt;td&gt;Customer &lt;/td&gt;&lt;td&gt;Edit own profile &lt;/td&gt;&lt;td&gt;None &lt;/td&gt;&lt;td&gt;&lt;code&gt;PUT /customers/42/profile&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;email&lt;/code&gt; &lt;/td&gt;&lt;td&gt;Customer &lt;/td&gt;&lt;td&gt;Edit own profile &lt;/td&gt;&lt;td&gt;Verification email &lt;/td&gt;&lt;td&gt;&lt;code&gt;PUT /customers/42/email&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;verified&lt;/code&gt; &lt;/td&gt;&lt;td&gt;Server &lt;/td&gt;&lt;td&gt;Not client-writable &lt;/td&gt;&lt;td&gt;Set by verification &lt;/td&gt;&lt;td&gt;None&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;isActive&lt;/code&gt; &lt;/td&gt;&lt;td&gt;Operations &lt;/td&gt;&lt;td&gt;Manage account status &lt;/td&gt;&lt;td&gt;Deactivation checks &lt;/td&gt;&lt;td&gt;&lt;code&gt;POST /customers/42/deactivate&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;tags&lt;/code&gt; &lt;/td&gt;&lt;td&gt;Support &lt;/td&gt;&lt;td&gt;Manage tags &lt;/td&gt;&lt;td&gt;None &lt;/td&gt;&lt;td&gt;&lt;code&gt;POST&lt;/code&gt; and &lt;code&gt;DELETE /customers/42/tags&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;code&gt;displayName&lt;/code&gt; and &lt;code&gt;phoneNumber&lt;/code&gt; pass all three and share a resource.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;email&lt;/code&gt; fails only the workflow test, and that's enough. The verification rules then live on the one endpoint that triggers them, and a PUT that resends the current address triggers nothing.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;isActive&lt;/code&gt; fails all three. Changing it is a state transition with its own checks, so it becomes a named operation. A deactivation can no longer ride along with a phone-number change, because there's no body to carry it.&lt;/p&gt;
&lt;p&gt;The one wide body becomes these requests:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-http&quot;&gt;PUT /customers/42/profile
{ &quot;displayName&quot;: &quot;Ada&quot;, &quot;phoneNumber&quot;: null }

PUT /customers/42/email
{ &quot;email&quot;: &quot;*Emails are not allowed*&quot; }

POST /customers/42/tags
{ &quot;name&quot;: &quot;vip&quot; }

DELETE /customers/42/tags/beta

POST /customers/42/deactivate
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The server requires every field in each body, so a body that leaves one out fails with a 400, and no intent depends on the server guessing:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;Intent &lt;/th&gt;&lt;th&gt;Request&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;Set &lt;code&gt;phoneNumber&lt;/code&gt; &lt;/td&gt;&lt;td&gt;&lt;code&gt;PUT /customers/42/profile&lt;/code&gt; with &lt;code&gt;&quot;phoneNumber&quot;: &quot;555-1234&quot;&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Clear &lt;code&gt;phoneNumber&lt;/code&gt; &lt;/td&gt;&lt;td&gt;&lt;code&gt;PUT /customers/42/profile&lt;/code&gt; with &lt;code&gt;&quot;phoneNumber&quot;: null&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Leave &lt;code&gt;phoneNumber&lt;/code&gt; alone &lt;/td&gt;&lt;td&gt;Skip the profile PUT, or resend its current value&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Remove one tag &lt;/td&gt;&lt;td&gt;&lt;code&gt;DELETE /customers/42/tags/beta&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;A mapper can still turn a value into a null the user never chose. That error doesn't vanish, but when a screen loads its section whole, the mapper can only null fields the client fetched. Skipping a PUT means tracking which sections were touched, one flag per section rather than per field. Merge Patch on the same small resource would keep leave alone, but only by handing back the field-level tracking the mapper layers lose.&lt;/p&gt;
&lt;h4 id=&quot;collections-with-identity-get-their-own-addresses&quot;&gt;Collections With Identity Get Their Own Addresses&lt;/h4&gt;
&lt;p&gt;Collections whose elements carry identity get addressed individually, the way GitHub's REST API handles issue labels and Stripe's API exposes subscription items.&lt;/p&gt;
&lt;p&gt;A collection with no natural key, like an ordered list of steps, stays in its parent's body and is replaced whole with it.&lt;/p&gt;
&lt;h3 id=&quot;what-normalizing-costs&quot;&gt;What Normalizing Costs&lt;/h3&gt;
&lt;h4 id=&quot;more-calls-usually-along-lines-the-work-already-follows&quot;&gt;More Calls, Usually Along Lines the Work Already Follows&lt;/h4&gt;
&lt;p&gt;The objection is usually raised for admin and operations apps, on the assumption that they edit everything at once. Many don't. Their work moves from correcting contact details to reviewing access to deactivating an account, and each step is a write this design already names.&lt;/p&gt;
&lt;p&gt;The customer screen is the harder case, because it shows every concern on one page. A single Save button now makes one call per concern it touches plus one per tag, and has to report which ones failed.&lt;/p&gt;
&lt;p&gt;Shaping the screen like the write surface is a design choice, but common controls already lean that way. Nielsen Norman Group's toggle-switch guidelines say a switch &quot;should take immediate effect and should not require the user to click Save or Submit,&quot; and tag chips can save on each click the same way. A single Save that carries a deactivation along with a phone-number edit is the wide write again, moved into the interface.&lt;/p&gt;
&lt;p&gt;Call count does hurt clients that write in volume, like an offline app syncing a day of edits, a bulk-edit grid, or an importer loading thousands of customers. They need a batch endpoint, not the wide body back. In the style of Microsoft Graph's JSON batching, each entry carries its own method, URL, and body and gets its own status. Each one passes through the rules of the endpoint it names, without quietly turning the batch into a transaction.&lt;/p&gt;
&lt;h4 id=&quot;atomicity-across-concerns-has-to-be-named&quot;&gt;Atomicity Across Concerns Has to Be Named&lt;/h4&gt;
&lt;p&gt;A single wide update usually applies all or nothing, and separate calls don't, so a save across contact details and tags can fail halfway. As long as the server validates each write against current state, every successful call leaves a valid customer, so a failure leaves an incomplete edit rather than a corrupt record.&lt;/p&gt;
&lt;p&gt;Separate calls stop being enough when writes are only valid together. Closing an account deactivates the customer and cancels their subscriptions, and a deactivated customer who's still billed has to be impossible. That's a use case, and it gets a named operation, &lt;code&gt;POST /customers/42/close&lt;/code&gt;. Rules linking two fields follow the owners. A shipping address needs a postal code, and one owner holds both, so they share a resource. An enterprise tier requires a phone number, but the owners differ. A downgrade that also drops the phone number changes both fields together, so it gets a named operation. Listing every rule that reads more than one section finds these cases.&lt;/p&gt;
&lt;p&gt;The wide update never removed those actions. It hid them in field values, so &lt;code&gt;&quot;isActive&quot;: false&lt;/code&gt; is a deactivation command the handler detects by comparing against the stored value. Naming them isn't a return to RPC, the endpoint-per-action style that resource-oriented APIs replaced. A normalized surface is still mostly GET and PUT on resources, with named operations kept for transitions, the way Stripe's API finalizes invoices and GitHub's API merges pull requests. An operation earns a name when the business needs its writes to succeed together, not when a screen happens to save several things at once.&lt;/p&gt;
&lt;h4 id=&quot;concurrent-edits-surface-as-conflicts-instead-of-merging&quot;&gt;Concurrent Edits Surface as Conflicts Instead of Merging&lt;/h4&gt;
&lt;p&gt;If one agent changes &lt;code&gt;displayName&lt;/code&gt; while another changes &lt;code&gt;phoneNumber&lt;/code&gt;, two PUTs to the profile each send both fields, and the later one overwrites the earlier change. A partial update would keep both, but only with the change tracking clients rarely manage, and even then a merge can pair a new contact's name with the old contact's phone.&lt;/p&gt;
&lt;p&gt;The usual guard is a version check. A GET on the profile returns a version tag in its &lt;code&gt;ETag&lt;/code&gt; header, and the client sends that tag back in an &lt;code&gt;If-Match&lt;/code&gt; header on the PUT. If the resource changed in between, the server answers &lt;code&gt;412 Precondition Failed&lt;/code&gt; instead of overwriting. An edit screen loads each section from its own GET, so it resends that resource's own representation and version. A server that requires the header answers a missing one with &lt;code&gt;428 Precondition Required&lt;/code&gt;, defined in RFC 6585.&lt;/p&gt;
&lt;p&gt;Small resources make that check practical. On the wide customer, a tag another agent adds fails every save that loaded the customer before it. A batch replaying a day of offline edits gets 412s on its stale entries to refetch and reapply, which is where offline-first apps may need field merging. Outside offline sync, a 412 on the profile means someone changed the same details at the same time, which is rare and should reach the user.&lt;/p&gt;
&lt;h4 id=&quot;adding-a-writable-field-changes-the-contract&quot;&gt;Adding a Writable Field Changes the Contract&lt;/h4&gt;
&lt;p&gt;If &lt;code&gt;preferredName&lt;/code&gt; joins the profile, an older client's PUT leaves it out, and the server either clears it or rejects the request. So the field comes with a new version of the resource, which each request declares in its path or media type. Older clients keep sending the old version's request, and the server leaves &lt;code&gt;preferredName&lt;/code&gt; alone for those.&lt;/p&gt;
&lt;p&gt;A partial update skips the version by treating missing as leave alone, the rule that stopped the phone number from being cleared. Normalizing limits versions instead. Reads gain fields freely, and a field with its own owner, scope, or workflow gets its own resource. A resource that still gains writable fields every release may be better served by partial updates. For the rest, the versions that remain are the price of a body the server never guesses about.&lt;/p&gt;
&lt;h4 id=&quot;migration-runs-alongside-the-wide-write&quot;&gt;Migration Runs Alongside the Wide Write&lt;/h4&gt;
&lt;p&gt;Partners already calling the wide update can't simply start getting 405. The narrow endpoints go in alongside it, and clients move over screen by screen. Meanwhile the wide update has to route each field through its narrow endpoint's rules, or it becomes a back door, and it keeps its old missing-versus-null ambiguity until its last client moves. Once its traffic stops, &lt;code&gt;/customers/42&lt;/code&gt; keeps serving the composed view and answers writes with 405 and &lt;code&gt;Allow: GET&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&quot;three-failures-behind-one-wide-write&quot;&gt;Three Failures Behind One Wide Write&lt;/h3&gt;
&lt;p&gt;I've come to think most software problems trace back to a failure to align with the need, a failure of architecture governance, or a failure to change direction. The wide write and the usual fixes for it show all three. Teams adopt a design guide written for someone else's clients. They let the read model decide a write surface nobody owned. And they special-case each ambiguous field instead of changing the design.&lt;/p&gt;
&lt;p&gt;None of the corrections takes new technology:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Group fields into a writable resource only when they share an owner, an authorization scope, and a workflow&lt;/li&gt;
&lt;li&gt;Replace those resources whole with PUT, requiring every field in the body&lt;/li&gt;
&lt;li&gt;Give collections with identity-bearing elements their own POST and DELETE endpoints&lt;/li&gt;
&lt;li&gt;Give the operation its own endpoint when a change is a state transition or has to be atomic across concerns&lt;/li&gt;
&lt;li&gt;Require &lt;code&gt;If-Match&lt;/code&gt; on PUTs, because resending a value read earlier is how a client leaves a field alone&lt;/li&gt;
&lt;li&gt;Version a writable resource when it gains a field, and let reads grow freely&lt;/li&gt;
&lt;li&gt;Move existing clients onto the narrow endpoints, then let aggregate URLs answer GET and refuse writes&lt;/li&gt;
&lt;li&gt;Save partial updates and patch formats for documents, like preference bags and large configurations&lt;/li&gt;
&lt;/ul&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/27424/your-reads-should-not-design-your-writes</guid>
<pubDate>Wed, 16 Sep 2026 17:00:01 +0000</pubDate>
</item>
<item>
<title>If you're hiring in this AI era, would you give Junior devs a chance?</title>
<link>https://coderlegion.com/27394/if-youre-hiring-in-this-ai-era-would-you-give-junior-devs-a-chance</link>
<description>&lt;p&gt;So I shared about the &lt;a rel=&quot;nofollow&quot; href=&quot;https://coderlegion.com/27322/learn-to-code-from-scratch-or-build-with-ai-a-junior-devs-dillema&quot;&gt;Junior dev's dilemma&lt;/a&gt; (in this AI era) a few days ago. And last night while on twitter I came across this tweet by the youtube legends. It was interesting to see that they share this sentiment.&lt;/p&gt;
&lt;p&gt;However, I'm curious to know what a hiring manager's perspective of this would be. &lt;/p&gt;
&lt;p&gt;If you're a founder or hiring, would you give a junior in this era a chance? And what would your expections of them be?&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://coderlegion.com/?qa=blob&amp;amp;qa_blobid=8461297652374010745&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
</description>
<category>Discussions</category>
<guid isPermaLink="true">https://coderlegion.com/27394/if-youre-hiring-in-this-ai-era-would-you-give-junior-devs-a-chance</guid>
<pubDate>Wed, 16 Sep 2026 13:31:09 +0000</pubDate>
</item>
<item>
<title>7 Leading Warehouse Management Software Development Companies in 2026</title>
<link>https://coderlegion.com/27256/7-leading-warehouse-management-software-development-companies-in-2026</link>
<description>&lt;p&gt;Warehouses now handle far more than storing and moving products. Inventory accuracy, order fulfilment, barcode scanning, picking, packing, replenishment, and shipment coordination all depend on timely information.&lt;/p&gt;
&lt;p&gt;As operations grow, disconnected spreadsheets and manual updates can make everyday warehouse work harder to manage. Choosing warehouse management software development companies therefore requires attention to workflows, integrations, automation, scalability, and the practical needs of warehouse teams.&lt;/p&gt;
&lt;h3 id=&quot;how-we-selected-these-warehouse-management-software-development-companies&quot;&gt;How We Selected These Warehouse Management Software Development Companies&lt;/h3&gt;
&lt;p&gt;We compared these companies primarily on technical capability and compliance readiness, along with real client reviews.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Client Reviews and Reputation:&lt;/strong&gt; Public ratings and verified reviews on platforms such as Google and Clutch provide signals about communication, delivery quality, and consistency across past engagements.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Technical Skillset:&lt;/strong&gt; Assessed web, mobile, API, cloud, data engineering, IoT, and automation capabilities relevant to warehouse platforms.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Warehouse and Logistics Experience:&lt;/strong&gt; Reviewed prior work involving inventory, warehouse operations, order management, transportation, or related enterprise software.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Security and Compliance Readiness:&lt;/strong&gt; Evaluated access controls, authentication, secure integrations, and data protection practices given how warehouse systems connect employees, suppliers, and carriers.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Value and Delivery Fit:&lt;/strong&gt; Considered pricing alongside technical capability, project experience, and delivery scope rather than hourly rate alone.&lt;/p&gt;
&lt;h3 id=&quot;top-7-warehouse-management-software-development-partners&quot;&gt;Top 7 Warehouse Management Software Development Partners&lt;/h3&gt;
&lt;p&gt;The companies below represent different approaches to custom software development, logistics technology, enterprise systems, and digital products. Your ideal choice will depend on warehouse complexity, existing systems, automation requirements, and expected scale.&lt;/p&gt;
&lt;blockquote&gt;&lt;ol&gt;
&lt;li&gt;Citrusbug Technolabs&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://citrusbug.com/&quot;&gt;Citrusbug Technolabs&lt;/a&gt; builds custom warehouse management software development solutions around inventory tracking, order processing, and everyday warehouse workflows. &lt;/p&gt;
&lt;p&gt;The team has &lt;a rel=&quot;nofollow&quot; href=&quot;https://citrusbug.com/logistics-software-development-company/&quot;&gt;built logistics software&lt;/a&gt; platforms that connect warehouse operations with broader supply chain systems, mobile apps, and operational dashboards. &lt;/p&gt;
&lt;p&gt;Its engineers shape each build around real operational processes rather than forcing teams into a fixed template. This custom approach extends to automation, integrations, and scalable cloud architecture.&lt;/p&gt;
&lt;h3 id=&quot;core-strengths&quot;&gt;Core Strengths&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Custom warehouse platforms shaped around real receiving, picking, and&lt;br&gt;
dispatch workflows&lt;/li&gt;
&lt;li&gt;Mobile and dashboard development for warehouse floor and management&lt;br&gt;
teams&lt;/li&gt;
&lt;li&gt;Integration experience connecting warehouse systems with broader&lt;br&gt;
logistics platforms&lt;/li&gt;
&lt;li&gt;Automation support for replenishment, task assignment, and workflow&lt;br&gt;
triggers&lt;/li&gt;
&lt;li&gt;Scalable cloud architecture built for growing operations&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;why-choose-them&quot;&gt;Why Choose Them&lt;/h3&gt;
&lt;p&gt;Citrusbug is a strong fit for teams that want a warehouse platform tailored to their exact process instead of a rigid off-the-shelf product. Its combination of custom engineering and integration experience supports both simple and complex warehouse setups.&lt;/p&gt;
&lt;blockquote&gt;&lt;ol&gt;
&lt;li&gt;Cleveroad&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://www.cleveroad.com/&quot;&gt;Cleveroad&lt;/a&gt; delivers custom software for logistics and supply chain operations, including warehouse management, inventory control, transportation, and order-related workflows. &lt;/p&gt;
&lt;p&gt;Its development scope also covers web, mobile, cloud, and enterprise applications built for connected operations. &lt;/p&gt;
&lt;p&gt;The company positions its warehouse work as part of a broader logistics technology practice. Projects typically combine tailored interfaces with cross-system data exchange.&lt;/p&gt;
&lt;h3 id=&quot;core-strengths-1&quot;&gt;Core Strengths&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Logistics and supply chain software development as a core practice&lt;br&gt;
area&lt;/li&gt;
&lt;li&gt;Cross-platform capability spanning web, mobile, and cloud&lt;/li&gt;
&lt;li&gt;Experience connecting warehouse tools with transportation and&lt;br&gt;
business systems&lt;/li&gt;
&lt;li&gt;Custom interface design built around operational teams&lt;/li&gt;
&lt;li&gt;Enterprise application development for larger warehouse programs&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;why-choose-them-1&quot;&gt;Why Choose Them&lt;/h3&gt;
&lt;p&gt;Cleveroad suits organizations that need warehouse software to communicate closely with transportation and inventory systems. Its broader logistics focus adds useful context beyond a single warehouse module.&lt;/p&gt;
&lt;blockquote&gt;&lt;ol&gt;
&lt;li&gt;ScienceSoft&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://www.scnsoft.com/&quot;&gt;ScienceSoft&lt;/a&gt; provides custom software and consulting services for warehouse and logistics operations, drawing on a long history in enterprise software engineering. &lt;/p&gt;
&lt;p&gt;Its technology capabilities cover inventory management, transportation, analytics, IoT, and integrations with established business systems. &lt;/p&gt;
&lt;p&gt;The company often works with organizations that already run complex operational environments. Its consulting background adds a planning layer ahead of development.&lt;/p&gt;
&lt;h3 id=&quot;core-strengths-2&quot;&gt;Core Strengths&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Long-standing enterprise software engineering background&lt;/li&gt;
&lt;li&gt;Consulting services alongside custom development&lt;/li&gt;
&lt;li&gt;IoT and analytics capability for connected warehouse operations&lt;/li&gt;
&lt;li&gt;Experience integrating with established, complex business systems&lt;/li&gt;
&lt;li&gt;Broad technology portfolio beyond a single warehouse module&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;why-choose-them-2&quot;&gt;Why Choose Them&lt;/h3&gt;
&lt;p&gt;ScienceSoft works well for organizations with an existing, complex technology environment that a new warehouse platform must fit into. Its consulting-first approach helps surface integration issues early.&lt;/p&gt;
&lt;blockquote&gt;&lt;ol&gt;
&lt;li&gt;Innowise Group&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://innowise.com/&quot;&gt;Innowise Group&lt;/a&gt; develops custom software across logistics, supply chain, IoT, cloud, and enterprise applications, giving it a wide base for warehouse-focused projects. Its technology capabilities support inventory tracking, order processing, automation, and connected operational systems. &lt;/p&gt;
&lt;p&gt;The company's multidisciplinary teams can combine mobile, backend, and device-level development within one engagement. This breadth suits warehouse projects that go beyond basic inventory screens.&lt;/p&gt;
&lt;h3 id=&quot;core-strengths-3&quot;&gt;Core Strengths&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Multidisciplinary teams covering mobile, cloud, and IoT development&lt;/li&gt;
&lt;li&gt;Experience across logistics, supply chain, and enterprise software&lt;/li&gt;
&lt;li&gt;Capability to connect physical devices with warehouse software&lt;/li&gt;
&lt;li&gt;Analytics and data engineering support for operational reporting&lt;/li&gt;
&lt;li&gt;Large-team delivery model suited to bigger warehouse programs&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;why-choose-them-3&quot;&gt;Why Choose Them&lt;/h3&gt;
&lt;p&gt;Innowise Group is a good option when a warehouse project needs more than a simple inventory screen, such as connected devices or heavier data engineering. Its broad engineering base covers most warehouse technology needs in one team.&lt;/p&gt;
&lt;blockquote&gt;&lt;ol&gt;
&lt;li&gt;DreamzTech Solutions&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://www.dreamztech.com/&quot;&gt;DreamzTech Solutions&lt;/a&gt; provides custom software development services built around each client's internal workflows and business rules. Its warehouse-relevant work centers on shaping applications to match how a specific operation receives, stores, picks, packs, and dispatches products. &lt;/p&gt;
&lt;p&gt;The company positions this flexibility as useful when off-the-shelf warehouse products fall short of an organization's actual process. Development typically starts from the operational workflow rather than a fixed software template.&lt;/p&gt;
&lt;h3 id=&quot;core-strengths-4&quot;&gt;Core Strengths&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Workflow-first development approach built around existing operations&lt;/li&gt;
&lt;li&gt;Flexibility to match software to non-standard warehouse processes&lt;/li&gt;
&lt;li&gt;Custom application design rather than fixed-template products&lt;/li&gt;
&lt;li&gt;Attention to business rules specific to each client&lt;/li&gt;
&lt;li&gt;Suited to operations that outgrow generic warehouse software&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;why-choose-them-4&quot;&gt;Why Choose Them&lt;/h3&gt;
&lt;p&gt;DreamzTech Solutions fits teams whose warehouse process does not match standard software closely enough. Its workflow-first approach reduces the need to change operations to suit the tool.&lt;/p&gt;
&lt;blockquote&gt;&lt;ol&gt;
&lt;li&gt;Requestum&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://requestum.com/&quot;&gt;Requestum&lt;/a&gt; develops custom software solutions for businesses with specialized operational and technical requirements, including warehouse-adjacent enterprise applications. &lt;/p&gt;
&lt;p&gt;Its engineering services support integrations, digital platforms, and workflows that connect warehouse activity with wider business processes. &lt;/p&gt;
&lt;p&gt;The company frequently builds software that sits between physical operations and enterprise systems. This positioning suits warehouse platforms that must report into larger business tools.&lt;/p&gt;
&lt;h3 id=&quot;core-strengths-5&quot;&gt;Core Strengths&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Experience building software that bridges physical operations and&lt;br&gt;
enterprise systems&lt;/li&gt;
&lt;li&gt;Custom engineering model built around tailored workflows&lt;/li&gt;
&lt;li&gt;System integration and reporting capability&lt;/li&gt;
&lt;li&gt;Interface design reflecting how individual teams actually work&lt;/li&gt;
&lt;li&gt;Enterprise application development beyond a single warehouse module&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;why-choose-them-5&quot;&gt;Why Choose Them&lt;/h3&gt;
&lt;p&gt;Requestum suits organizations that need their warehouse platform to report cleanly into wider enterprise software. Its integration-focused engineering reduces friction between operational and business systems&lt;/p&gt;
&lt;blockquote&gt;&lt;ol&gt;
&lt;li&gt;Attract Group&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://attractgroup.com/&quot;&gt;Attract Group&lt;/a&gt; provides custom web and mobile development for businesses that need tailored digital products, including warehouse-facing applications. Its work centers on operational interfaces, integrations, and platforms designed around specific organizational needs. &lt;/p&gt;
&lt;p&gt;The company emphasizes simple, repeatable interfaces for tasks performed throughout the day. This focus makes it a fit for warehouse teams prioritizing usability on the floor.&lt;/p&gt;
&lt;h3 id=&quot;core-strengths-6&quot;&gt;Core Strengths&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Web and mobile development focused on operational usability&lt;/li&gt;
&lt;li&gt;Interface design built for repetitive, day-to-day warehouse tasks&lt;/li&gt;
&lt;li&gt;Custom product approach rather than generic templates&lt;/li&gt;
&lt;li&gt;Experience aligning software with receiving, picking, and dispatch&lt;br&gt;
steps&lt;/li&gt;
&lt;li&gt;Attention to end-user experience for warehouse floor staff&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;why-choose-them-6&quot;&gt;Why Choose Them&lt;/h3&gt;
&lt;p&gt;Attract Group is a solid choice for organizations that prioritize simple, usable interfaces for warehouse staff. Its product-design focus helps reduce training time and daily friction on the floor.&lt;/p&gt;
&lt;h3 id=&quot;cost-and-roi-how-your-software-partner-choice-affects-results&quot;&gt;Cost and ROI: How Your Software Partner Choice Affects Results&lt;/h3&gt;
&lt;p&gt;The cost of a warehouse management software development project depends on more than a provider's hourly rate. A team without warehouse or logistics experience may quote a lower rate but spend more time on discovery, rework, and workflow corrections once real operational requirements surface, which pushes the total cost above what a more experienced partner would have charged.&lt;/p&gt;
&lt;p&gt;ROI is closely tied to how well the platform fits actual warehouse workflows and integrates with existing systems such as ERP, TMS, and eCommerce tools. A provider that gets these integrations and data flows right early reduces the ongoing cost of fixes and manual workarounds. Weaker execution here often shows up later as duplicated data entry, delayed inventory updates, or staff reverting to spreadsheets alongside the new system.&lt;/p&gt;
&lt;p&gt;Support and ownership terms also shape long-term ROI. Warehouse operations change as businesses add locations, devices, and sales channels, so a partner's approach to maintenance, upgrades, and source-code ownership affects how much future changes will cost. Comparing these terms alongside the initial quote gives a clearer picture of total cost over the life of the platform.&lt;/p&gt;
&lt;h3 id=&quot;how-to-choose-the-best-warehouse-management-software-development-partner&quot;&gt;How to Choose the Best Warehouse Management Software Development Partner&lt;/h3&gt;
&lt;p&gt;Your selection should start with the warehouse processes you want to improve and the systems your new platform must connect.&lt;/p&gt;
&lt;h3 id=&quot;map-your-warehouse-workflows&quot;&gt;Map Your Warehouse Workflows&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Document receiving, putaway, storage, picking, packing, and dispatch&lt;br&gt;
steps&lt;/li&gt;
&lt;li&gt;Include returns and stock adjustment processes in the workflow map&lt;/li&gt;
&lt;li&gt;Note which steps are manual today and which need automation&lt;/li&gt;
&lt;li&gt;Identify bottlenecks in the current process before development starts&lt;/li&gt;
&lt;li&gt;Involve warehouse staff, not just managers, in workflow mapping&lt;/li&gt;
&lt;li&gt;Share the finished workflow map with shortlisted providers&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;define-inventory-and-tracking-requirements&quot;&gt;Define Inventory and Tracking Requirements&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Decide how you need to track SKUs, batches, and serial numbers&lt;/li&gt;
&lt;li&gt;Confirm whether you need location-level or bin-level tracking&lt;/li&gt;
&lt;li&gt;Set clear rules for stock levels, thresholds, and movements&lt;/li&gt;
&lt;li&gt;Identify how many warehouses or locations must be tracked&lt;/li&gt;
&lt;li&gt;Clarify reporting needs for inventory accuracy and discrepancies&lt;/li&gt;
&lt;li&gt;Decide how historical inventory data should be handled or migrated&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;review-integration-and-device-support&quot;&gt;Review Integration and Device Support&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Check whether the provider can connect ERP, eCommerce, and TMS&lt;br&gt;
systems&lt;/li&gt;
&lt;li&gt;Confirm support for accounting, supplier, and carrier integrations&lt;/li&gt;
&lt;li&gt;Verify compatibility with your barcode scanners, tablets, or RFID&lt;br&gt;
equipment&lt;/li&gt;
&lt;li&gt;Ask how integrations are tested before go-live&lt;/li&gt;
&lt;li&gt;Discuss how data conflicts between systems will be handled&lt;/li&gt;
&lt;li&gt;Review API documentation and integration ownership after launch&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;evaluate-automation-and-real-time-visibility&quot;&gt;Evaluate Automation and Real-Time Visibility&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Identify repetitive tasks that could use automated alerts or rules&lt;/li&gt;
&lt;li&gt;Ask about automated replenishment and task assignment capability&lt;/li&gt;
&lt;li&gt;Confirm which warehouse events need real-time updates&lt;/li&gt;
&lt;li&gt;Discuss data freshness requirements for picking and shipment status&lt;/li&gt;
&lt;li&gt;Ask how automation rules can be adjusted after launch&lt;/li&gt;
&lt;li&gt;Review how exceptions and errors are surfaced to warehouse staff&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;confirm-scalability-support-and-ownership&quot;&gt;Confirm Scalability, Support, and Ownership&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Ask how the architecture handles more users, products, and locations&lt;/li&gt;
&lt;li&gt;Confirm the platform can scale to additional order and transaction&lt;br&gt;
volume&lt;/li&gt;
&lt;li&gt;Clarify who handles maintenance, monitoring, and bug fixes after&lt;br&gt;
launch&lt;/li&gt;
&lt;li&gt;Confirm source code and data ownership terms before signing&lt;/li&gt;
&lt;li&gt;Ask about documentation provided at project handover&lt;/li&gt;
&lt;li&gt;Discuss upgrade paths as your warehouse operation grows&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;conclusion-1&quot;&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;Warehouse software connects physical inventory movement with the digital information that teams use to make daily decisions. Its value therefore depends on accuracy, usability, integration, and reliable performance across busy operational environments.&lt;/p&gt;
&lt;p&gt;The right &lt;a rel=&quot;nofollow&quot; href=&quot;https://citrusbug.com/warehouse-management-software-development/&quot;&gt;warehouse management software development partner&lt;/a&gt; will depend on your facility size, fulfilment model, existing technology, automation goals, and integration needs. Use this list as a starting point, then assess each provider against your specific warehouse workflows.&lt;/p&gt;
&lt;h3 id=&quot;frequently-asked-questions&quot;&gt;Frequently Asked Questions&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;What does warehouse management software include?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;It can include receiving, putaway, inventory tracking, picking, packing, dispatch, replenishment, returns, barcode scanning, reporting, and warehouse analytics.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;How long does warehouse software development take?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;A focused warehouse application may take several months, while complex platforms with integrations, automation, and multiple facilities generally require longer.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;How much does warehouse software development cost?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Costs depend on features, integrations, devices, automation, users, warehouse locations, infrastructure, and support requirements.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Can warehouse software integrate with ERP systems?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Yes. Custom warehouse platforms can integrate with ERP, eCommerce, TMS, accounting, supplier, carrier, and other business systems through APIs or other integration methods.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Should warehouse software support barcode scanning?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Barcode support is useful for many warehouses because it can improve inventory identification, receiving, picking, movement tracking, and dispatch accuracy.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Can warehouse management software support multiple locations?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Yes. A properly designed platform can manage multiple warehouses, locations, users, inventory pools, and operational rules within one system.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;What security features should warehouse software have?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Consider role-based access, authentication, secure APIs, encryption, audit logs, backups, monitoring, and controlled permissions for external integrations.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;How can you measure warehouse software success?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Track inventory accuracy, picking time, order fulfilment speed, stock discrepancies, manual work, order errors, warehouse productivity, and processing costs.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/27256/7-leading-warehouse-management-software-development-companies-in-2026</guid>
<pubDate>Tue, 15 Sep 2026 10:40:40 +0000</pubDate>
</item>
<item>
<title>💻 CODER LEGION — How I Finally Connected the Pieces Behind MyZubster</title>
<link>https://coderlegion.com/27014/coder-legion-how-i-finally-connected-the-pieces-behind-myzubster</link>
<description>&lt;p&gt;💻 CODER LEGION — How I Finally Connected the Pieces Behind MyZubster&lt;/p&gt;
&lt;p&gt;For a long time, I had projects that looked completely unrelated.&lt;/p&gt;
&lt;p&gt;Open source. Automation. IoT. Privacy. Crypto. Metaverse. Makers. Community projects. Even kefir fermentation.&lt;/p&gt;
&lt;p&gt;From the outside, it could easily look like too many ideas going in too many directions.&lt;/p&gt;
&lt;p&gt;But eventually I realized something:&lt;/p&gt;
&lt;p&gt;The problem wasn't that these projects were disconnected.&lt;br&gt;
The problem was that I hadn't built the architecture that connected them yet.&lt;/p&gt;
&lt;p&gt;And that's how I got here.&lt;/p&gt;
&lt;p&gt;I stopped thinking about MyZubster as a single application.&lt;/p&gt;
&lt;p&gt;Instead, I started thinking about it as an ecosystem of independent modules.&lt;/p&gt;
&lt;p&gt;A kefir experiment helped make the concept surprisingly clear.&lt;/p&gt;
&lt;p&gt;Take something physical: kefir fermentation.&lt;/p&gt;
&lt;p&gt;Add sensors.&lt;/p&gt;
&lt;p&gt;Now you have data.&lt;/p&gt;
&lt;p&gt;Connect a microcontroller.&lt;/p&gt;
&lt;p&gt;Now you have an IoT node.&lt;/p&gt;
&lt;p&gt;Write software around it.&lt;/p&gt;
&lt;p&gt;Now the data can be stored, visualized and processed.&lt;/p&gt;
&lt;p&gt;Add rules.&lt;/p&gt;
&lt;p&gt;Now you have automation.&lt;/p&gt;
&lt;p&gt;Connect that physical object to a virtual representation.&lt;/p&gt;
&lt;p&gt;Now you have the beginning of a digital twin.&lt;/p&gt;
&lt;p&gt;Put that digital twin inside a navigable environment.&lt;/p&gt;
&lt;p&gt;Now the metaverse isn't just a 3D world anymore.&lt;/p&gt;
&lt;p&gt;It becomes an interface to something happening in the real world.&lt;/p&gt;
&lt;p&gt;The architecture suddenly looks like this:&lt;/p&gt;
&lt;p&gt;Physical World → Sensors → IoT → Data → Automation → Digital Twin → Metaverse&lt;/p&gt;
&lt;p&gt;But there was still another problem.&lt;/p&gt;
&lt;p&gt;Who builds all of this?&lt;/p&gt;
&lt;p&gt;One developer cannot realistically be an expert in everything.&lt;/p&gt;
&lt;p&gt;And that's when another piece clicked.&lt;/p&gt;
&lt;p&gt;Subcultures are already distributed knowledge networks.&lt;/p&gt;
&lt;p&gt;The maker understands hardware.&lt;/p&gt;
&lt;p&gt;The backend developer understands APIs and infrastructure.&lt;/p&gt;
&lt;p&gt;The frontend developer builds interfaces.&lt;/p&gt;
&lt;p&gt;The automation enthusiast connects workflows.&lt;/p&gt;
&lt;p&gt;The 3D artist creates virtual environments.&lt;/p&gt;
&lt;p&gt;The game developer understands interaction.&lt;/p&gt;
&lt;p&gt;The privacy community asks difficult questions about data collection.&lt;/p&gt;
&lt;p&gt;The cypherpunk questions centralization.&lt;/p&gt;
&lt;p&gt;The fermentation community actually understands the biological process we're trying to monitor.&lt;/p&gt;
&lt;p&gt;The open-source community knows how to turn experiments into reproducible projects.&lt;/p&gt;
&lt;p&gt;Instead of trying to make everybody become the same kind of MyZubster user, I realized the architecture should allow these communities to remain different and still collaborate.&lt;/p&gt;
&lt;p&gt;That changed everything.&lt;/p&gt;
&lt;p&gt;The goal became less about building one giant application and more about defining interfaces between systems.&lt;/p&gt;
&lt;p&gt;In programming terms, I started thinking about the community almost like software architecture.&lt;/p&gt;
&lt;p&gt;You don't want one gigantic class responsible for everything.&lt;/p&gt;
&lt;p&gt;You want components with clear responsibilities.&lt;/p&gt;
&lt;p&gt;You want interfaces.&lt;/p&gt;
&lt;p&gt;You want events.&lt;/p&gt;
&lt;p&gt;You want APIs.&lt;/p&gt;
&lt;p&gt;You want modules that can be replaced without destroying the entire system.&lt;/p&gt;
&lt;p&gt;And ideally, you want contributors to be able to build things you never anticipated.&lt;/p&gt;
&lt;p&gt;That is exactly how I started looking at MyZubster.&lt;/p&gt;
&lt;p&gt;The same principle applies to its communities.&lt;/p&gt;
&lt;p&gt;Maker Culture → Hardware&lt;/p&gt;
&lt;p&gt;Open Source → Software&lt;/p&gt;
&lt;p&gt;IoT → Real-world connectivity&lt;/p&gt;
&lt;p&gt;Automation → Actions&lt;/p&gt;
&lt;p&gt;Privacy/Cypherpunk → Architecture constraints&lt;/p&gt;
&lt;p&gt;Artists/Game Devs → Digital experience&lt;/p&gt;
&lt;p&gt;Metaverse → Visualization and discovery&lt;/p&gt;
&lt;p&gt;Community → Knowledge&lt;/p&gt;
&lt;p&gt;Marketplace → Exchange&lt;/p&gt;
&lt;p&gt;MYZ → Economic coordination&lt;/p&gt;
&lt;p&gt;Suddenly, what looked like random experiments started looking like layers of the same system.&lt;/p&gt;
&lt;p&gt;And the kefir project became important for another reason.&lt;/p&gt;
&lt;p&gt;It's small.&lt;/p&gt;
&lt;p&gt;You can actually build it.&lt;/p&gt;
&lt;p&gt;You can measure it.&lt;/p&gt;
&lt;p&gt;You can break it.&lt;/p&gt;
&lt;p&gt;You can change the code.&lt;/p&gt;
&lt;p&gt;You can add another sensor.&lt;/p&gt;
&lt;p&gt;You can automate something.&lt;/p&gt;
&lt;p&gt;You can reproduce the experiment somewhere else.&lt;/p&gt;
&lt;p&gt;That makes it a much better architectural test than starting with:&lt;/p&gt;
&lt;p&gt;“Let's build the metaverse.”&lt;/p&gt;
&lt;p&gt;That's too abstract.&lt;/p&gt;
&lt;p&gt;Instead:&lt;/p&gt;
&lt;p&gt;Build one real thing.&lt;/p&gt;
&lt;p&gt;Connect it.&lt;/p&gt;
&lt;p&gt;Expose its data.&lt;/p&gt;
&lt;p&gt;Automate it.&lt;/p&gt;
&lt;p&gt;Create its digital representation.&lt;/p&gt;
&lt;p&gt;Let someone else reproduce it.&lt;/p&gt;
&lt;p&gt;Then repeat.&lt;/p&gt;
&lt;p&gt;Kefir today.&lt;/p&gt;
&lt;p&gt;Tomorrow it could be an urban garden.&lt;/p&gt;
&lt;p&gt;Then a weather station.&lt;/p&gt;
&lt;p&gt;A robot.&lt;/p&gt;
&lt;p&gt;A greenhouse.&lt;/p&gt;
&lt;p&gt;A community laboratory.&lt;/p&gt;
&lt;p&gt;A distributed environmental monitoring project.&lt;/p&gt;
&lt;p&gt;The domain changes.&lt;/p&gt;
&lt;p&gt;The architecture doesn't necessarily have to.&lt;/p&gt;
&lt;p&gt;And that's the part I'm most interested in as a developer.&lt;/p&gt;
&lt;p&gt;If we can define the interfaces correctly, MyZubster doesn't need to know in advance what every future project will be.&lt;/p&gt;
&lt;p&gt;A project could simply expose:&lt;/p&gt;
&lt;p&gt;“This is what I am.”&lt;/p&gt;
&lt;p&gt;“This is the data I produce.”&lt;/p&gt;
&lt;p&gt;“These are the events I emit.”&lt;/p&gt;
&lt;p&gt;“These are the actions I support.”&lt;/p&gt;
&lt;p&gt;“This is how you interact with me.”&lt;/p&gt;
&lt;p&gt;Other components can then build on top of it.&lt;/p&gt;
&lt;p&gt;That's where automation becomes powerful.&lt;/p&gt;
&lt;p&gt;An event from the physical world can trigger software.&lt;/p&gt;
&lt;p&gt;Software can trigger another service.&lt;/p&gt;
&lt;p&gt;A service can update a digital twin.&lt;/p&gt;
&lt;p&gt;The digital twin can change inside the metaverse.&lt;/p&gt;
&lt;p&gt;A community member can discover the project there.&lt;/p&gt;
&lt;p&gt;That person can inspect its documentation or source code.&lt;/p&gt;
&lt;p&gt;And potentially become the next contributor.&lt;/p&gt;
&lt;p&gt;So the architecture becomes a loop:&lt;/p&gt;
&lt;p&gt;REAL WORLD&lt;br&gt;
↓&lt;br&gt;
DATA&lt;br&gt;
↓&lt;br&gt;
SOFTWARE&lt;br&gt;
↓&lt;br&gt;
AUTOMATION&lt;br&gt;
↓&lt;br&gt;
DIGITAL WORLD&lt;br&gt;
↓&lt;br&gt;
DISCOVERY&lt;br&gt;
↓&lt;br&gt;
COMMUNITY&lt;br&gt;
↓&lt;br&gt;
CONTRIBUTION&lt;br&gt;
↓&lt;br&gt;
BETTER SOFTWARE&lt;br&gt;
↓&lt;br&gt;
REAL WORLD&lt;/p&gt;
&lt;p&gt;And that's when I realized:&lt;/p&gt;
&lt;p&gt;The community itself is part of the architecture.&lt;/p&gt;
&lt;p&gt;Not an audience sitting outside the software.&lt;/p&gt;
&lt;p&gt;Part of the system.&lt;/p&gt;
&lt;p&gt;This also changed how I think about MYZ.&lt;/p&gt;
&lt;p&gt;I don't want the interesting question to be:&lt;/p&gt;
&lt;p&gt;“Will this token go up?”&lt;/p&gt;
&lt;p&gt;The interesting engineering question is:&lt;/p&gt;
&lt;p&gt;“Can an economic layer coordinate useful contributions across an open ecosystem?”&lt;/p&gt;
&lt;p&gt;Can someone fix an issue?&lt;/p&gt;
&lt;p&gt;Build a module?&lt;/p&gt;
&lt;p&gt;Design a 3D asset?&lt;/p&gt;
&lt;p&gt;Document hardware?&lt;/p&gt;
&lt;p&gt;Test an automation?&lt;/p&gt;
&lt;p&gt;Translate documentation?&lt;/p&gt;
&lt;p&gt;Create a digital twin?&lt;/p&gt;
&lt;p&gt;Improve a physical project?&lt;/p&gt;
&lt;p&gt;Complete a bounty?&lt;/p&gt;
&lt;p&gt;And can the ecosystem recognize that contribution?&lt;/p&gt;
&lt;p&gt;That's a much harder problem than simply creating a token.&lt;/p&gt;
&lt;p&gt;But it's also a much more interesting one.&lt;/p&gt;
&lt;p&gt;So, how did I manage to connect all these apparently unrelated ideas?&lt;/p&gt;
&lt;p&gt;I stopped trying to connect them by subject.&lt;/p&gt;
&lt;p&gt;I connected them by architecture.&lt;/p&gt;
&lt;p&gt;Kefir doesn't need to be related to the metaverse culturally.&lt;/p&gt;
&lt;p&gt;It only needs an interface.&lt;/p&gt;
&lt;p&gt;A maker doesn't need to understand crypto.&lt;/p&gt;
&lt;p&gt;A crypto developer doesn't need to understand fermentation.&lt;/p&gt;
&lt;p&gt;A 3D artist doesn't need to understand embedded electronics.&lt;/p&gt;
&lt;p&gt;Each layer only needs a bridge to the next one.&lt;/p&gt;
&lt;p&gt;That's the breakthrough.&lt;/p&gt;
&lt;p&gt;Not:&lt;/p&gt;
&lt;p&gt;“Everything is the same thing.”&lt;/p&gt;
&lt;p&gt;But:&lt;/p&gt;
&lt;p&gt;“Everything can communicate.”&lt;/p&gt;
&lt;p&gt;That's what I'm trying to build with MyZubster.&lt;/p&gt;
&lt;p&gt;Not one monolithic platform.&lt;/p&gt;
&lt;p&gt;Not one subculture.&lt;/p&gt;
&lt;p&gt;Not one application.&lt;/p&gt;
&lt;p&gt;But an open system where independent projects, developers and communities can connect through shared interfaces and build things none of us could build alone.&lt;/p&gt;
&lt;p&gt;And we're still experimenting.&lt;/p&gt;
&lt;p&gt;Some ideas will work.&lt;/p&gt;
&lt;p&gt;Some will fail.&lt;/p&gt;
&lt;p&gt;Some architectures will need to be rewritten.&lt;/p&gt;
&lt;p&gt;That's exactly why I'm sharing the process with developers.&lt;/p&gt;
&lt;p&gt;Because the interesting question now isn't whether the concept sounds futuristic.&lt;/p&gt;
&lt;p&gt;It's:&lt;/p&gt;
&lt;p&gt;How would you architect it?&lt;/p&gt;
&lt;p&gt;If you were designing an open system connecting IoT devices, automation, digital twins, a metaverse, community contributions and an economic layer, where would you draw the boundaries?&lt;/p&gt;
&lt;p&gt;What would you decentralize?&lt;/p&gt;
&lt;p&gt;What would you keep local?&lt;/p&gt;
&lt;p&gt;What would you expose through APIs?&lt;/p&gt;
&lt;p&gt;And what would you absolutely not put on a blockchain?&lt;/p&gt;
&lt;p&gt;That's the discussion I'd love to have with the Coder Legion community.&lt;/p&gt;
&lt;h2 id=&quot;coderlegion-programming-softwareengineering-softwarearchitecture-developers-opensource-iot-automation-digitaltwin-metaverse-makerculture-apis-eventdrivenarchitecture-web3-blockchain-privacy-decentralization-robotics-myzubster-myz-buildinpublic&quot;&gt;CoderLegion #Programming #SoftwareEngineering #SoftwareArchitecture #Developers #OpenSource #IoT #Automation #DigitalTwin #Metaverse #MakerCulture #APIs #EventDrivenArchitecture #Web3 #Blockchain #Privacy #Decentralization #Robotics #MyZubster #MYZ #BuildInPublic&lt;/h2&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/27014/coder-legion-how-i-finally-connected-the-pieces-behind-myzubster</guid>
<pubDate>Sun, 13 Sep 2026 01:47:08 +0000</pubDate>
</item>
<item>
<title>Custom eLearning Software Development: Benefits, Process &amp; Cost</title>
<link>https://coderlegion.com/26491/custom-elearning-software-development-benefits-process-cost</link>
<description>&lt;p&gt;You’ve outgrown your LMS. Maybe it can’t handle the branching logic your compliance training needs; maybe your instructors are stitching together three different tools to track completion; or maybe every “customization” request comes back from the vendor as a locked feature behind a higher pricing tier. This is where most training managers and L&amp;amp;D leads start asking whether building something custom is worth it, or just a more expensive way to have the same problems.&lt;/p&gt;
&lt;h2 id=&quot;why-off-the-shelf-platforms-start-breaking-down&quot;&gt;Why Off-the-Shelf Platforms Start Breaking Down&lt;/h2&gt;
&lt;p&gt;Most off-the-shelf learning platforms are built to serve thousands of different companies at once, which means they’re optimized for the average use case, not yours. That works fine until your workflows, compliance rules, or content types stop being average.&lt;/p&gt;
&lt;p&gt;A few patterns show up repeatedly once a company hits this wall:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Reporting doesn’t match how the business actually measures training impact (completion rate isn’t the same as competency).&lt;/li&gt;
&lt;li&gt;Integrations with HR systems, CRMs, or internal tools require workarounds or third-party middleware.&lt;/li&gt;
&lt;li&gt;Content authoring tools inside the platform can’t support interactive simulations, branching scenarios, or gamified assessments without exporting to yet another tool.&lt;/li&gt;
&lt;li&gt;User roles and permissions are rigid, which becomes a real problem once you’re managing external partners, franchisees, or multi-tenant clients alongside internal staff.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;None of these are dealbreakers on their own. Together, they slowly turn a “training platform” into an operational bottleneck.&lt;/p&gt;
&lt;h2 id=&quot;what-custom-elearning-software-actually-solves&quot;&gt;What Custom eLearning Software Actually Solves&lt;/h2&gt;
&lt;p&gt;Custom development isn’t about having a flashier interface. It’s about removing the compromise layer that comes with generic software. When a platform is built around your actual content structure, user hierarchy, and reporting needs, a few things change immediately.&lt;/p&gt;
&lt;p&gt;Workflows match how your teams actually work, instead of forcing your process to fit the software’s assumptions. If your onboarding path depends on job role, location, and prior certifications, that logic can live natively in the system rather than being simulated through tags and manual overrides.&lt;/p&gt;
&lt;p&gt;Data ownership stays with you. Vendor platforms often make learner data portability difficult by design, since switching costs are part of their retention strategy. A custom build means your learning data, analytics, and content assets are yours outright, with no dependency on a third party’s roadmap or pricing changes.&lt;/p&gt;
&lt;p&gt;Scaling doesn’t mean renegotiating. Many commercial platforms price by active user tiers, which means growth directly increases your software cost, sometimes disproportionately. A custom system scales with your infrastructure choices, not a vendor’s seat-based pricing model.&lt;/p&gt;
&lt;p&gt;Integration becomes native, not bolted on. Connecting to an existing HRIS, single sign-on provider, or internal analytics dashboard is a design decision from day one instead of a workaround built after the fact.&lt;/p&gt;
&lt;p&gt;This is also where working with a team that offers dedicated &lt;a rel=&quot;nofollow&quot; href=&quot;https://wpwebinfotech.com/elearning/&quot;&gt;eLearning software development services&lt;/a&gt; makes a practical difference, because platform architecture decisions made early (data models, authentication, content delivery) are expensive to unwind later if they’re not planned around your actual growth path.&lt;/p&gt;
&lt;h2 id=&quot;the-real-development-process-step-by-step&quot;&gt;The Real Development Process, Step by Step&lt;/h2&gt;
&lt;p&gt;Custom builds fail more often because of a skipped discovery phase than because of bad code. Here’s what a properly run process looks like.&lt;/p&gt;
&lt;h3 id=&quot;1-requirements-and-learning-architecture-mapping&quot;&gt;1. Requirements and Learning Architecture Mapping&lt;/h3&gt;
&lt;p&gt;Before any design work starts, map the technical and instructional requirements together. This means defining learner personas, content types (video, SCORM, xAPI, simulations), assessment logic, and how progress data needs to be reported to stakeholders. Skipping this step is the biggest reason custom &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.sap.com/resources/what-is-lms&quot;&gt;LMS&lt;/a&gt; projects go over budget, because teams add features mid-build instead of scoping them upfront.&lt;/p&gt;
&lt;h3 id=&quot;2-system-architecture-and-integration-planning&quot;&gt;2. System Architecture and Integration Planning&lt;/h3&gt;
&lt;p&gt;This stage decides the technical backbone: whether the platform is multi-tenant or single-tenant, how authentication works (SSO, SAML, OAuth), what content standards it supports (SCORM 1.2, SCORM 2004, xAPI/Tin Can, cmi5), and how it connects to existing systems like HR software or payment gateways for paid courses.&lt;/p&gt;
&lt;h3 id=&quot;3-ux-and-instructional-design&quot;&gt;3. UX and Instructional Design&lt;/h3&gt;
&lt;p&gt;Interface design for learning software isn’t the same as general product design. Cognitive load, navigation friction during assessments, and mobile accessibility for field-based learners all directly affect completion rates. This stage typically produces wireframes and a content-authoring flow before development starts.&lt;/p&gt;
&lt;h3 id=&quot;4-development-in-sprints&quot;&gt;4. Development in Sprints&lt;/h3&gt;
&lt;p&gt;Custom builds are usually developed in two- to four-week sprints, with a working module released at the end of each one rather than a single release at the end of the project. This lets stakeholders test real functionality early and catch misalignments before they compound.&lt;/p&gt;
&lt;h3 id=&quot;5-qa-across-devices-and-content-types&quot;&gt;5. QA Across Devices and Content Types&lt;/h3&gt;
&lt;p&gt;Learning platforms need testing beyond standard software QA, because content rendering, &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.ispringsolutions.com/blog/scorm-course&quot;&gt;SCORM&lt;/a&gt; package compatibility, and offline-sync behavior (for field teams without steady connectivity) each need separate verification passes.&lt;/p&gt;
&lt;h3 id=&quot;6-deployment-and-data-migration&quot;&gt;6. Deployment and Data Migration&lt;/h3&gt;
&lt;p&gt;If you’re moving off an existing LMS, learner records, completion history, and certification data need to migrate without gaps, since compliance training often requires an unbroken audit trail.&lt;/p&gt;
&lt;h3 id=&quot;7-post-launch-support-and-iteration&quot;&gt;7. Post-Launch Support and Iteration&lt;/h3&gt;
&lt;p&gt;Custom software isn’t finished at launch. Usage data from the first few months typically reveals friction points that weren’t visible during testing, and a reasonable support plan should include iteration based on that real usage data, not just bug fixes.&lt;/p&gt;
&lt;h2 id=&quot;what-actually-drives-the-cost&quot;&gt;What Actually Drives the Cost&lt;/h2&gt;
&lt;p&gt;Cost estimates for custom eLearning software vary widely because “custom” can mean a lot of different things. A few factors move the number more than anything else.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Content complexity is usually the biggest driver. A platform delivering static video and PDF content costs far less to build than one supporting branching scenarios, VR/AR modules, or adaptive learning paths that adjust based on performance.&lt;/li&gt;
&lt;li&gt;Integration count matters more than most people expect. Each system you connect to (HRIS, payroll, CRM, video conferencing, payment processors) adds authentication handling, data mapping, and ongoing maintenance surface area.&lt;/li&gt;
&lt;li&gt;User scale and tenancy model affect both build and infrastructure cost. A single-organization platform is simpler than a multi-tenant system serving multiple client organizations with isolated data and branding.&lt;/li&gt;
&lt;li&gt;Compliance requirements add real cost when the platform needs to meet standards like GDPR, HIPAA, or SOC 2, since these require specific data handling, encryption, and audit logging built in from the architecture stage, not added later.&lt;/li&gt;
&lt;li&gt;Ongoing maintenance is often left out of initial budgets entirely, but custom software needs regular updates for security patches, browser compatibility, and content standard changes. Budgeting only for the build and not the year after launch is the most common cost-planning mistake companies make.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The honest answer to “what will this cost” is that it depends entirely on which of these factors apply to your specific use case, which is exactly why a proper discovery phase before development starts is worth the time it takes.&lt;/p&gt;
&lt;h2 id=&quot;bringing-it-together&quot;&gt;Bringing It Together&lt;/h2&gt;
&lt;p&gt;Custom eLearning software makes the most sense when your training needs have outgrown what a generic platform can flex to support, whether that’s complex compliance tracking, multi-tenant delivery, or integrations that off-the-shelf tools weren’t built for. The process itself isn’t mysterious: it comes down to mapping requirements honestly, planning architecture before design, building in testable sprints, and budgeting for the software’s life after launch, not just its first release. Companies that treat discovery and post-launch iteration as optional end up with expensive systems that still don’t fit. Get those two parts right, and custom development stops being a gamble and starts being a straightforward operational decision.&lt;/p&gt;
&lt;h2 id=&quot;frequently-asked-questions-1&quot;&gt;Frequently Asked Questions&lt;/h2&gt;
&lt;h3 id=&quot;is-custom-elearning-software-more-expensive-than-buying-an-lms-subscription-&quot;&gt;Is custom eLearning software more expensive than buying an LMS subscription?&lt;/h3&gt;
&lt;p&gt;Upfront cost is usually higher for custom development, but subscription platforms charge recurring per-user fees that compound as you scale. For organizations with more than a few hundred consistent learners or complex compliance needs, custom software often becomes cheaper over a three- to five-year period.&lt;/p&gt;
&lt;h3 id=&quot;how-long-does-it-take-to-build-a-custom-elearning-platform-&quot;&gt;How long does it take to build a custom eLearning platform?&lt;/h3&gt;
&lt;p&gt;A functional MVP typically takes three to six months, depending on integration complexity and content types supported. Full-featured platforms with multiple content standards, integrations, and multi-tenancy usually take six to twelve months.&lt;/p&gt;
&lt;h3 id=&quot;can-custom-elearning-software-support-scorm-and-xapi-content-&quot;&gt;Can custom eLearning software support SCORM and xAPI content?&lt;/h3&gt;
&lt;p&gt;Yes, custom platforms can support SCORM 1.2, SCORM 2004, xAPI (Tin Can), and cmi5, and unlike some commercial tools, they can support multiple standards simultaneously without licensing restrictions.&lt;/p&gt;
&lt;h3 id=&quot;do-small-businesses-need-custom-elearning-software-or-is-off-the-shelf-enough-&quot;&gt;Do small businesses need custom eLearning software, or is off-the-shelf enough?&lt;/h3&gt;
&lt;p&gt;For straightforward training needs with a small user base and no complex integrations, off-the-shelf platforms are usually more practical. Custom development becomes worthwhile once workflow complexity, integration needs, or user scale outgrow what a generic platform can flex to support.&lt;/p&gt;
&lt;h3 id=&quot;what-happens-to-learner-data-if-we-switch-from-a-commercial-lms-to-a-custom-platform-&quot;&gt;What happens to learner data if we switch from a commercial LMS to a custom platform?&lt;/h3&gt;
&lt;p&gt;Data migration needs to be planned as its own project phase, since learner records, completion history, and certifications must transfer without gaps. This is especially important for compliance training, where an incomplete audit trail can create regulatory risk.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/26491/custom-elearning-software-development-benefits-process-cost</guid>
<pubDate>Tue, 08 Sep 2026 13:24:25 +0000</pubDate>
</item>
<item>
<title>How to Build Financial Software: A Developer’s Practical Guide</title>
<link>https://coderlegion.com/26394/how-to-build-financial-software-a-developers-practical-guide</link>
<description>&lt;p&gt;Building financial software is not just another web development project. When your application handles payments, bank data, account balances, transactions, or personal financial information, a small security mistake can become a serious business problem. So how do you build financial software that is reliable, secure, scalable, and still practical for developers to maintain? The answer is to treat security, data integrity, compliance, and failure handling as core product requirements from the beginning, rather than features to add after the application is working.&lt;/p&gt;
&lt;h2 id=&quot;what-does-it-take-to-build-financial-software-&quot;&gt;What Does It Take to Build Financial Software?&lt;/h2&gt;
&lt;p&gt;To build financial software properly, developers need to combine five fundamentals: secure architecture, strong authentication and authorization, accurate transaction processing, protected financial data, and continuous security testing.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;A typical financial application may include:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;User registration and identity verification&lt;/li&gt;
&lt;li&gt;Account and wallet management&lt;/li&gt;
&lt;li&gt;Payment processing&lt;/li&gt;
&lt;li&gt;Transaction history&lt;/li&gt;
&lt;li&gt;Financial reporting&lt;/li&gt;
&lt;li&gt;Notifications and alerts&lt;/li&gt;
&lt;li&gt;Administrative dashboards&lt;/li&gt;
&lt;li&gt;Third-party banking or payment integrations&lt;/li&gt;
&lt;li&gt;Audit logs&lt;/li&gt;
&lt;li&gt;Fraud detection and risk controls&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The technology stack matters, but architecture matters more. A modern framework cannot compensate for weak access controls, poor database design, exposed secrets, or incorrect transaction logic.&lt;/p&gt;
&lt;p&gt;NIST’s Secure Software Development Framework recommends integrating security practices throughout the software development life cycle rather than treating security as a final testing phase.&lt;/p&gt;
&lt;h2 id=&quot;1-start-with-financial-requirements-not-code&quot;&gt;1. Start With Financial Requirements, Not Code&lt;/h2&gt;
&lt;p&gt;Before choosing React, Django, Node.js, Java, AWS, or any other technology, define exactly what the application is responsible for.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Ask:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;What financial data will the system store?&lt;/li&gt;
&lt;li&gt;Which users can access it?&lt;/li&gt;
&lt;li&gt;What transactions can users perform?&lt;/li&gt;
&lt;li&gt;Which operations require additional verification?&lt;/li&gt;
&lt;li&gt;What happens if a transaction fails halfway through?&lt;/li&gt;
&lt;li&gt;What information must be recorded for auditing?&lt;/li&gt;
&lt;li&gt;Which external financial services will the application connect to?&lt;/li&gt;
&lt;li&gt;What regulatory and contractual requirements apply?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This step prevents a common development mistake: building the interface first and discovering later that the underlying financial workflow cannot safely support it.&lt;/p&gt;
&lt;p&gt;For example, transferring money is not simply a button that changes one balance. The system needs to validate the request, authenticate the user, check authorization, verify available funds, process the transaction safely, record the event, and handle failures without accidentally creating or losing money.&lt;/p&gt;
&lt;h2 id=&quot;2-design-the-architecture-around-security&quot;&gt;2. Design the Architecture Around Security&lt;/h2&gt;
&lt;p&gt;For financial applications, security should influence architecture from day one.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;A practical architecture might contain:&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Frontend → API layer → Authentication/Authorization → Business logic → Database → External financial services&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Sensitive operations should pass through controlled backend services rather than relying on frontend validation.&lt;/p&gt;
&lt;p&gt;Frontend checks are useful for user experience, but they are not security boundaries. A malicious user can bypass browser-based validation and call an API directly.&lt;/p&gt;
&lt;p&gt;Use server-side validation for every sensitive operation.&lt;/p&gt;
&lt;p&gt;The architecture should also separate responsibilities. Authentication determines who a user is. Authorization determines what that user is allowed to do. Business logic determines whether the requested financial operation is valid.&lt;/p&gt;
&lt;p&gt;Keeping these responsibilities clear makes the system easier to test and harder to expose accidentally.&lt;/p&gt;
&lt;h2 id=&quot;3-make-authentication-stronger-than-a-password&quot;&gt;3. Make Authentication Stronger Than a Password&lt;/h2&gt;
&lt;p&gt;Financial software should assume that account credentials can eventually be compromised.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Use appropriate controls such as:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Multi-factor authentication&lt;/li&gt;
&lt;li&gt;Strong password policies&lt;/li&gt;
&lt;li&gt;Secure session management&lt;/li&gt;
&lt;li&gt;Short-lived access tokens where appropriate&lt;/li&gt;
&lt;li&gt;Secure password hashing&lt;/li&gt;
&lt;li&gt;Login attempt monitoring&lt;/li&gt;
&lt;li&gt;Account recovery protections&lt;/li&gt;
&lt;li&gt;Device or risk-based verification for sensitive actions&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Authentication alone is not enough.&lt;/p&gt;
&lt;p&gt;Consider a user who successfully logs into an account. That does not automatically mean they should be allowed to change another user’s bank details, access administrative functions, or download sensitive reports.&lt;/p&gt;
&lt;p&gt;That is where authorization becomes critical.&lt;/p&gt;
&lt;h2 id=&quot;4-enforce-authorization-on-every-sensitive-request&quot;&gt;4. Enforce Authorization on Every Sensitive Request&lt;/h2&gt;
&lt;p&gt;One of the most dangerous mistakes in financial software is assuming that because a user is authenticated, they can access any resource associated with their account.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Every sensitive API request should verify:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Who is making the request?&lt;/li&gt;
&lt;li&gt;What resource are they accessing?&lt;/li&gt;
&lt;li&gt;What action are they attempting?&lt;/li&gt;
&lt;li&gt;Are they allowed to perform that action?&lt;/li&gt;
&lt;li&gt;Does the current transaction state permit it?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For example, an API endpoint such as &lt;code&gt;/accounts/12345/transactions&lt;/code&gt; should never rely solely on the account ID supplied by the client.&lt;/p&gt;
&lt;p&gt;The backend must verify that the authenticated user is actually authorized to access account &lt;code&gt;12345&lt;/code&gt;.&lt;br&gt;
This principle is central to &lt;a rel=&quot;nofollow&quot; href=&quot;https://wpwebinfotech.com/finance/&quot;&gt;secure financial software development&lt;/a&gt; because authorization failures can expose sensitive financial information even when authentication itself works correctly.&lt;/p&gt;
&lt;h2 id=&quot;5-protect-financial-data-at-every-stage&quot;&gt;5. Protect Financial Data at Every Stage&lt;/h2&gt;
&lt;p&gt;Financial information needs protection both in transit and at rest.&lt;/p&gt;
&lt;p&gt;Use encrypted connections for application traffic and carefully protect sensitive data stored in databases, backups, logs, and file storage.&lt;/p&gt;
&lt;p&gt;Developers should also avoid placing secrets directly in source code.&lt;/p&gt;
&lt;p&gt;API keys, database credentials, encryption keys, and service credentials should be managed through appropriate secret-management mechanisms and rotated when necessary.&lt;/p&gt;
&lt;p&gt;Be equally careful with logs.&lt;/p&gt;
&lt;p&gt;A debugging statement that accidentally records authentication tokens, payment details, personal information, or financial identifiers can turn a useful monitoring system into a security liability.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The rule is simple:&lt;/strong&gt; Log what you need to investigate a problem, not everything the application happens to know.&lt;/p&gt;
&lt;h2 id=&quot;6-treat-transactions-as-financial-operations&quot;&gt;6. Treat Transactions as Financial Operations&lt;/h2&gt;
&lt;p&gt;Financial transaction logic deserves special attention because correctness matters just as much as security.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;A transaction should be:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Atomic where required&lt;/li&gt;
&lt;li&gt;Consistent&lt;/li&gt;
&lt;li&gt;Traceable&lt;/li&gt;
&lt;li&gt;Idempotent where appropriate&lt;/li&gt;
&lt;li&gt;Recoverable after failure&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Consider a payment request that reaches the server twice because of a network timeout.&lt;/p&gt;
&lt;p&gt;If the system processes both requests independently, a customer could be charged twice.&lt;/p&gt;
&lt;p&gt;An &lt;a rel=&quot;nofollow&quot; href=&quot;https://cloud.google.com/discover/idempotency&quot;&gt;idempotency&lt;/a&gt; mechanism allows the application to recognize that the second request represents the same operation and prevents unintended duplication.&lt;/p&gt;
&lt;p&gt;Database transactions, unique identifiers, proper locking strategies, and carefully designed state transitions can help maintain financial consistency.&lt;/p&gt;
&lt;p&gt;Never assume that a successful HTTP response automatically means a financial transaction has completed correctly. The application’s transaction state must be explicit and auditable.&lt;/p&gt;
&lt;h2 id=&quot;7-build-an-audit-trail&quot;&gt;7. Build an Audit Trail&lt;/h2&gt;
&lt;p&gt;Financial applications need to answer questions such as:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Who changed this account? When did it happen? What was changed? Was the action successful?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;That requires meaningful audit logging.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;An audit record might capture:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;User or service identity&lt;/li&gt;
&lt;li&gt;Timestamp&lt;/li&gt;
&lt;li&gt;Action performed&lt;/li&gt;
&lt;li&gt;Resource affected&lt;/li&gt;
&lt;li&gt;Transaction or request identifier&lt;/li&gt;
&lt;li&gt;Result&lt;/li&gt;
&lt;li&gt;Relevant security context&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Audit logs should be protected against unauthorized modification and should not contain unnecessary sensitive information.&lt;/p&gt;
&lt;p&gt;A useful audit trail is valuable for debugging, security investigations, operational reviews, and compliance processes.&lt;/p&gt;
&lt;h2 id=&quot;8-validate-third-party-integrations-carefully&quot;&gt;8. Validate Third-Party Integrations Carefully&lt;/h2&gt;
&lt;p&gt;Most financial applications do not operate in isolation.&lt;/p&gt;
&lt;p&gt;They may connect to payment processors, banking APIs, identity providers, fraud detection systems, accounting platforms, or notification services.&lt;/p&gt;
&lt;p&gt;Every integration introduces another dependency and another potential failure point.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Before integrating a service, understand:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;What data it receives&lt;/li&gt;
&lt;li&gt;What data it returns&lt;/li&gt;
&lt;li&gt;How authentication works&lt;/li&gt;
&lt;li&gt;How errors are reported&lt;/li&gt;
&lt;li&gt;How rate limits work&lt;/li&gt;
&lt;li&gt;What happens during downtime&lt;/li&gt;
&lt;li&gt;How credentials are stored and rotated&lt;/li&gt;
&lt;li&gt;Whether requests can safely be retried&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Never design your application around the assumption that an external API will always respond quickly and successfully.&lt;/p&gt;
&lt;p&gt;Build explicit strategies for timeouts, retries, error handling, and reconciliation.&lt;/p&gt;
&lt;h2 id=&quot;9-test-security-before-production&quot;&gt;9. Test Security Before Production&lt;/h2&gt;
&lt;p&gt;Security testing should happen throughout development, not just immediately before launch.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Useful practices include:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Dependency scanning&lt;/li&gt;
&lt;li&gt;Static application security testing&lt;/li&gt;
&lt;li&gt;Dynamic security testing&lt;/li&gt;
&lt;li&gt;API testing&lt;/li&gt;
&lt;li&gt;Authentication and authorization testing&lt;/li&gt;
&lt;li&gt;Penetration testing&lt;/li&gt;
&lt;li&gt;Database security testing&lt;/li&gt;
&lt;li&gt;Secret scanning&lt;/li&gt;
&lt;li&gt;Infrastructure configuration reviews&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;NIST specifically recommends integrating secure development practices into the &lt;a rel=&quot;nofollow&quot; href=&quot;https://aws.amazon.com/what-is/sdlc/&quot;&gt;SDLC&lt;/a&gt; and addressing security earlier rather than waiting until the end of development.&lt;/p&gt;
&lt;p&gt;This “shift-left” approach can reduce the cost and effort associated with fixing security problems late in development.&lt;/p&gt;
&lt;h2 id=&quot;10-plan-for-failure-not-just-success&quot;&gt;10. Plan for Failure, Not Just Success&lt;/h2&gt;
&lt;p&gt;A financial application should be designed around the assumption that something will eventually fail.&lt;/p&gt;
&lt;p&gt;A database may become unavailable. A payment provider may time out. A message may be delivered twice. A user’s connection may disappear during a transaction.&lt;/p&gt;
&lt;p&gt;The system needs predictable behavior in these situations.&lt;/p&gt;
&lt;p&gt;For example, if a payment provider times out, the application should not immediately assume that the payment failed. The provider may have processed the transaction even though the response never reached your application.&lt;/p&gt;
&lt;p&gt;That is why reconciliation processes matter.&lt;/p&gt;
&lt;p&gt;Good financial software has mechanisms to identify uncertain transaction states and resolve them safely rather than guessing.&lt;/p&gt;
&lt;h2 id=&quot;11-keep-the-first-version-focused&quot;&gt;11. Keep the First Version Focused&lt;/h2&gt;
&lt;p&gt;It is tempting to build every feature into a financial platform from day one.&lt;/p&gt;
&lt;p&gt;That usually increases risk.&lt;/p&gt;
&lt;p&gt;Start with the smallest useful financial workflow and make that workflow reliable before expanding it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;For example:&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;User authentication → account creation → transaction creation → transaction verification → transaction history → audit trail&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Once those fundamentals work correctly, additional capabilities can be introduced systematically.&lt;/p&gt;
&lt;p&gt;A smaller system with clear boundaries is generally easier to secure, test, monitor, and maintain than a large system filled with interconnected features.&lt;/p&gt;
&lt;h2 id=&quot;a-practical-secure-financial-software-development-checklist&quot;&gt;A Practical Secure Financial Software Development Checklist&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Before launching, developers should be able to answer “yes” to questions like:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Does server-side authorization protect all sensitive APIs?&lt;/li&gt;
&lt;li&gt;Is sensitive data encrypted appropriately?&lt;/li&gt;
&lt;li&gt;Are secrets kept outside source code?&lt;/li&gt;
&lt;li&gt;Are financial operations protected against duplicate processing?&lt;/li&gt;
&lt;li&gt;Are important actions recorded in audit logs?&lt;/li&gt;
&lt;li&gt;Are dependencies monitored for vulnerabilities?&lt;/li&gt;
&lt;li&gt;Are authentication and authorization tested independently?&lt;/li&gt;
&lt;li&gt;Can the application handle third-party service failures?&lt;/li&gt;
&lt;li&gt;Can uncertain transactions be reconciled?&lt;/li&gt;
&lt;li&gt;Is there a process for detecting and responding to vulnerabilities?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;NIST’s SSDF organizes secure development around preparing the organization, protecting software, producing well-secured software, and responding to vulnerabilities.&lt;/p&gt;
&lt;h2 id=&quot;conclusion-2&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;The best way to build financial software is to think beyond features. A payment screen, dashboard, or transaction API is only one part of the product. The real engineering challenge is making sure every financial operation remains secure, accurate, traceable, and recoverable.&lt;/p&gt;
&lt;p&gt;Start with clear requirements, design security into the architecture, enforce backend authorization, protect sensitive information, make transaction processing resilient, maintain audit trails, test continuously, and prepare for failure.&lt;/p&gt;
&lt;p&gt;That approach may require more planning at the beginning. Still, financial software is not the place to discover security and reliability problems after users have started trusting the system with their money.&lt;/p&gt;
&lt;h2 id=&quot;frequently-asked-questions-faqs-&quot;&gt;Frequently Asked Questions (FAQs)&lt;/h2&gt;
&lt;h3 id=&quot;what-is-the-most-important-part-of-financial-software-development-&quot;&gt;What is the most important part of financial software development?&lt;/h3&gt;
&lt;p&gt;Security and transaction integrity are the most important foundations. The system must protect financial data while ensuring that transactions cannot be improperly created, modified, duplicated, or lost.&lt;/p&gt;
&lt;h3 id=&quot;how-do-you-make-financial-software-secure-&quot;&gt;How do you make financial software secure?&lt;/h3&gt;
&lt;p&gt;Use strong authentication, server-side authorization, encryption, secure secret management, input validation, audit logging, dependency monitoring, security testing, and reliable transaction controls throughout the development life cycle.&lt;/p&gt;
&lt;h3 id=&quot;what-technology-is-best-for-building-financial-software-&quot;&gt;What technology is best for building financial software?&lt;/h3&gt;
&lt;p&gt;There is no single best technology stack. Choose technologies with strong security support, mature ecosystems, reliable libraries, experienced developer availability, and a good fit for the application’s scalability and compliance requirements.&lt;/p&gt;
&lt;h3 id=&quot;why-is-audit-logging-important-in-financial-software-&quot;&gt;Why is audit logging important in financial software?&lt;/h3&gt;
&lt;p&gt;Audit logs provide a traceable record of important actions. They help teams investigate suspicious activity, troubleshoot incidents, understand changes, and demonstrate operational accountability.&lt;/p&gt;
&lt;h3 id=&quot;how-can-developers-prevent-duplicate-financial-transactions-&quot;&gt;How can developers prevent duplicate financial transactions?&lt;/h3&gt;
&lt;p&gt;Use idempotency controls, unique transaction identifiers, appropriate database constraints, and carefully designed transaction states. The system should safely handle retries instead of assuming every request will arrive only once.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/26394/how-to-build-financial-software-a-developers-practical-guide</guid>
<pubDate>Mon, 07 Sep 2026 11:35:05 +0000</pubDate>
</item>
<item>
<title>Naming Is Not the Hardest Problem in Development</title>
<link>https://coderlegion.com/26364/naming-is-not-the-hardest-problem-in-development</link>
<description>&lt;p&gt;&lt;strong&gt;The reliable naming approach serving me very well daily for 6+ years.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;This is a well‑known quote in the SWE world, originally said by an engineer at Netscape over 20 years ago: “There are only two hard things in Computer Science: cache invalidation and naming things.” :)&lt;/p&gt;
&lt;p&gt;So, the issue is, to put it mildly, nothing new. I read a recent article about a naming approach that felt to me mechanical and ineffective, likely based on &quot;not invented here syndrome&quot;. And I decided to share my naming approach that is based on a combination of the long know ideas and practices that is really effective.&lt;/p&gt;
&lt;p&gt;Note, that I only use it for OOAD and OOP in complex business domain areas. I am not sure if it works for functional programming or the other SWE areas like system programming etc.&lt;/p&gt;
&lt;p&gt;The most important basis and a tool for an effective naming strategy for code identifiers was outlined in Eric Evans’s DDD book back in 2003: the Ubiquitous Language. This is essentially the foundation of good naming — it’s about using domain‑specific terms that the engineer standardises into a unique vocabulary.&lt;/p&gt;
&lt;p&gt;With names built this way, the functional purpose of a variable, field, method, or class becomes immediately clear in domain terms. For example, take the object &lt;code&gt;UploadedFileStreamValidation&lt;/code&gt;. Essentially, anyone reading the code is now reading it in the language of the domain.&lt;/p&gt;
&lt;p&gt;Once we see such a name, we instantly understand the object’s responsibilities. Next, we need to determine the object’s place within a formal hierarchy of responsibilities — a hierarchy that’s actually quite universal across many domains.&lt;/p&gt;
&lt;p&gt;And here, DDD comes in handy again. It already defines a set of object archetypes that clearly delimit their formal responsibilities. There aren’t that many of them: Value Object, Entity, Aggregate, Domain Service, Application Service, Repository, Factory, and a few others.&lt;/p&gt;
&lt;p&gt;Add one of these archetypes to our identifier, and we get &lt;code&gt;UploadedFileStreamValidationService&lt;/code&gt;. Now it’s clear where this object fits in the system, what it’s responsible for, and how it relates to other objects. This, really, is the very effective approach.&lt;/p&gt;
&lt;p&gt;Now, a few simple conventions:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Interfaces always start with I.&lt;/li&gt;
&lt;li&gt;Types start with T.&lt;/li&gt;
&lt;li&gt;Enums start with E.&lt;/li&gt;
&lt;li&gt;Abbreviations in are always in CAPS&lt;/li&gt;
&lt;li&gt;Class names are always in PascalCase.&lt;/li&gt;
&lt;li&gt;Variable and parameter names are always in camelCase.&lt;/li&gt;
&lt;li&gt;Object properties and database fields are always in snake_case.&lt;/li&gt;
&lt;li&gt;Constants are always in ALL_CAPS snake_case.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Add your own conventions when feels useful.&lt;/p&gt;
&lt;p&gt;Applying the approach consistently multiplies readability and comprehension (!) for a new or returning reader. The code base written this way is a story about meanings, relations, intentions and outcomes that are the core elements to support the correct engineering decision making.&lt;/p&gt;
&lt;p&gt;Using this kind of naming approach, you can build very large systems.&lt;/p&gt;
&lt;p&gt;I’d be happy to discuss this further if anyone’s interested.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/26364/naming-is-not-the-hardest-problem-in-development</guid>
<pubDate>Mon, 07 Sep 2026 05:05:54 +0000</pubDate>
</item>
</channel>
</rss>