Software does not travel
The risk is never the technology. It is the assumptions the technology encodes, and assumptions are invisible to everyone who shares them.
A platform that works perfectly in Amsterdam can fail in Nairobi without a single line of it being wrong.
That is the hardest kind of failure to act on, because there is no defect to point at. The code is correct. The tests pass. The architecture is one a competent reviewer would approve. What has happened is that the software carries a set of assumptions about the world, the assumptions were true where it was written, and nobody wrote them down, because nobody noticed they were assumptions.
I have spent most of my career building for markets that break them. Not as a specialism I chose, particularly. It is where I am from and it is where the work was. But it turns out to be an unusual place to have learned, because the things you are forced to stop assuming are the things nobody in a larger market is ever forced to examine.
The assumptions nobody writes down
Five of them are load-bearing in most modern systems, and false in a large part of the world.
Connectivity is binary. Systems are built for online or offline, and the real condition is neither. It is intermittent, asymmetric, and expensive per megabyte. A field technician in a rural district is not offline. He is online for ninety seconds at unpredictable intervals, on a connection that will truncate a large upload without telling you it did. That was the working condition for the operations and logistics systems I built on a government health programme in Africa. Retry logic written for a flaky Wi-Fi network in an office is not the same problem and does not survive contact with the real one.
Payment means a card. In much of the world it does not. It means a mobile wallet, a bank transfer with a reference number a human types in, a national switch, or cash on delivery that has to be reconciled days later. These are not Stripe with different branding. They settle differently, they fail differently, they have different ideas about what a refund is, and several of them will happily tell you a payment succeeded before the money has moved.
Email is the channel. Email is a channel with a low open rate and, in several markets, an address most customers do not check. The channel is WhatsApp. That is not a preference; it is where commerce actually happens, and it changes the architecture: message templates require approval, delivery is governed by rules you do not control, and the whole thing has a rate limit somebody else sets.
Providers persist. In a large market, the third parties you integrate with are boring and durable. In a small one, they are smaller businesses with their own difficulties. A provider can degrade, change its terms, or become uneconomic to keep, and the question is not whether that happens but how much of your system falls over when it does.
One jurisdiction covers everything. The moment a system serves users across a border, the data path crosses a regulatory boundary, and the boundary is almost never where the architecture diagram suggests it is.
None of these is a technology problem. Every one of them is an assumption problem, which is why competent reviews miss them: a reviewer who shares the assumption cannot see it, and there is nothing in the codebase to find.
| THE ASSUMPTION | WHAT IS ACTUALLY TRUE | WHAT IT COSTS TO FIND OUT LATE |
|---|---|---|
| Connectivity is on or off | Intermittent, asymmetric, metered | A sync model retrofitted after launch |
| Payment means a card | Wallets, transfers, national switches, cash | Reconciliation rebuilt while trading |
| Email is the channel | The channel is WhatsApp | A messaging layer under someone else’s rules |
| Providers persist | Suppliers degrade and change terms | A migration on their timetable, not yours |
| One jurisdiction covers it | The data path crosses a border | A disclosure gap with a regulator attached |
What this looks like when it fails in a transaction
The version of this that matters commercially is not a system falling over. It is a system working fine, and the risk sitting somewhere nobody looked.
Take the most common shape. A European company has one customer operating outside Europe. The engineering is good: that deployment was isolated deliberately, with its own connectivity assumptions and its own retry behaviour, because someone sensible built it. Everything about the architecture is defensible.
And the data for that deployment rests in a jurisdiction the company’s own privacy notice does not mention. Nobody made a mistake in the code. The disclosure simply never caught up with the architecture, because the person who wrote the privacy notice and the person who chose the region were not in the same conversation, and it is not the kind of thing a technical review looks for unless the reviewer has been on the other side of that border.
That is a finding a buyer needs before signing and it costs weeks, not quarters, to fix. But it will not be found by anyone reviewing the codebase as a codebase. It is found by asking where the data goes and then reading what the company has told its customers.
The same applies to the payment rails, where the diligence question is not “does the integration work?” but “what happens to reconciliation when the provider tells you a payment has succeeded when it has not?” And to the channel, where the question is not “do notifications send?” but “what is the plan when the platform changes its template rules?” Those questions do not occur to someone who has only integrated with providers that behave.
Why the constraint makes the software better
Here is the part that surprises people, and it is the reason I do not treat any of this as a handicap.
Every property in that list is one that serious engineers say they want and few teams actually build, because nothing forces them to.
Offline-first is a discipline almost nobody adopts voluntarily. It requires you to decide what happens when two versions of the truth meet, and that decision is genuinely hard, so in a good-connectivity market it gets deferred forever. If your users are in a district with ninety seconds of signal, it does not get deferred. You build a merge policy, you test it against real failure, and you end up with a system that also happens to be robust in Amsterdam.
Idempotency is the same story. When a payment provider might tell you something twice, or tell you something optimistically, you cannot write hopeful code. You put a fence on every mutating operation because you have to, and the result is a system that survives the retry storm nobody planned for.
Provider independence, likewise. If you have watched a supplier become untenable, you stop letting anything downstream quietly depend on it. On one platform I run, a messaging provider became uneconomic to keep, and moving off it took a weekend. That was not heroics. It was the return on having refused, months earlier, to let the rest of the system know or care which provider was behind that boundary.
So the honest summary is not that frontier markets are harder. It is that they remove your ability to defer the decisions that everyone should be making anyway. The architecture that results is more conservative, more explicit about failure, and more portable than the one you would have written with better infrastructure and fewer constraints.
What I do differently because of it
When a system has exposure outside its home market, I ask where the data physically rests, and then I read what the company has published about it. Those two answers are supposed to match and frequently do not.
I look for the assumption of a well-behaved provider. Not the integration quality, which is usually fine, but the blast radius: if this supplier degraded, changed its terms, or became uneconomic, what else would have to change with it.
And I ask what the system does under partial success, because partial success is the normal condition once you leave a market with good infrastructure. A half-completed sync. A payment confirmed to the customer and not yet settled. A message the platform accepted and never delivered. Every one of those is a state, and a system that has no name for it will invent one badly.
None of that is exotic. It is a reviewer’s list, and it is only unusual because of where I learned it.
The point
Software does not travel because assumptions do not travel, and the assumptions are the part that never appears in the repository.
If you are buying a business whose software runs somewhere its authors do not live, the code review is the easy half. The harder half is finding out what the system takes for granted, and whether the world it is going into agrees.