Brandon Trautmann

Irri (Side project)

The views expressed in this blog post are my own and do not reflect the views of my employer.

What it was

Irri was my attempt at a third party, modern, companion application for the first party supported Hydrawise mobile application. It was also the first app I started writing after starting to learn Flutter at Betterment. I started the project after being frustrated by some of the quirks of the first party application and I figured I could do it better. Lo and behold, their API was open! It seemed that this would be easy. But once I started digging in, it was a bit harder than I thought.

API limitations

Firstly, the API (at least the portion that was open) left a lot to be desired:

  • It exposed a user's sensors, but not their status
  • It didn't expose any portion of a user's irrigation schedule
  • It didn't expose information about the user's event history
  • It would occasionally return invalid JSON (such as during 401 events)

Essentially, you were starting from scratch. But the biggest challenge was that interacting with the API itself didn't have an incredibly modern feeling. Messaging, when returned, was short and not the best for being displayed to users (even though that seemed to be the intention). And maybe I'm being too picky, but all endpoints were suffixed with .php.

An API for an API

Since all my "side projects" are primarily aimed at learning, I decided this would be a great time to build out my own API that wrapped this one. That would decouple the front end from (some of) the issues I mentioned above, primarily those related to the ergonomics of the API. Since I was already working in dart on the front end, I decided to give shelf a shot. I actually really enjoyed working with it and found their to be solid support for everything I needed to do.

The problem with spinning up my own API was that I'd now sort of doubled my workload. Any time I wanted to ship a feature (which, in the beginning, is pretty much all you're doing), I had to ship the backend first. This is fine for a team of even 2, but can be tough when you're squeezing working sessions in between a full time job and being a husband and father. But it became even harder when I started to build out irrigation schedules.

Schedules

Because the primary part of having an irrigation device is scheduling when your lawn/plants are watered, and because these schedules weren't exposed by the first party API, I needed to allow a user to build out their own schedules from my application. This was a really cool exercise in schema design, because schedules weren't really something I'd thought too much about. What I ended up with looked something like:

  • A Program has a programId, a customerId, a controllerId (the irrigation device), a name, and a frequency which is an array of 7 bools indicating the days of the week the program runs.
  • A Run has a runId, runGroupId, zoneId, and lastRunTimestamp.

With these columns I was able to allow the creation and modification of programs. But I needed to be able to run them.

Google Cloud Platform

Because I was already using GCP to deploy my application (via Cloud Run) and host the data (via Cloud SQL), it was a simple enough decision to use Cloud Tasks to run the programs. Cloud Tasks does an awesome job of running tasks at the exact time you specify which was critical for these irrigation schedules. However, creating tasks meant interacting with Google APIs and without a first party dart SDK, it meant bringing in another language I wasn't incredibly familiar in. I chose Ruby, because we use Ruby at Betterment and so I'd toyed with it just enough to feel like I could figure things out.

This was a side project

So if you're keeping track, we now have three things to maintain:

  • The front end, an ideally fully-featured application that, oh, by the way, is supposed to look really good (spoiler alert, it didn't yet)
  • A robust middleware that talks to the first party API
  • A Google Cloud Tasks integration in a language I'm a novice in

None of this alone is enough to break the camel's back, but when you put them all together, I was finding it overwhelming to try and gain feature parity with the first party application while having less than 5 hours a week to work on this (in disjointed time slots). Not to mention, every month the Google Cloud bill was ~$10, so I was losing money (not much, but worth calling it out).

Deciding to stop

While my goal with this project was never to make money, I felt I had squeezed a lot of learning out of the endeavor. Namely:

  • An exercise in schema design
  • Shipping an API
  • Getting my feet with in GCP
  • Different time zone use cases (the user's timezone became important not only for data display but also for running programs at the correct time in the user's timezone)

And my last learning was recognizing my limitations with regards to my lifestyle. The most important thing to me is to be present for my family, and I wasn't able to do both that and work on this project. So I decided to stop working on it and to be more thoughtful about future side projects and understanding their scope and what I'm trying to learn before embarking on them.