We learned it in stages, each of them on a different layer.
OZORApp taught us that "offline-first" is not about the network being off, but about a network that lies: on the Hungarian EDGE network a call to the server neither succeeded nor failed — it simply hung, so the service worker waited forever and the app would not load at all, despite being entirely in the cache. Since then no project of ours makes a network call without its own timeout.
Inzerátor taught us to read an obstacle correctly: when a WAF blocks the hosting provider's IP, the wrong answer is to punch a hole into the home network; the right one is to reverse the direction and have a daemon listen on a database channel.
Health Bro taught us that a field name is not a contract — a field whose name promised seven days was quietly carrying fifty-nine, and the payload sent to the model had grown to a hundred and thirty kilobytes before anyone noticed.
Objatie taught us the bug need not be in the code: the deploy uploaded only part of the repository, so a registration living in a file that never shipped worked locally and returned a 500 in production.
Farangur taught us what is not worth building: working without a connection was a third of the design's complexity and it fell to a single sentence — we pack at home, on wifi. Next to OZORApp, where a dead connection decided whether the app opened at all, it is the same problem with the opposite answer. The difference is not technical; it is where a person is standing when they hold it.
Syntrofi taught us what a copy costs. We made the second generation by copying the first and rewriting the names in it — and broke sign-in at every entry point at once, because the path to the private data was suddenly called something else than the code expected. An event has been data rather than a fresh deploy ever since, and the deploy carries a guard that checks that path.
And the accounting system taught us the most expensive one: the hard part was never the code. The bank's consent expires roughly daily while the access token lives for months — so the token keeps refreshing over a consent that is already dead, the bank returns an error that never mentions consent, and you go looking for the bug in your own code. Nothing in the documentation says this. You find it the evening before payroll taxes are due, when a batch that cleared last month suddenly does not.