Build1 publisher3 min readPublished
A vague prompt hands the model the retention decision on a payments table
A dev.to post traces one payments system from an updated_by column to a March repair job rebuilt out of the provider's statements. Its claim is that the retention choice had to be made before any code was generated.
The Engineer · Build desk

What happened
- A payments product answered balance questions with ordinary CRUD, then months later had to say who approved a reversal and what they knew, without erasing the original record.
- An updated_by column recorded who wrote each row and a shadow table kept the previous version, but the shadow table could say nothing about writes made before it existed.
- When finance asked for the March reversals, a repair job reconstructed them from the provider's statements, because the system's own records no longer held them.
- The post argues the missing item was an earlier decision about whether the system needed to keep its history, made before any of the code was written.
Compiled by The EngineerSomething wrong?How this is made
Why it matters
- constraint Once rows are overwritten, later additions cannot read them back; recovery in this case depended on the payment provider having kept its own statements.
- decision Retention has to be settled before generation, because a model that is not told about the requirement will pick a shape that discards the prior value.
- cost Keeping the facts moves the bill to the read path: somebody has to be able to interpret a five-year-old event written by charge code that has since been deleted.
- capability An append-only log of decisions scoped to the one feature that needs history avoids committing the whole CRUD system to event sourcing.
A vague request leaves the model room to pick a familiar shape, and the post names the shapes it picks: route handler, controller, model, query, response [6]. It calls that pull regression to the architectural mean, where a model without constraints reaches for the common answer instead of the answer that fits this system [7]. The miss is quiet, because the endpoint returns the field and the test passes [8]. A model 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 [9].
Asking it to behave like a senior architect does not supply any of that. "That gives it a title; it does not tell it how this system works," the post says [10].
The concrete damage in the payments example is one controller holding three jobs: it verifies the provider's signature, writes the row, and decides whether the customer may be charged again [11]. That arrangement is convenient until one of the three has to change on its own, at which point it drags the other two along [12]. "The code made that choice quietly. The product was never asked," according to the post [13].
The fix offered is a prompt that carries the constraints. As the post writes it, the model is told to verify the signature and translate the payload, to call the existing recordPayment function, to leave payment records alone rather than updating them directly, to follow the duplicate handling in recordRefund, and to test a valid event and a repeated event [14]. The rule now has a name. The model has an example, and the tests have a job [15]. Output may still need editing, and it has less room to build a second architecture beside the first [15].
A prompt lasts for one request and then it is gone. The next agent, or the next Friday patch, has to be able to find the decision, so it lives in module boundaries, in examples in the repository, and in tests; a dependency check can make a broken boundary plain [16][17].
Retention has a running cost, and the post puts it on the read side: "The cost is not in the writing but in the reading" [19]. Every payment event kept is a shape somebody must still be able to interpret in five years, including events written by a version of the charge code that no longer exists [20]. Against that sits the alternative already described: "The March repair job is what the other choice costs." [21]
The post limits the scope. The profile photo still needs updating, and several layers would add little to it [22]. A boundary belongs where a shared rule or an external dependency has needs of its own [23]. Where history matters, an append-only log of decisions may be enough, and event sourcing takes the same idea further by building current views from the saved facts [24].
The argument rests on a single worked payments example, with no measured comparison between vague and constraint-carrying prompts [29]. The retention part transfers on its own logic, because the three remedies in the story are three attempts to read back rows that were already gone, and the last one depends on the provider's statements [28][18].
What to watch
- Whether agent harnesses begin reading repository-level boundary rules on their own, instead of depending on each prompt to name the existing function.
- Whether dependency-check tooling fails a build on a crossed module boundary in generated code rather than only reporting it.
- A measured comparison of defect rates between vague and constraint-carrying prompts, which the post does not attempt.