When COVID-19 hit, my kids’ school, like many others around the world, moved over to a fully remote/online learning program. The school and some of the other parents were looking for ways to help the students keep in touch during lockdown. Over the course of a couple of weekends back in April, I built a little web app for them. It collects availability and matches kids for 1:1 online video playdates and allows them to sign up for extra “recess” classes facilitated by teachers. This was fun and it was nice to be able to give something back to the school community at such a stressful time. Building something that would be simple for the school to operate was an interesting challenge.
I want to share a few things I used on this project that made it much faster to build.
Google Sheets for admin
I needed an admin interface to provide student lists, class data, teacher availability and some other data. Building a UI for all this and teaching someone from the school to use it would clearly have been a lot of work. Having someone from the school edit JSON or CSV files was definitely not going to fly. But everyone at the school already knows how to edit a Google Spreadsheet. That’s how I decided to capture all the admin data. This worked remarkably well - it was easy to set up the sheet with data validation rules so that bad input data didn’t get entered and the admin UI on the main tool just has a single “sync from Google Sheet” button to press after changes have been made.
divjoy for awesome UI boilerplate
For previous projects, I’ve faffed about for literal days up front just to get the UI framework of choice set up, add some kind of user account system and get the project building and deploying. This time, I used divjoy which is a React codebase generator. With divjoy you choose the UI Kit, auth mechanism, etc and the tool generates a codebase you can deploy or start editing right away. I was fortunate to chat with Gabe Ragland, divjoy founder, on twitter right when I got started and he gave me early access to their Cloud Firestore bindings, which I used for this project.
The code divjoy generates is really beautiful and easy to understand. It makes a wonderful starting point for building a moderately complex app. Divjoy costs $60 for unlimited exports for a year. It was worth every penny in terms of how much of my chilling-out-at-the-weekend time was saved by starting with something so clean and ready to build on.
Firebase’s Cloud Firestore for the database
Because divjoy had the Firestore bindings built in, I decided to try it out as the database for this project. I had never used Firestore before. The Firebase APIs and documentation in general leave a lot to be desired (and probably the most time consuming part of this whole project was trying to navigate obtuse Firebase API docs). However, the database engine and in particular the live syncing web UI for it were super productive to build on. The free tier (“spark plan”) of Firebase pricing allows only 50K/day document reads. While the next tier of pricing (which I upgraded to) is pretty cheap (6 cents / 100K reads), I ended up having to think about the pricing structure when designing the schema / layout of my data which was surprising.
For my last few projects I have used cloud functions of one variety or another. I’ve chosen them to keep frontend and backend hosted together and in a single code base. For this project, the goal was simply to complete it as quickly as possible with minimum effort so I decided to return to using Heroku. It felt like a breath of fresh air to use a tried-and-true boring-old imperatively coded backend - everything just worked and I didn’t spend an ounce of energy fighting the runtime. I hadn’t used Heroku in years and was quite pleased to see a few of the quality of life improvements (e.g. first class connection to github) which have been added.
Some of the jobs which run on the backend are quite complex (e.g. generating the full playdate schedule for the week). Rather than structure the backend as an HTTP server (which might apply timeouts on handler’s completion), I made this a single job draining a work queue stored in redis.
The most fun part of the project was writing a scheduling function to optimize the playdates generated by the system. It matches kids optimizing for mutual availability (across 119 possible timeslots each week), prefers matches with friends who were specifically requested (across the whole grade) and then fills remaining desired slots with other kids from the same class. It’s an interesting global maximization problem. I ended up writing a greedy algorithm with random shuffles of the kids and the timeslots built in, wrapped in a loop with a solution scorer which picks the best scoring solution generated in the time available. Again, this was optimized for time to write, but it seems to do a pretty good job!
I’ll leave you with some screenshots of the app.
Scheduled on Google Calendar