Tagged enterprise-software
46 pieces
- · 10 min read
The Balance Behind Every Screen: Thirteen Modules of Leave Screens Are One Arithmetic Identity
An entitlement system written up as thirteen modules of screens has one real deliverable, and it is an arithmetic identity: allocated, less used, less pending, equals available. That identity has to stay correct through cancellations after a calendar change, double submissions, administrative adjustments and year-end resets. A click-through acceptance checklist exercises none of those, which is how these systems pass acceptance and then produce balances nobody can defend.
- · 9 min read
Every Third-Party API Fails Differently, So Put One Adapter Between Each One and Your Product
Integrating providers inline means every provider's schema, auth, rate limits and failure modes spread through your application, and one provider's outage empties the page instead of thinning it. One adapter per provider behind a single internal shape contains the variation where it starts. On a flight-search product this took API success from 90.6 to 98.95 percent and empty results caused by provider failure from 6.8 to 0.9 percent.
- · 9 min read
A Prompt Is Not a Lock: Three Ways Row-Level Security Leaks Even When the Rule Is Written Correctly
When an assistant answers questions about live records, the engineering is the permission boundary, not the retrieval. In a Postgres demonstrator with a correctly written row-level security policy, 3 of 8 permission checks still returned another person's full record. All three leaks came from below the policy: an owner-login exemption, a view running as its definer, and a session variable surviving in a pooled connection.
- · 8 min read
Slow at Scale, Worse When Scaled: It Is Usually a Query-Count Problem
When a product is slow under load and adding servers makes the economics worse without fixing the latency, the bottleneck is the work inside one request rather than the capacity to run many. Profile a single request before provisioning anything. On a flight-search product this took the median from 12.8 to 2.8 seconds and database queries per search from 149 to 38, on the same hardware, at roughly a third of the infrastructure cost per search.
- · 7 min read
Your Hybrid Search Is Silently Dense-Only, and Reciprocal Rank Fusion Will Not Tell You
The widely copied hybrid search recipe pairs a vector index with Postgres full-text search and fuses the two with reciprocal rank fusion. Run a Chinese query against it and the full-text side returns zero rows, because there is no Chinese-capable text search configuration. Fusion then produces the dense-only ordering, with no error and no warning, and every test that checks for results still passes.
- · 10 min read
Entries Are the Record, the Balance Is a View
A balance held in a column is a cache with no source behind it. Every correction becomes an edit, and nothing in the system can reconstruct how the number reached its current value. Derive the balance from entries instead and it is explainable at any date, while corrections become entries rather than edits. The cost is real: derivation is slower than reading a column, and a stored snapshot is an optimisation of the derivation rather than a replacement for it.
- · 8 min read
The Cheapest Option That Does Not Work: Gate on the Physical Constraint Before You Rank on Price
When a cheap option carries a risk the user cannot see, ranking it first and warning in the fine print puts the trap exactly where trust is highest. Treat every option as a candidate until it proves executable, gate on the physical constraint, and reject silently. On a self-transfer flight product this caught 76 percent of the combinations that could not actually be made, took false-viable connections from 12.4 to 1.9 percent, and raised the share of searches returning the cheapest valid result from 81 to 95.8 percent.
- · 8 min read
You Asked for 40 Rows and Got 4: What a Filter Does to an HNSW Vector Search
Add a WHERE clause to a vector search and the index does not search harder to compensate. It walks its usual neighbourhood, most of what it finds fails your filter, and you get a fraction of the rows you asked for. At a 10 percent selective filter on default ef_search, a reproduction on pgvector returned 3.93 rows on average against a request for 40. No error, no warning, just a short list that looks like a thin corpus.
- · 8 min read
Schema-Valid and Ten Thousand Times Wrong: Units Are Semantics, Not Syntax
A strict JSON schema on an extraction pipeline validates type, range and required fields. It cannot see that the number 12 in a table headed in ten-thousands means 120,000. The row passes every check, lands in the database as 12, and is wrong by four orders of magnitude with no validation error anywhere. The fix is an arithmetic cross-check against a total the document already states.
- · 8 min read
Your Decimal Engine Is Still Eating Floats: Determinism Is Decided at the Parser Boundary
Switching a calculation engine to decimal arithmetic fixes nothing if the numbers arrive as floats. A spreadsheet stores numeric cells as IEEE-754 doubles, the usual dataframe library infers a float type on read, and constructing a decimal from a float inherits the float's error exactly. The engine then computes precisely on a value that was already wrong, and nothing in the stack will tell you.
- · 11 min read
Who Owns This Fact? Assign Ownership Per Datum Before You Integrate, Not After
When several integrated systems each hold a version of the same fact and each can write it, there is no answer to which one is right. Not a hard answer, no answer: there is no shared clock, no shared sequence, and last-write-wins gives you determinism rather than truth. Fact ownership has to be assigned per datum before integration. This covers the single-writer rule, read-only projections with provenance, what actually happens when two systems both update, and why sync is not an answer.
- · 11 min read
Concurrency Belongs at the Write, Not at the Read
Under ordinary database isolation levels two concurrent requests can both read the same available balance, both find it sufficient, and both commit, leaving the balance negative. Checking availability before the write cannot prevent it, because the check and the write are separate decisions taken against a stale view. Enforce the constraint where the write lands: a uniqueness constraint, a conditional update, or explicit locking.
- · 7 min read
A Migration Is Not a Build: The Old System Knows Things Nobody Wrote Down
Replacing a long-lived platform gets scoped as a greenfield build because the new stack is the exciting part. What actually has to move is fifteen years of business rules encoded in stored procedures and triggers, which no specification describes because nobody ever wrote them. The first deliverable is proving that knowledge moves, on a copy, before anything depends on it having moved.
- · 10 min read
Year-End Is a Rule, Not a Reset
Carry-over, expiry and lapse at a period boundary are derivation rules applied at a date. Implemented as a destructive reset that overwrites the previous period's state, the previous year becomes unreconstructable and any retrospective correction is impossible. The reset version is always the one chosen first, because it is a smaller piece of code and the cost only appears months later when somebody asks a question the system can no longer answer.
- · 6 min read
Your Budget Cap Resets on Resume, Which Is Exactly When the Cost Arrives
A spend limit on an automated run is a control only if it accumulates across the whole task. On at least one widely used agent runner the cap applies per invocation, so resuming or continuing a task starts the budget again. Cost controls therefore stop working at the precise moment a task goes back for more work, which is when the spend happens. The fix is to track cost outside the tool, against the task rather than the invocation.
- · 9 min read
The Old System Knows Things Nobody Wrote Down
In long-lived business platforms a large share of policy sits in stored procedures, triggers, constraints, defaults and view definitions rather than in application code. A developer reading the application sees a call and a result, and the policy inside is invisible. Enumerating it from the system catalogue produces an artefact the organisation has never had, and the gap between that artefact and the specification is the real finding.
- · 7 min read
Your Dates Are 543 Years Off, and Every Row Loaded Successfully
A calendar convention read as the wrong system shifts every date by a fixed offset that can be centuries, and nothing rejects the result because the output is a valid date. The same class covers text moving through a legacy code page, numeric separators, and time zones stored without one. These failures pass row counts, null checks and schema validation, which is why a migration has to test for them explicitly rather than wait for them.
- · 7 min read
Your Mel Spectrogram Is Not the Same as Theirs: Six Parameters That Have to Match
Audio feature extraction has at least six parameters whose defaults differ between libraries, including the mel scale convention itself. A mismatch between the path used in training and the path running on the device produces a systematic tilt across the spectrum rather than noise, so the model degrades in a structured way that looks like a robustness problem. Pin all six and a recurring bug becomes a one-time piece of work.
- · 8 min read
The Fan-Out Budget: Expand, Filter With Something Cheap, Spend the Paid Call Last
When one user request has to become many calls to a paid provider, cost and latency scale with the number of candidates rather than with the number of requests. The fix is an ordering: generate candidates from something you own, filter them against something cheap, and spend the expensive call only on survivors. The budget then holds per request instead of per candidate.
- · 8 min read
Inventory on a Copy, Touch Nothing Live
The cheapest risk reduction available in a migration is a first gate that runs entirely against a database copy and ships no features. Because there is no operational risk, the work can be thorough, and thoroughness is the entire value. This is how to scope, price and sell a milestone whose deliverable is documentation rather than software.
- · 7 min read
A Cached Price Is a Gate, Not a Quote: Two Jobs One Number Cannot Do
An indicative value and a committed value are different objects with different tolerances, and the cheapest way to break a product is to let one become the other. A cached price that ranks candidates is allowed to be wrong. The same number shown to a user as what they will pay is a promise. Keep them separate in the type system, not just in the documentation.
- · 8 min read
Price the Baseline in the Same Pass, or Your Ranking Is Not Answering the Question
A product that offers a clever alternative to an obvious option has to show it beats that option, on the same inputs, at the same moment. Ranking your own candidates against each other answers which of yours is best, which is not what the user asked. The baseline has to be computed in the same pass, or the comparison is against a number from a different time with different availability.
- · 8 min read
A Rule With No Instance Is Not Found by Inspection
A business rule that only fires on a condition absent from your copied data will not be discovered by reading the schema, however carefully you read it. That is the structural limit of static discovery, and it is why keeping the old system running during replacement matters more than the completeness of the catalogue. Shadow running and output comparison on live traffic are the only mechanisms that close the gap.
- · 8 min read
A Global Minimum Is Wrong in Both Directions: Feasibility Rules Belong Per Location
One threshold applied everywhere is simultaneously too strict where conditions are easy and too loose where they are hard, so it rejects good options and admits dangerous ones at the same time. Feasibility rules have to be per location and per case, and the cases differ in kind rather than in degree: clearing immigration and collecting a bag is not a longer version of walking between gates.
- · 7 min read
Four Mechanisms, Four Numbers: Why One Reliability Metric Hides the Work
An integration layer that improved API success, end-to-end failure, duplicate results and empty responses did so through four separate mechanisms. Reported as one number, you cannot tell which part earned its keep, you cannot tell when one regresses, and you cannot defend the architecture. Measure each mechanism on its own, and the gap between two of the numbers becomes the most informative thing in the report.
- · 7 min read
You Cannot Deduplicate Before You Normalise: Identity Is a Decision, Not a Property
Two records from different sources cannot be compared until they share a shape, so deduplication is downstream of normalisation whether or not anyone planned it that way. The harder half is that identity then has to be defined deliberately: nothing in the data says which fields make two records the same thing, and that choice decides both your duplicate rate and your merge errors.
- · 8 min read
At Least Once With No Replay
Inbound webhooks from major messaging and social platforms retry over an extended window and typically offer no way to request message history afterwards. That pairing produces duplicates on one side and unrecoverable gaps on the other. Deduplicating on a stable identifier and recording every receipt has to be built on day one, because an outage longer than the retry window is a permanent data loss.
- · 7 min read
Put the Catalogue in Data and Growth Becomes a Row Instead of a Release
Every product has one thing certain to grow: the list of cities, currencies, carriers, document types or tenants it supports. Where that list lives is decided on day one and cannot be revisited cheaply, because by the time it matters the launch set is wired through the application. Modelled as data, adding to it is a row. Modelled as code, it is a ticket, a deployment and a regression pass, forever.
- · 7 min read
Prove You Can Get the Data Before You Build the Thing That Needs It
For products whose value is access to someone else's data, the risk that ends the project is not the engineering. It is whether you can obtain the data at all, on terms that permit your use case, at a price the product can carry. That question is answerable in days for a few hundred pounds, and it is routinely answered last, after the architecture has been built on an assumption.
- · 7 min read
Milestones for a Buyer Who Cannot Read the Code: Make Each One Independently Checkable
A non-technical client cannot evaluate progress by looking at the work, so milestones built around internal stages leave them trusting rather than knowing. Structure the stages so each ends in something they can verify themselves, put the milestone that could end the project first, and put the one you control least in the smallest envelope.
- · 7 min read
The Seam Is Nobody's Job: Why Failures Live Between Systems That Each Work Correctly
Three systems, each owned, each correct by its own contract, each reporting success. The failure lives in the relationship between two of them, and relationships are not assigned to anyone. That is why these defects survive code review, pass every component test, and are found by a customer rather than by a team.
- · 6 min read
Who Hears About the Failure First: Five Ways to Buy the Same Integration
The same piece of work can be bought from a platform, an agency, a specialist, a generalist or in-house, and the engineering is similar in all five. What differs is who finds out when the boundary between two systems breaks, how long it takes them, and whether anyone is contractually obliged to care. Choose on that rather than on price.
- · 7 min read
Attribute at Capture, Never Infer Later: An Untagged Record Belongs in a Bucket, Not in an Estimate
Attribution reconstructed after the fact from session data, timing or traffic patterns is a model presented as a measurement, and it is confidently wrong in the cases that matter most. Capture the context at the moment the thing happens, store it on the record, and put what arrives without context into an explicit unattributed bucket rather than apportioning it.
- · 9 min read
Read the Metadata Before You Read the Spec
A specification document carries forensic information its authors did not intend to send. The rendering engine and version reveal how it was produced. Route names in a module list reveal the framework it was designed against. Word frequency reveals what the client actually cares about: one specification used the word audit 73 times. Reading the document as an artefact is a discovery technique.
- · 7 min read
Build or Buy Your Agent Infrastructure
Almost every agent platform sells the part that is easy and leaves you the part that is hard. The loop, the tool dispatch, and the trace viewer are commodity. Whether your system knows it made progress, what it does when it gives up, and what stops a call tree are yours regardless of what you buy.
- · 7 min read
What Happens to Your AI Feature at Ten Times the Traffic
An AI feature does not scale like the rest of your product. Its cost is per-request and does not amortise, its latency degrades in a shape most load tests never produce, and its failure mode under saturation is to return plausible garbage rather than to reject the request. All three arrive together, on the day the feature succeeds.
- · 7 min read
Multi-Tenant Retrieval: Leakage Is a Design Decision
Cross-tenant leakage in a retrieval system is not an accident that happens to careless teams. It is the default outcome of a design that treats tenancy as a filter applied at query time, because a filter is something that can be forgotten, bypassed, or applied one layer too late.
- · 7 min read
Signs the Failure Is in the Seams, Not the Model
Every component passes its own tests. Every team's investigation ends politely at their boundary. The system does not work. Five signs tell you the failure lives between components rather than inside one, and they matter because a seam failure bought as a component engagement is money spent on the wrong question.
- · 7 min read
What a Two-Week Diagnostic Should Deliver
A diagnostic is not a discovery phase, an architecture review, or a slide deck of observations. It is a finding with evidence attached, a ranked list of what else is wrong, and a scoped fix you could hand to a different vendor. If the deliverable cannot be handed to someone else, it was a sales document.
- · 7 min read
How to Tell Your Vendor Is Aiming at the Wrong Thing
The expensive failure is almost never a vendor who cannot execute. It is a competent team executing precisely, on the wrong component. That is much harder to detect, because everything looks like progress, and there are four tells that show up before any code does.
- · 8 min read
What It Costs to Fix a Failing AI System
Nobody in this field answers the cost question straight, so here it is. The price is not set by your system's complexity, it is set by which of four diagnoses you land on, and the four are separated by more than an order of magnitude. Any quote produced before the diagnosis is a quote for the most expensive branch, because that is the only one you can price without looking.
- · 8 min read
Rebuild or Repair? How to Tell Before You Approve Six Months
Two contractors quoted four and six months to rebuild a model. The actual fix took five days and was not the model. The decision between rebuilding and repairing is not a judgement call, it is a diagnosis you can run in an afternoon, and running it first is the difference between a five-day fix and six figures spent reproducing the same failure.
- · 8 min read
The Third Option: Large Integrator, Narrow Specialist, or One Accountable Owner
An organisation with a hard cross-layer production problem weighs four options, and each is structurally wrong for it in a different way. The large integrator is broad and shallow with hand-offs at every seam. The narrow specialist is deep in one layer and hands off at its edge. The AI vendor sells the five percent. Staffing gives you hands without an owner. This is what the fifth option is and when it is not the answer.
- · 7 min read
Shadow and Canary: Shipping an AI Change Without Betting the Business
Two unglamorous techniques between a change and everyone. Shadow runs the new version against real traffic without showing anyone the results, so you find out how it behaves on production inputs before a single user sees it. Canary releases it to a small fraction with an automatic undo. The alternative, which is what most teams do, is to flip it on for everyone and hope.
- · 7 min read
Your Agent Is Missing a State: Done, Gave Up, Killed
Chess has a draw, which is not a loss. A solver returns unknown, which means it ran out of budget and not that no answer exists. A database names a deadlock victim so the transaction knows it was killed. Your agent has two states, done and killed, and when the cap trips the run must be filed as one of the two it has a name for, so it gets filed as done. Everything downstream believes it.
- · 11 min read
The Eighty Percent That Separates an AI Agent Demo From a Platform
Building the agents is about 20% of the work. The 80% that decides whether an agent survives production is the part everyone skips: reconciliation when systems disagree, confidence thresholds that return no answer instead of a wrong one, validation before any real action, and the cost and observability work. A better model does not touch any of it, and all of it is buildable today.