Background
Once all the features were in, the places that break in production started to show. Memory leaked slowly at the end of long meetings, stale closures piled up listeners at the moment of reconnecting after returning from the background, and when the WebSocket received a malformed payload, the exception thrown by JSON.parse took down the screen outright — all sporadically.
The problem was that where to start was unclear. Trying to fix everything at once had no end, and fixing just one spot let another one break again.
Approach
First, I cast a net. I placed an ErrorBoundary at the top of the app tree so that an exception drops to a fallback instead of killing the screen. Next, I gathered the lifecycle policy in one place — a lifecycle manager singleton automatically disconnects after 7.5 minutes in the background, and a network monitor is responsible for reconnecting.
For the leaks themselves, I fixed all four causes together in one PR: made the connection service’s timeouts trackable, explicitly released the timeouts on store subscriptions, released the BackHandler subscription on component unmount, and cleared the notification store’s timers on meeting leave.
By storing handlers and timeouts directly on the track object, I made it possible to grab exactly the same reference again at release time.
Finally, I cleaned up console noise across the entire codebase. I applied __DEV__ guards to 166 console.* calls so stdout stays empty in production, and in the same commit removed 4 unused dependencies, cutting package-lock by 872 lines.
Results
__DEV__guards applied: 166 console calls across 55 files.- 4 memory leaks fixed at once (timeout tracking · unsubscribing · BackHandler cleanup · timer clearing).
- Audio-level listener leak removed — blocks 5,000 event dispatches per second (based on 100 people × 50 per second).
- 4 unused dependencies removed → package-lock −872 lines.
- 5 runtime crash guards added, including a WebSocket
JSON.parsetry-catch, aNumber()NaN guard, and guaranteed ordering forPromise.all.
What I Learned
The hard part of stability work is that the results are invisible. The better you do it, the more nothing happens for users. That made it important to leave the names of the places that broke in PRs and commit messages — the moment you write down “4 memory leaks,” those four become a record that cannot break the same way again.