dmitry@echowaves — ~/echowaves/builds/wisaw

WiSaw: how a seven-day app was rebuilt four times

WiSaw is the app I keep returning to, so it is the honest record of my architecture decisions. Each rewrite was forced by something real: traffic I could not afford to serve, a platform shift I did not want to fight, a crash that only happened on other people's phones.

WiSaw is free and has no accounts; version 7.x ships on iOS, Android and the web.App StoreGoogle Playwisaw.comHow it was built

That is how it all began

Before WiSaw there was Echowaves, a minimalistic social sharing platform built on the theory that most of what makes social apps heavy is also what makes them miserable. In 2018 I stripped the idea down far enough to test whether it could survive without accounts at all. The first version took seven days: Minimalistic Social App in few days.

The rule that came out of it has not changed since: if a feature does not earn its complexity, the app does not get the feature. Minimalistic architecture for Minimalistic product is the longer argument.

The architecture, in the order it happened

  1. 2018

    Seven days: two native apps and a REST API

    iOS and Android native clients, a Koa 2 REST API on AWS Elastic Beanstalk with autoscaling, RDS PostgreSQL. Deliberately boring, deliberately small: one language across the stack (JavaScript), React where there was a browser, few enough moving parts that one person could hold all of it — which is the actual constraint of a side project.

  2. 2019 → 2021

    One client codebase, functions instead of servers

    Two native codebases stopped paying rent: clients moved to a single React Native codebase and then to the Expo managed workflow. The Koa service moved to AWS Lambda, so an app that idles for weeks costs nothing. Class components and higher-order components became functional hooks, with performance work in every step. Writing the pieces I needed and giving them away came out of this era: expo-cached-image, expo-storage, expo-masonry-layout.

  3. 2021 → 2023

    REST to GraphQL, because the feed is a graph query

    The product question was always "what is near me, right now" — which is a geo query with subscriptions, not a set of endpoints. Migrated to GraphQL with real-time subscriptions over WebSocket: AppSync, PostgreSQL for the geo data, Node handlers deployed as infrastructure code with AWS CDK in TypeScript instead of a console I would have to remember. Responsiveness improved immediately; anonymous posting got simpler because the client stopped guessing which endpoint to poll.

  4. 2025 → now

    Agentic rewrite: Expo Router, typed, and safe to touch

    The current shape is Expo Router with file-based routes, Jotai for local state, Apollo cache for server state, TypeScript through the client. This is the first round of rewrites done with coding agents in the loop, which changed what "cheap" means: a whole navigation redesign that I would have quoted at weeks arrived as a sequence of reviewable commits. It also made verification the bottleneck — see below.

The bug that made me a better developer

In 2021 my own iPhone started locking up while using my own app. Nothing in the crash reports, nothing in the logs; the screen went black, spun for half a minute, and dropped to the PIN screen. It never happened in development, only in production, and only after days of heavy use — so it looked like an Expo bug, or an iOS bug, or a me bug.

It was all three interacting. My image cache stored full-size files on disk and I reused them for thumbnails, assuming that if the bytes were already local, rendering them was free. On iOS, decoding an image costs memory proportional to the file behind it, not to the size of the box on screen — so twenty-odd thumbnails in a scrolling feed could hold enough decoded bitmaps for the OS to kill the app silently for memory pressure. The cache accumulating on disk is what made it take days to appear.

The fix was one line of discipline: if there is no real thumbnail, resize to the rendered dimensions before drawing. A bug that made me a better developer has the full hunt, including what I got wrong for weeks and why production-only bugs deserve their own methodology.

Two habits came out of it. First, the cache logic became expo-cached-image, so nobody else has to learn this the way I did. Second: a bug you cannot reproduce is an instrumentation problem, not a guessing problem — every "maybe it is X" step I took produced both a fix and a measurement.

Where AI changed the work

Not as a demo: on the production app, on screens real users see, in the last two years of this project.

Touch and layout, which was never my strength

The interaction surface is what held WiSaw back longest: nav bars that never got out of the way, buttons in places a thumb cannot reach, a detail screen with more chrome than content. In 2025 I put an agent (Claude 4 in Copilot agent mode) on it one component at a time. Roughly three hours across two iterations produced 22 commits: nav bar that hides its labels on scroll, drawer navigation, card-based detail screens, tag labels, dark theme, footers rebuilt around the bottom of the screen, and haptic feedback it added on its own initiative. Before vs. After: How AI vibe-coded a mobile app makeover shows the screens; Design Smarter is the model bake-off that chose the model.

Features that were previously unaffordable

AI image recognition now describes every photo posted, and AI auto-grouping sorts photos into Waves. Both are the kind of feature a one-person product never gets, because the prototype is cheap and the maintenance is not. Agents closed most of that gap.

The part nobody puts in the demo

Speed stopped being the constraint, so verification became it. An agent will happily produce a change that renders correctly and breaks the memory discipline described above. Everything I now build around agents is aimed at that: gates that catch it, measurements that show whether they work, and handover so the team owns both. That is the work I describe under How I work, and WiSaw is where I test it on myself first.

All buildsHome