Motivation
I’ve started listening to Maintainable and one of the Robby’s standard questions is, “Are you on Team Refactor or Team Rewrite?”
I’m on team refactor. The best way to achieve a rewrite is through incremental refactors.
Background
I joined MealPal in 2016 as the first engineer in NYC. The first version of the application — that helped prove product market fit and earned the company a Series A — was built on a PaaS called Parse. At the time, it was essentially a fully-hosted lambda-like API layer on MongoDB with a spreadsheet-like admin interface. Facebook acquired the company in 2013, and they announced the platform would shut down in January 2017. We had four months to migrate to something else.
The Parse shutdown was a blessing in disguise. It gave me the excuse to say no to new feature development and focus on a wire-protocol compliant replacement for our API. I chose Rails+PostgreSQL, and I was able to rewrite the entire API in about three months. It was faster, more reliable, and easier to maintain.
It gave us a solid, modern foundation to build new features on, while we “ate the elephant,” gradually hollowing out legacy controllers powering routes like POST /v1/functions/getKitchenMenu
in favor of canonical REST-ful routes like GET /api/v2/menu
.
Outline
-
How did we get here?
- What was Parse.com?
- Why did we use it?
- Why didn’t we stick with it?
- A Tiny Primer on Mongo vs PostgreSQL
- When did it shut down? January 2017
-
The Project Plan: How are we going to do this?
- Getting Executive Buy-In: Nothing motivates like the threat of your database being deleted.
- Don’t change client code (iOS, Android, Angular Web)
- Build an admin tool to replace Parse.com dashboard
- Two-pronged Strategy:
- Project Idol: Write routes in rails that quack-like exactly like their Parse equivalents
- Project Shadow: Replicate our parse dataset into a mongo database and run open-source parse on it.
Technical Milestones
- Translating MongoDB ids to PostgreSQL UUIDs (Base62 <-> left-padded UUIDv4)
- Rake tasks to bi-directionally fetch records out of Mongo into PostgreSQL via ActiveRecord.
- Deploying to Heroku with https://github.com/nonrational/heroku-buildpack-hub-spoke and https://github.com/nonrational/unibus
- QuantumLeaps!
Robinson Coderusso - A Developer’s Log
- Day 0 =>
rails new
- Day 10 =>
ParseDrive
– Build rails wrappers around Parse APIs and start to dump data - Day 17 =>
ParseModel
; mixin w/ attribute normalizers for ActiveModel<->JSON - Day 20 => Generate
ActiveModel
s; first migration from MongoDB schema - Day 21 => Create
ParselikeApiController
to support Cloud-Code function quack-alike routes - Day 22 => Bad day. Realized that Parse uses POST exclusively; goodbye naive caching strategies.
- Day 27 => Can freely convert
parseId
to UUIDs; Base62 <-> left-padded UUIDv4. - Day 40 => 18 routes down,
rake db:parse:import
can import mongodb to postgres - Day 60 => 35 routes down, ParseDrive Exports outgrew google drive, move to s3 upload
- Day 61 => 36 routes down, Moved from an integer timezone offset to time zone name for ActiveSupport
- Day 69 => QuantumLeap - A framework to allow to fake Time.current at the rails session level.
- Day 71 => Figured out how to deploy a monorepo with 3 apps and 2 engines to Heroku
- Day 75 => Moved the “parse-like” API routes to an engine,
rails new
on our Admin tool - Day 81 => 50 routes down! 95.5% code coverage
- Day 94 => Shim parse session token mechanism to avoid breaking mobile clients in the field
- Day 107 => Wait… who has our SSL cert? I thought you did!
- Day 108 => Go live.