{"slug": "the-architecture-of-averages", "title": "The Architecture of Averages", "summary": "A developer argues that AI coding assistants default to conventional architectures when prompts omit key constraints, a failure mode they call \"regression to the architectural mean.\" The engineer illustrates the problem with a payments system where an AI-generated controller conflated signature verification, record writes, and charge decisions, and recommends naming shared rules, pointing models at existing examples, and backing decisions with tests and module boundaries. The piece concludes that durable, append-only records of decisions are needed when a system's history must outlive the code that first wrote it.", "body_md": "A payments product begins by asking what a customer’s balance is, and ordinary CRUD answers that well enough. Some months later somebody asks a different sort of question: who approved that reversal, what did they know when they approved it, and can we correct the mistake without making the original disappear. The balance, it turns out, was the first question and the easiest.\n\nSo an `updated_by` column is added to the payments table, which tells us who but not what they knew. A shadow table follows, to keep the previous row before each write, though it can say nothing about the writes that came before it existed. Then finance asks for the March reversals, and a repair job is written to reconstruct them from the provider’s statements rather than from our own records, which no longer contain them. AI can produce each of these in an afternoon, and each is defensible on its own. Taken together they are an attempt to recover what we chose to overwrite.\n\nWhat was missing was an earlier decision, made before any of it: did the system need to keep its history? **AI fills gaps with convention; engineers must make lasting decisions durable.**\n\nWhen a request is vague, the model has room to choose a familiar shape: route handler, controller, model, query, response. It cannot know about an unreliable provider, a rule shared across the app, or a need for history unless the request or the codebase shows it. We may ask it to act like an experienced architect, of course. That gives it a title; it does not tell it how this system works.\n\nCall this **regression to the architectural mean**: when the constraints are missing, the model reaches for the common answer instead of the answer that fits here. Usually the common answer does not look bad. The endpoint returns the field and the test passes, so it is easy to miss the quieter question of where the feature has made its home.\n\nOur payment controller verifies the provider’s signature, writes the row, and decides whether the customer may be charged again. That is convenient until one of the three must change on its own and drags the other two along. The code made that choice quietly. The product was never asked.\n\nNow the same product needs a payment webhook. The handler can check the provider’s signature and translate its payload before calling the same payment rule the nightly import already uses, so that both paths mean the same thing by “record a payment.” A useful prompt says as much:\n\nVerify the signature and translate the payload. Call the existing `recordPayment` function. Do not update payment records directly. Follow duplicate handling in `recordRefund`; test a valid event and a repeated event.\n\nSuddenly the rule has a name, the model has an example, and the tests have a job to do. The result may still need editing, but it has less room to build a second architecture beside the first.\n\nThe decision must also survive the prompt, because the next agent — or the next Friday patch — will need to find it. Module boundaries, examples in the repository, and tests give it a life beyond the original request. A dependency check can make a broken boundary plain.\n\nNone of this means every operation deserves an interface. The profile photo still needs updating, and several layers would add little to it. A boundary earns its place when a shared rule or an external dependency has needs of its own.\n\nA boundary makes a rule findable. When the rule records a fact whose meaning must survive the system that first wrote it, the boundary also needs a durable record. Event sourcing is one way of doing that; it is not the point in itself.\n\nThis brings us back to the balance and its history. A saved payment fact tells us what happened, while a dashboard builds a view from those facts; because of that difference, we can rebuild the dashboard without recording the payment again.\n\nWhere history matters, an append-only log of decisions may be enough. Event sourcing takes the same idea further, building the current views from the saved facts. The cost is not in the writing but in the reading: every payment event we keep is a shape somebody must still be able to interpret in five years, including the ones written by a version of the charge code that no longer exists. The March repair job is what the other choice costs. The cost should answer a real business need, since one feature that needs history does not require rebuilding the whole CRUD system.\n\nThe distinction also helps us decide where human attention belongs. Code that records business facts or triggers outside actions deserves close review, while agents can have more freedom with derived views that checks and replay can test. **Architecture does not remove responsibility; it decides where responsibility must be concentrated.** That freedom has limits: rebuilding must be safe to repeat, use versioned logic, and avoid sending the same email or taking the same payment twice. **Replay is not an excuse to act twice, and the architecture must make that separation possible.** Otherwise the rebuild that repairs the dashboard also thanks the customer, in September, for a payment made in March.\n\nA rebuildable view is not harmless because it can be fixed. A stale balance approves the credit at noon and is corrected at four; the correction does not withdraw the money.\n\nA reviewer who knows which rule a change touches, which facts it writes, which effects it sets loose outside the system and which code it reuses is reading decisions; one who is handed a tour of new files guesses at those same decisions, politely and in a hurry, which is a failure less of diligence than of what was put in front of them. Much of what they review can be checked and rebuilt, and there the automated checks carry the weight well enough, so that the attention worth having gathers where it must — on the lasting commitments, the sensitive data, and the actions that leave the building and do not come back. No speed of generation moves it elsewhere. Where the design itself is still uncertain AI will compare the options and run the experiments willingly, and produce the code quickly whichever way the comparison falls, **but it cannot settle a decision whose success nobody has defined**.\n\nAll of which returns us to the person who will ask, some months from now, who approved the reversal and what they knew when they approved it, for that person always arrives. By then the dashboard can be rebuilt and the view recomputed, the column added late as ours was; but the email cannot be unsent, nor the credit unapproved, and the row that was overwritten was overwritten by somebody quite certain, at the time, that nobody would ask.", "url": "https://wpnews.pro/news/the-architecture-of-averages", "canonical_source": "https://dev.to/savioresende/the-architecture-of-averages-3077", "published_at": "2026-09-20 03:15:36+00:00", "updated_at": "2026-09-20 03:54:35.322462+00:00", "lang": "en", "topics": ["ai-tools", "developer-tools", "ai-agents", "artificial-intelligence"], "entities": [], "alternates": {"html": "https://wpnews.pro/news/the-architecture-of-averages", "markdown": "https://wpnews.pro/news/the-architecture-of-averages.md", "text": "https://wpnews.pro/news/the-architecture-of-averages.txt", "jsonld": "https://wpnews.pro/news/the-architecture-of-averages.jsonld"}}