Engineering notes
Problems I have run into shipping mobile apps: POS hardware, releases, state, real-time flows and the details that make an app dependable. First shared on LinkedIn.
Printing images across Android app boundaries on POS hardware
The POS printer wanted a file path, and the file lived in a sandbox the printing app could not see.
Using Expo OTA updates on POS terminals
I had been rebuilding and reinstalling APKs for every change. The terminals only needed a new JavaScript bundle.
When a POS has a camera but no camera app
launchCameraAsync worked on my phone and failed on every new terminal. The hardware was there; the app to handle the intent was not.
What I learned upgrading from Expo SDK 55 to 57
Expected dependency bumps; got a lesson in component identity, hook naming and choosing what to upgrade into.
Measuring mobile performance with EAS Observe
Moving from “this feels fast” to launch times and time-to-interactive measured on real devices.
Separating server, persistent and interface state
Not every piece of state belongs in the same store. The question is who owns the truth.
Using Server-Sent Events for payment confirmation
Replacing polling with an event stream, so a trip becomes ready the moment payment is confirmed.
Crossing the React Native / native boundary intentionally
Cross-platform does not mean never writing native code. It means keeping the native surface small.
Why async workflows need more than isLoading
If a process continues after the first request finishes, it probably deserves a state machine.
Reliability is a user-facing mobile feature
Timeouts, retries and clear feedback made a field app feel dependable, which mattered more than looks.
Permission requests are product flows
Ask when the purpose is clear, explain a denial, and leave a way back to settings.
Deep linking is a coordination problem
When navigation depends on app state, “navigate now” is often the wrong instruction. “Navigate when ready” is better.
Debouncing is about more than reducing requests
Debouncing makes the system react to the input the user meant, not every keystroke on the way there.