Exploring a modern Cushion

I started writing code for Cushion in 2014, which feels like an eternity ago in the modern app world (or several lifetimes ago in the modern pandemic world). A lot has changed with tech in that time, like frameworks, browser support, and new native APIs, but even more has changed with myself—personal preferences, lessons learned, and simply acquired knowledge. Cushion is in a pretty good spot, considering its age, but like any app, it houses a considerable amount of legacy code. I did make a conscious effort to keep the codebase “respectable” from the start, but some early decisions, while right at the time, ended up being deep regrets down the road. While I was full-time on Cushion, I couldn’t afford to halt forward progress and straighten everything out. Lately, however, I’ve been really interested in correcting these flaws and exploring what a modern Cushion would look like if I were to build it now.

First, before diving into what I’d do different now, I’d like to share a few big regrets with the current codebase.

  1. I originally used AngularJS for the front-end before switching to Vue.js. While I did find a gradual way to replace AngularJS by embedding Vue.js instances within it, I never fully replaced AngularJS—mainly because Cushion’s routing is built on AngularJS and it’d require an all-or-nothing approach to convert the app. Also worth noting, I built Cushion over the course of several years, which means there are plenty of features I regret, so it feels like a waste of time to refactor the code for those features.

  2. To make the AngularJS situation even worse, I wrote it all with CoffeeScript. Keep in mind this was before ECMAScript inherited all of the good CoffeeScript features, like classes, arrow functions, and interpolation, so it was right for the time, but unthinkable now. Sure, there are plenty of tools for “decaffeinating” CoffeeScript into JavaScript, but again, the majority of the CoffeeScript code isn’t worth the time to migrate.

  3. The original build script used Gulp, which was the hot new stuff at the time and felt like it ran laps around other build libraries, like Grunt, but as soon as Webpack came along with features like hot module replacement, Gulp felt like watching water boil. Fortunately, this was one pain in my side that I did take the time to improve, albeit in a two-step process from Gulp to Weback 3, then from Webpack 3 to Vue CLI (which abstracts Webpack 4). I’m both embarrassed and obligated to share that with hot module replacement, the build time for code changes went from a minute and a half to under two seconds.

  4. The front-end isn’t typed or tested. When I coded the “redeux” of TeuxDeux back in 2012, I was really proud to have nearly 100% test coverage for both the front-end and backend. Achieving that was easier than it would’ve been with Cushion because there were no questions about what I was building—part of TeuxDeux’s design is that it’s essentially feature-complete. With Cushion, however, there were so many unknowns with the concept in general. I didn’t know if a given feature was right for Cushion long term or a gut reaction that I’d rip out later. This made testing the front-end not seem plausible while also trying to become a sustainable business on a limited timeline. Some might argue, however, that testing “slows you down” enough to make better decisions about what you’re building and how you’re building it.

  5. Related to good practices that encourage a focused scope, Cushion wasn’t built with mobile in mind. Probably the number one request I get from folks is for a mobile app or, at the very least, a responsive web app. If I included “must work well on mobile” as a guiding principle from the start, I’m confident it would’ve “slowed me down” enough to avoid building many of the one-off, gut-reaction features that sent Cushion off course. As Cushion’s codebase grew in complexity, it became harder to consider refactoring everything to work across all devices. Similar to refactoring the code of regretted features, refactoring these features to also work on mobile feels like a distracting time sink.

  6. Saving the biggest regret for last—we built too much, too fast. I’m convinced there was a point with Cushion, before I hired anyone, where if I were to stop building new features and simply iterated on the scope at that time, I could’ve had one of those do-one-thing-really-well apps that you only read about. My achilles heel in this case is that I’m a builder at heart—not a “haha” business person—so building new features brought so much more excitement than optimizing funnels or growth-hacking (dry-heave). To me, at the time, I thought that if I could build it (more features), they would come—and they did! But the red flags started to appear. As the app grew, I couldn’t clearly define Cushion’s main purpose—it’s a financial forecasting tool for freelancers, but it also has time-tracking, and you can schedule your daily workload, too. Then, I realized that folks were only using a couple of its features and neglecting the rest. I should’ve listened to what I think is the best advice for building an app—find an incredibly narrow wedge and remain laser focused on it, then expand. I had a wedge (freelancers), but it wasn’t narrow enough from the start, like “freelance illustrators in US cities whose eyes gloss over when they see a spreadsheet” or “wedding photographers in the suburbs who often hire subcontractors”. Each of these target demographics provides a crystal clear image to build around vs all freelancers everywhere.

Phew. Okay. That was a lot to get off my chest, but I think it’s time to let it out.

Moving forward, I’m going to start by improving the dev environment. While it’s definitely the more A-to-B task on the list that can easily be broken up into nightly chunks, it also affects everything from here on out. If I speed up the build time, that’s less time waiting and more time for actual progress. If I set up TypeScript and front-end testing, I’ll be more confident about big changes or refactors. You get the idea.

Here’s the initial batch of items on my list:

  • Migrate from NPM to Yarn

  • Install (and use) TypeScript

  • Switch from SASS to SCSS, but only use it for nesting

  • Use CSS variables where I would’ve used SASS variables

  • Establish test-driven development on the front-end with Jest

  • Start using Storybook instead of Cushion’s custom component explorer

  • Set up Heroku Review Apps for PR-based staging environments

While this seems like a lot, I’ve actually made a ton of progress already. I’m not going to kid myself, though—at the end of the day, I still have a full-time job at Stripe, whereas before I worked day/night/weekends on Cushion. That said, I do love having it back in my weekly routine. It feels so much healthier to shift my perception of Cushion to more of a “lifetime” project where I can make gradual progress over the course of years, rather than sprinting day-to-day with the feeling that I’m falling behind or running out of time.