Skip to main content
← Back to the trail log

Feb 2026 – present

Noboru

In progress

Role Co-founder. Backend and core mobile features.

    A hiking companion app for iOS and Android: browse trails and parks, collect passport stamps, and plan trips that keep working with no signal.

    Stack: React Native · Expo · TypeScript · .NET · AWS Lambda · Aurora PostgreSQL · SQLite

    trails in the catalog
    38,005
    National Parks and forests covered
    193

    The problem

    Hikers plan at home and then lose signal on the trail. Noboru is a React Native app with five tabs: Home, Explore, Community, Journal, and Profile. National parks, forests, and trails are real data, and a trip's itinerary, checklists, expenses, and notes have to stay usable when the network goes away.

    Noboru's Create New Trip screen, with fields for trip name, dates, trip type and destination
    Planning a trip
    Noboru's Community tab, showing a trail community with current conditions and recent threads
    Community
    Noboru's Journal tab, showing an adventure journal card and the latest park stamps
    Journal

    My role is covered in Expeditions. This page is about the product and how it works.

    The hardest stretch

    Offline-first trip planning on top of a backend that was online-only. I built it in five phases, each one a prerequisite for the next.

    1. A local SQLite cache, read before the network, so a trip opens instantly and stays readable with no connection.
    2. Client-generated UUIDv7 ids, with every create idempotent on its id, so a retried request never duplicates a row.
    3. A durable write outbox. An action applies locally at once, appends a mutation to an ordered queue, and never waits on the network. Creating then deleting the same item cancels both, so nothing is sent.
    4. An incremental pull against a per-trip change cursor, with tombstones for deletes. The cursor only advances after the changes are applied, so a failed pull retries the same range.
    5. A conflict policy for notes.

    Decisions

    Chose A .NET API on AWS Lambda behind API Gateway, with Cognito tokens

    Rejected The app talking to Aurora directly through the RDS Data API

    In the direct setup any signed-in user could obtain credentials that ran arbitrary SQL as the cluster's master user. Now only the backend touches the database, through a least-privilege role, and it decides who owns a row.

    Chose Client-generated ids

    Rejected Server-assigned ids with local-to-server remapping

    A row gets its real, final id when it is created, so it never needs remapping after it syncs, and a retry can be replayed safely.

    Chose One changes feed for first load and refresh

    Rejected A separate cold-load code path

    A trip with no stored cursor pulls since null, which the backend treats as everything. There is one path to keep correct instead of two.

    Chose Last-write-wins per field, except notes, which get a conflict copy

    Rejected A merge UI or a text CRDT

    A note is long-form writing, and silently overwriting it is the one clearly wrong outcome. A conflict copy never loses writing and needs no new data structure. Resolving it is just editing or deleting a note.

    See my role in the Expeditions section