What it solves
Showing up at unfamiliar water raises the same questions every time: what lives here, what will it take, are conditions any good right now, and is there a better spot or a bait shop down the road. The answers are scattered across a weather app, a tide table, a species guide and a map search. GoFish answers all of them from one screen, keyed to your current location, and keeps a private log of what you actually caught.
How it is built
A Flutter client sits on Firebase: Auth for email and Google sign-in, Firestore for catch logs, chat history and a location cache, and Storage for catch photos. Every service sits behind an interface with a Firebase implementation and a static facade, so tests can swap in a mock without touching a call site. Weather, tides, moon phase and precipitation feed a 0–10 conditions score, and a fishing assistant answers questions through Claude.
The hard decision
Third-party calls go through a server-side proxy, and that proxy is one function, not four.
Places, weather and LLM calls all need API keys, and a key compiled into an APK can be pulled out of it. Moving those calls into Cloud Functions keeps the keys server-side as secrets and gives one place to enforce a per-user daily cap on assistant messages. The first version used a function per upstream call, each held warm to avoid cold starts, and idle warm instances became most of the monthly bill. Folding them into a single proxy that routes on an action field cut four warm instances to one, and the call sites changed only in the name they invoke.
What it taught me
Moving the keys server-side was plainly the right call, and it still had a price I did not see until the bill arrived: every function I added to hide a key was another container kept warm around the clock. On serverless, the thing you pay for when nobody is using the app is the idle instance, not the request, so the number of deployables is a design decision in its own right. One routed entry point looks less tidy on paper than a function per endpoint, but it costs a quarter as much to keep ready, and it leaves exactly one place to put a limit.