Design

Restructuring an Evolving App: Part 1

Jan 23, 2018

Cushion is coming up on 4 years soon. With the anniversary approaching, I’ve been reflecting a lot. I think about how this side project has become my full-time job. I think about how I’ve been able to employ people. I think about how incredibly far we’ve come since the original idea of Cushion.

I’d like to focus on this last thought—how far we’ve come. As positive as it sounds, there’s also a negative side to it. In a positive light, we’ve built dozens of useful features that help freelancers every day—I’m proud of this. However, while building these useful features incrementally, we’ve squeezed a lot into an interface that wasn’t designed with the new features in mind. This resulted in several compromise-based implementations—specifically regarding the layout, routing, and UX.

While it’s easier to release features and improvements little by little, there comes a point where we need to take a giant step back and make sure everything fits together as best it can. After years with a layout that worked, we eventually found ourselves playing a game of jenga with the UI—carefully placing new pieces on top, rather than rethinking the layout to consider all the pieces at once.

To make matters worse, the current routing system was structured with complete disregard for hierarchy. Rather than following a breadcrumb pattern with levels, like schedule > overview > projects, we have something like projectsScheduleOverview—a single route that defines all of the route’s views from a flat perspective. I still shake my head at the original dev who I inherited the code from—me from 4 years ago.

layout comparison

In an ideal world, a route’s views would cascade down, like a Russian doll. A child view wouldn’t know about its parent, nor would it need to know. If we wanted to separate out a specific part of the app, we could extract the view at any point and it could function on its own. Instead, the routes are tied directly to each level of navigation. Extracting a view would be like pulling a piece from the bottom of the jenga tower.

steps

In addition to the layout’s UI, its UX didn’t age well, either. As Cushion grew beyond a single view, common workflows that once involved a single step now became two steps, or three. Creating a new invoice for a client now meant navigating to the invoicing section, knowing to click a button halfway down the page, then being taken to an entirely new page. Not only is this cumbersome for anyone familiar with Cushion, it leaves a terrible impression for anyone evaluating the app for the first time.

UI/UX aside, these are common workflows that freelancers are likely to experience at any given moment—meeting a new client, learning about a new project, sending an invoice to get paid, starting a timer. In the instant a freelancer needs to act, the last thought crossing their mind should be “Oh, I need to switch to the time-tracking section in order to start a timer.” Instead, this should involve one step—“Start a timer.”

I could go on and on about the issues that pile up over time, but that’s to be expected of any app that’s been around for years. Fortunately, we’re in a position to correct these flaws, and I’m determined to do so, instead of living with them. In the next post, I’ll detail the redesigned layout we’re working on, including the design process and technical challenges.


I criticize Cushion quite a bit in this post, but at the end of the day, I’m thrilled with what we’ve been able to accomplish as a nimble team of three. The fact that anyone pays for Cushion is a win in my book.

Story

My Typical Week as a Founder

Apr 14, 2017

Most of this journal’s posts were written as the result of a random thought that was worth writing about. I’d like to continue this, but also focus on what others are curious about. One of the most common questions I get is what my day-to-day looks like running Cushion. Since I wear many hats as the founder, designer, developer, support person, and now manager, I try to fill a lot into the week. Writing this down will be a good exercise for me as well to recognize any areas where I can improve.

Monday

I’ve come to accept that Mondays are meant for catching up. If I go into a Monday thinking I’ll be productive from a design or development sense, I’m always left disappointed, feeling like I didn’t accomplish anything that day. If I could only convince myself to count emails responded to, planning for the week, and coordinating with others, I’d feel much better at the end of the day.

Intercom

Most mornings, I wake up between 7:30 and 8am, make coffee, and sit on the couch with my cat while catching up on email, support, or even working on an idea I let marinate the night before. Around 10 or 11am, which is when my wife wakes up (she’s a night owl), I’ll start to get ready to leave for the studio. I’ve come to realize that my less stressful days are when I take the morning slow and get ready with my wife rather than rushing to the studio before she’s awake.

Craft

Depending on the week, I’ll either work with Carly Ayres on a Talking Shop interview, Andy J. Miller on a Ask a Freelancer podcast episode, or I’ll write in this journal. Each of these are on 3-week intervals, with exception for the journal, which I’m trying to keep to at least once a week.

Talking Shop

For Talking Shop, Carly has already interviewed the guest, transcribed and edited the interview, and coordinated with Ping Zhu on the illustration. Once everything is ready, I’ll add it to the website and queue a tweet for the next day.

Ask a Freelancer

For Ask a Freelancer, we have a TypeForm survey that funnels questions into a Google spreadsheet that Andy can pick from. In the afternoon, he sends me draft audio and a sketch for the illustration to approve. I listen to the episode and we text back-and-forth about the sketch until Andy‘s ready to take it to final. Once I receive the audio and illustration, I’ll upload the audio to Soundcloud, which feeds the episode to our iTunes podcast. Then, I’ll add the illustration and Soundcloud embed to the website.

Clearly, the work involved with me adding Talking Shop posts and Ask a Freelance episodes to the website could be done by an assistant or content person, but we’re not there yet. Until then, more work for me!

Tuesday - Thursday

TeuxDeux

I try keep a fluid schedule rather than sticking to an exact routine, in case one area of Cushion needs more attention than another. It’s also easier to handle a lost afternoon because of a meeting, which can kill my productivity and throw off my entire day (I’m not a fan of meetings). I don’t use a traditional calendar, but instead keep a reflowing list of to-dos and scheduled events in my TeuxDeux.

Studio

Our studio is within walking distance of my apartment, so I’m able to avoid the subway commute completely and enjoy a nice walk. Along the way, I think about what I need to do that day, so I’m ready to hit the ground running as soon as I get into the studio. Larry, our backend dev, usually gets in earlier than me. On most days, we code independently until noon, then walk somewhere for lunch. Getting out of the studio for lunch and eating together is really important to me. It’s especially valuable for when one of us is stuck on a problem or decision. After talking about it over lunch, we usually return the studio with a solution.

Coffee Time

I typically get my best dev work done after lunch, which allows for a solid hour of uninterrupted time. Around 2:30pm, I make coffee for the studio and we talk about random, non-work things. Since we work out of a co-working space, it’s a good time to catch up with everyone else outside of Cushion.

From 3 to 5pm, I get a second wave of solid dev time. I try to keep any design work for moments when I’m taking it easy. It’s easier for me to iterate on a few ideas outside of vital 9-5 work time. Depending on whether my wife is working late at the studio or not, I’ll either work late, too, and eat at the studio, or we’ll walk home together and continue working on the couch.

When I’m home, I try not to work as much, or at the very least, only work on aspects of Cushion that aren’t too important. Sometimes this means one-liner bug fixes or book-length support tickets that I needed more time to answer. I also save any work on the marketing website for after-hours, so I can save the main workday for the app.

Cooking

Lately, I’ve been learning to cook more, which really helps me get away from the computer for an extended period of time and not think about work. I also feel better at the end of the day when I’ve “worked” with my hands and ate food from our own kitchen, instead of taking the lazy route by ordering delivery. Some days, however, all we want is food from our favorite Thai restaurant.

Friday

I’ve learned that it’s best to only work on low-hanging fruit on Fridays. This could mean bug fixes, small features, or any non-app work. Taking care of simple tasks makes it so much easier to call it day at a reasonable hour end get closure on the week. When I work on a feature that hooks me into multiple days, I’m always left thinking about it the rest of the day, completely distracted when we’re trying to relax. Big features also lead into working the weekend, which is common, but designated more for cleanup work.

Friday

Around 4 or 5pm on Friday, everyone in the studio starts to call it quits and hang out at the space. Most Fridays, I’ll end up walking to Fictive Kin’s studio for “Pizza Friday”, where we catch up over pizza and drinks. This time is especially precious for me because I get to see a few friends who used to work in our space. We’re also able to completely relax knowing that the week is over.

Saturday - Sunday

While I know I shouldn’t work on the weekend, I always end up spending time on a few ideas I wanted to dive deeper into or maintenance work I didn’t want to waste weekday time on. Some of my better weekends are spent walking around Brooklyn with my wife or playing basketball with a group of friends at our local courts. Since my wife freelances, we usually end up at the studio at some point, but it’s much more laid back. On Sunday, I’ll start to prepare for the week ahead, clear my inbox, and cross off any to-dos that were left. If I have an idea for a journal post, I’ll designate time for writing. Then, repeat.


Photography by Sara Kerens

Dev

Building Components in a Sandbox

Apr 07, 2017

Recently, we started looking for a front-end dev to take some weight off my shoulders. We published a job listing that describes the position and how we work. It also includes the steps for our hiring process. After a short interview to simply discuss the applicant’s background and interests, we invite them to do a paid “assignment”. The assignment is to have them build one of our existing AngularJS components in Vue.js, which we’re in the process of migrating to. The assignment reveals if they can 1) code, 2) communicate, 3) match a screenshot, and 4) learn on-the-go. I created a private repo for the assignment, so there’s also 5) know how to use version control.

At first, I intended for the repo to be a sandbox for the applicant—a place for them to start coding with minimal setup and remain outside of our top secret repos. In setting up the sandbox, I took the opportunity to start with a clean slate. If we were to start building Cushion now, what would we use for the dev environment? Three years is a long time in the modern dev world, and even though our current stack isn’t “old” by any means, certain areas of it are collecting cobwebs—specifically the front-end and its build script.

Aside from using such a heavy framework like AngularJS, one of my bigger regrets is writing the front-end in CoffeeScript. *gasp* CoffeeScript is easy to write fast, but at the expense of a lazy and loosely structured codebase—AngularJS didn’t help with that either. Fortunately, JavaScript is catching up with most of CoffeeScript’s more appealing features, so we’re taking the opportunity to migrate to ES6 during our Vue.js switch.

As for the build script, Cushion is currently a combination of Gulp and Browserify, which isn’t ideal for quickly developing small parts of a large app. After reading up on the latest tech stacks, it seems like Webpack is the way to go. Vue.js even has a wonderful CLI for quickly starting up a new environment with everything we need. A few tweaks here and there and our assignment repo was ready for use. There was only one problem—the environment was too nice. I grew jealous. With Webpack’s hot reloading, the code updates almost instantly. With Cushion’s build script, I’ll make a change and stare longingly out the window, watching the world go by.

I also hated the idea of the repo being a throwaway. If we were to finish, I imagined copying over the components manually. That’s 2014 Jonnie’s way of thinking. 2017 is a new me. I decided to turn the assignment repo into Cushion’s new component library. Since the applicants would be recreating Cushion’s existing components in our new stack, why not build them to be production-ready? That would make the assignment even more worthwhile.

hello world component

I styled a page to house the new components and created a “Hello World” component as an example. From there, we could add each new component to the page and interact with them in a sandbox environment—separate from the app.

For the applicants who really wanted to make an impression, we even had a test harness that came with Vue.js’s dev environment. Components could be unit tested in isolation, so we really knew that they worked (and would continue to work if anyone needed to make changes). With proper unit testing, the component page’s would no longer be used for testing behavior. Instead, it could be used to ensure that the components’ interactions are up to par with the rest of Cushion.

components

We currently have six components in the sandbox with two more on the way. Once we reach a point where we can start using these in production, I’ll package the library and install it as a dependency. Whenever we need a component, I can simply import it and know that it’ll work as expected.

Dev

Reactive Time with Vue.js

Mar 29, 2017

Now that we’re diving into the day-to-day with time-tracking in Cushion, accurately indicating the current time and date is very important. As a timer runs, its duration should increase, but it should also grow in the graph as time passes. Likewise, as the current day carries over to the next day, the interface should update to reflect that today of a moment ago is now yesterday. Before reactive frameworks like Vue.js, this was a considerable task, but now it couldn’t be easier.

When we first released the time-tracking beta, we used a computed now property to indicate the current time:

We quickly realized that new Date is not reactive, so the computed property would cache itself and never update. Having a now property is still incredibly useful when dealing with time-tracking, but we needed to update the value on an interval, so the property wouldn’t remain cached. To achieve this, we used the setInterval function to update the now property each minute:

This worked well for a single component, but we rely on the now property in several other areas of the app, so we would need to update those values, too. I scoff at the idea of repeating this logic wherever needed, let alone running multiple intervals simultaneously, so we needed to find a better way. We needed a centralized now property that would update itself across all the components that rely on it.

In Cushion, we use Vue.js’s state management library, Vuex, which provides the centralized store we need to map global properties to our components. We moved the now property out of our component’s local state and into a brand new, rent-controlled time module within our Vuex store. This let us access the property from any component that needs to reference it instead of copy/pasting the interval everywhere like a monster.

We then added a start action to the time module, which starts the interval and commits the current time each minute—exactly like before, but now centralized:

In our components, we map the centralized now state to a local computed property, which updates itself when the property changes:

And, for components that only need to be updated when the day changes, we can add a getter to our Vuex module:

Cushion’s interface will now automatically update itself to accurately reflect the current time. In the time-tracking week view, today’s column will highlight the current day and move to the next one when we cross midnight. In the day view, the “now” label will move across the graph as each minute passes and any timer will follow along with it. I doubt anyone will notice or think about the additional work required to make this happen, but that’s the story of my life.

Dev

Visualizing Daylight Saving Time

Mar 23, 2017

Earlier this week, we launched the day graph for Cushion’s time-tracking beta. Previously, Cushion only had a week graph, which would let you add time throughout the week or start a timer, but there wasn’t much of a visual for when the entry occurred. Now, with the day graph, people can focus on a single day with labels for each hour.

day graph

This new day graph is our first foray into visualizing time. We’ve been lucky so far—only dealing with dates, which don’t need to worry too much about timezones or other time-related traps. Now that we’ve taken the red pill, however, we get to reunite with my old friend—daylight saving time.

If your country, or state (hi Arizona!), doesn’t participate in daylight saving time, you have it so good—you have no idea. For New York, daylight saving time started earlier this month, so we all “lost” an hour—meaning on March 12th, we skipped 2am. If you were watching a digital clock, you’d see time “spring forward” from 1:59am to 3am. Since most of the clocks in our lives are digital, everything happens automatically, but back in my day we had to move the hour hand forward ourself. And sometimes we would forget and our world would be in complete disarray!

With the day graph, daylight saving time caused several issues. The first involved the hour labels on the graph. Instead of creating 25 labels (one for each hour, and an additional for tomorrow’s midnight), we needed to create only 24—for days where daylight saving time started, because we lose an hour.

To account for this, I made use of the date.getTimezoneOffset() method, which returns the difference in minutes between the local timezone and UTC (universal time). After selecting the date to visualize, Cushion will check the timezone offset for the beginning of the day and the end of the day. If they match, great—there are a full 24 hours in the day. If they don’t, we’ve either started or ended daylight saving time. For now, we’re only going to talk about starting daylight saving time.

In my logic for calculating the number of hour labels to draw, I simply needed to apply the difference between the timezone offsets—no conditions needed:

numHours = 24 + 1 + (endOfDayOffset - startOfDayOffset) / 60

This takes care of the number of labels, but what about the hours themselves. Since we’re skipping 2am, we’ll have to skip that label as well (i.e., the graph should show “12am 1am 3am 4am”). At first, I took the start date and simply added an hour for each label. This should have worked, but unfortunately, date-fns (the date/time library I used) had a bug.

Specifically because of daylight saving time, if you want to add a duration (1 hour) to a specific time (12am), you want to literally add the duration. In JavaScript, you need to get the milliseconds since epoch of the given time (date.getTime()), then create a new date using those milliseconds combined with the duration in milliseconds (new Date(date.getTime() + duration)). After several hours of pulling my hair out, I discovered that the library I used was adding time by setting time. Instead of creating a new date with the milliseconds added, it was getting the current hour, then using date.setHour to increase the time.

This wasn’t obvious to me at first, so the hour labels kept reading “12am 1am 3am 3am 4am”. This happened because when setting the time (instead of adding it), 3 hours from midnight uses date.setHours(0 + 3), which results in 3am. Since we’re starting daylight saving time, however, when adding only 2 hours from midnight, date.setHours(0 + 2) will also return 3am because we’re skipping 2am. When skipping 2am, 3 hours from midnight should return 4am. We could easily avoid this problem by adding milliseconds.

I submitted a Github issue and the fine folks maintaining date-fns fixed it same day.

daylight saving time

Since daylight saving time is handled automatically now, a graph showing “12am 1am 3am” might catch people off guard and cause a bit of confusion. Because of this, I decided to add a marker for when daylight saving time starts. Using the same offset check between the start of the day and the end of the day, I’m able to detect when a day is experiencing a daylight saving time shift. Then, I simply need to narrow down which hour starts the shift and display the marker at that point. I even went an extra step and added a directional arrow indicating whether daylight saving time starts or ends.


For the small percentage of devs who made it through this entire post, I definitely plan to write the second half of the story where we deal with daylight saving time ending. Skipping an hour is easy, but how do we handle falling back? I’m seriously asking because we still need to figure that out. Until next time!

Dev

Recording Screencast GIFs

Mar 21, 2017

I use GIFs a lot. So much so that I wrote a blog post about the perfect GIF workflow using Alfred and Dropbox. Aside from reacting to tweets with a favorite Seinfeld clip, GIFs provide a fantastic way to capture and share new features in Cushion. I can record an interaction to really show off the little details that make it special, rather than simply sharing a screenshot.

licecap

For the past few years, I’ve relied heavily on a little-known app named LICECap. Despite the unfortunate name, this single purpose app was great for quickly and easily recording a segment of the screen as a GIF. After a while, however, I noticed that the quality of its generated GIFs wasn’t ideal. With Cushion’s subtle variation of light grey colors, LICECap had trouble distinguishing between them all and would often remove a few when optimizing. I also had to record a GIF several times to get it right because LICECap didn’t provide any way of editing a clip after the fact.

screenflow

I decided to try a different workflow, which involved multiple apps and several additional steps, but would preserve the colors and quality I was after. In this new workflow, I would record the interaction using ScreenFlow—an app for recording and editing video screencasts. After exporting the screencast, I would import the video file into Photoshop and save for web as a GIF—making sure to optimize it enough to reach the 5mb maximum filesize for Twitter uploads.

Phew.

This workflow certainly wasn’t ideal and took much longer than using LICECap, but the GIF quality it produced was worth it... until recently. Earlier this year, I stumbled upon an app created by Giphy, the internet’s beloved GIF search engine. The app is called Giphy Capture and it’s by far the best way I’ve found to record a screencast GIF.

giphy capture

The app itself is expectedly rough and quirky—even the filename, which is in all caps. When you open the app, it shows a transparent, cyan window, which is your recording area. Clicking record starts capturing the screen, then clicking the stop button ends the recording.

giphy capture edit

Once you’ve finished recording, Giphy Capture lets you trim the clip or edit its dimensions, frame rate, or looping style. Even if you maximize the settings to full size and a high frame rate, the filesize is still incredibly small, so I choose these settings every time. Unfortunately, the app doesn’t have a way of saving default settings, so each time I record a GIF, I need to adjust the settings before saving.

giphy capture save

When you’re ready to save the GIF, you can either download it as a file or upload it to Giphy. I save the GIFs to my hard drive, so I can keep local copies and easily upload them to Twitter or our website.

giphy capture result

Compared to LICECap and ScreenFlow, I’m blown away by the quality of Giphy Capture’s GIFs. The frame rate is smooth, Cushion’s light grey colors are defined, and the filesize is surprisingly small. Despite its scrappy feel, Giphy Capture is my new go-to tool for recording interactions. If you need a screen recording app for GIFs, definitely give Giphy Capture a try.

Story

Writing a Job Listing

Mar 16, 2017

Recently, we realized that we need to hire a front-end dev. I think it was when we discovered that our GitHub Issues list is 10:1 front-end/backend, or the dozen features we launched on the backend, but are hidden because they have no front-end. As the founder, designer, front-end dev, and customer support person, I’m spread pretty thin, but I make up for it by spending my time wisely and working all hours of the day—the passion for what we have planned keeps me going.

On my own, I was able to keep a consistent pace, but once I hired our backend dev, Larry, the pace changed. Larry is really fast and only focuses on the backend, so while I’m designing a new feature or writing a post like this one, he’s already wrapping up another feature. In his first month alone, he launched three major features almost entirely on his own. I absolutely love the progress, but feel bad for not keeping up. I decided that it’s time to hire.

This is terrifying because the process itself is completely new to me. With Larry, I worked with him in the past and he expressed interest in joining Cushion without me asking. Hiring him was simply a matter of letting him join. Our current situation is quite different—we don’t have an obvious person in mind who we’ve worked with in the past, so we need to look outside of our own network and post a job listing.

Writing the job listing was an interesting exercise because I had to really think about Cushion as a company and describe how we work as a team. I’m not a big fan of job listings because most of them are written in a way that leaves me more intimidated than interested (“You are this, You are that”, etc.) I wanted ours to be inviting and casual—identical to how we are as a team.

I started simply writing about how we work. Our schedule was never intentionally structured in a specific way, but rather an organic result of showing up each day. I spend the morning at home, then wander in the late afternoon. Larry gets to the studio early and leaves before the rush. We sync up when we need to, but otherwise, we know what we need to accomplish.

Describing what we need in a front-end dev was the tough part because I latched onto specific tech—must know SASS, bonus points for knowledge of Vue.js, etc. My friend Rik Lomas of SuperHi recently hired a few people, so he had a several pointers for me. The most important one was to dial down the tech specifics. By listing Vue.js, we’re filtering out people who see that as something they don’t know rather than something they can learn. We mention that we’re interested in someone who is passionate about learning, so if the person is familiar with any JavaScript frameworks, Vue.js will be easy to pick up.

next steps

We kept the job listing itself concise and to-the-point, but at the end, I didn’t want to have a simple “Apply” button with a mailto link. Every company has a different hiring process, but unless you know someone who has gone through them, each step is a mystery. Keeping in line with our transparency, I decided to list the steps that we intend to follow with our hiring process, which involves applying, an interview, a paid assignment, and a 3-month trial period. By being transparent about it, there are no surprises. I doubt we’ll have anyone decide not to apply because of one of these steps, but it’ll at least give them a chance to come prepared with any questions.

typeform

I also wanted more structure to the application process itself, so we could get the factual information upfront, then go from there. I decided to use TypeForm, which we use for our Cushion surveys. This made it quick and easy for people to apply (compared to most job listings) while funneling all of the applicants into a spreadsheet for us.

zapier

Since the team is more than myself now, I wanted to involve Larry in the process, too. I set up a Zapier integration with TypeForm to post new applicants into our Slack channel. Now, both of us can check out the person’s portfolio and answers as soon as they come in. Even if the team is currently only the two of us, I still value having everyone on the same page with the same information.

So far, we’ve received a few dozen applications in the first day alone and I couldn’t be happier about that. Before immediately moving onto interviews, however, we’re going to let the listing sit for now to give people enough time to see it and apply. So, if you’re a front-end dev, interested in building tools for freelancers, we’re hiring.

Dev

Using Feature Flags to Run Betas

Mar 08, 2017

Lately, we’ve been releasing most of our larger features as betas first. We do this in order to launch an MVP of the feature, then have people test it while we incrementally improve upon it. For invoicing, we ran betas for line items, invoice logos, and sending invoices. Currently, we’re running betas for time tracking and accepting payments through Stripe. We use “feature flags” to toggle these betas.

In all honesty, there’s not much involved in implementing feature flags. On the user model, we have a features column, which is an array of strings. Each string is a key that we use to identify a beta (invoicing, time_tracking, etc.). When a user asks to be part of a beta, we simply add the beta key to the features column and they’re good to go.

On the front-end, all we need to do is show or hide features based on whether the keys exist in the user’s features array. For the Stripe beta, we enable the ability to authorize the integration. For the time tracking beta, we simply show the tab for the time tracking section. This way, the backend never needs to know about anything.

Well, almost never.

So far, the only beta that was tricky to toggle was invoices with line items. Previously, we only had invoices with a total amount, so the line item beta required the backend to handle both types of invoices. Still, this was easy because it simply meant calculating a total for line item users and manually entering a total for everyone else. After that beta, we decided to only release betas with a simple toggle on the front-end.

For customer support, we use Intercom, which lets us tag users with specific properties. We tag beta users with the features that they’re testing and we segment them to send messages to only the testers. For our time tracking beta, we created an automated onboarding message that appears when beta users visit the time tracking section for the first time. This has been incredibly helpful for setting expectations and listing the sub-features that we’re actively working on.

As we release new betas and people request to be a part of them, using Intercom tags, we can easily see if the person has tested a previous beta. If someone has tested multiple betas, we could easily message them when a new beta is ready to test.

Because betas have a “private“ feel, most people assume that we limit the number of “spots”. We actually welcome anyone who asks. As long as they’re well-aware that the feature is in-progress and might have a few loose ends, we’re always open to another set of eyes.

If you’re interested in testing any of our new features, hit me up inside Cushion and I’ll invite you to the beta.

Story

Our First Company Lunch

Feb 20, 2017

Ever since moving to New York and freelancing, I’ve worked out of a co-working space, which is now where we work on Cushion. Our co-working space is more of a tight-knit community than a rent-a-desk situation, so we all eat lunch together. Lunch is very important at our space—we even have a #lunch-train Slack channel to plan our lunches and handle reimbursement.

Occasionally, someone has a great idea for lunch, which has become known as “adventure lunch”. On a typical day, we’d pick up food from a nearby place, order delivery, or bring leftovers from home. Adventure lunch is when someone suggests going to a restaurant that involves a long walk or ride on the subway—it’s a real treat and a great way to close out a long week.

An extension of the adventure lunch is what we call the “executive lunch”. We have a few small companies at our space among a wider group of freelancers. Whenever one of us is taking an executive lunch, it’s simply an adventure lunch that’s business-related. Recently, Cushion took its first-ever executive lunch.

Larry and I rode the subway to DUMBO, walked past our old building at 10 Jay St, and ended up at the finest restaurant for a business meeting—Shake Shack. We talked about our upcoming plans for Cushion, discussed how everything’s been going, and brainstormed what we could do to improve. We also made the mistake of ordering double burgers, cheese fries, and milkshakes. Larry had the brilliant idea of skipping a few subway stops, so we could walk off the extra pounds. We arrived back at the studio and got back to work.


I’m writing about this because I started my career in the corporate tech world where team dinners were at fancy restaurants—the kind of place I’d never dream of going with my own wallet. If we were with an exec, they would order for the entire table and only half of it would get eaten. The free-flowing drinks would catapult the bill into the thousands.

I think about this a lot now, and even though we have funding, I’m still running Cushion the way I ran it when we were bootstrapped—the only difference is that we can now afford to hire more people. We don’t need to live beyond our means when something can be expensed. We can have just as good a time at our favorite burger spot.

Dev

How to embed Vue.js & Vuex inside an AngularJS app... wait what?

Feb 12, 2017

Since launching Cushion a couple years ago, we’ve been using AngularJS (1.x) as our front-end framework. If you’re curious how we ended up with Angular, I wrote about the decision (keep in mind this was 2014). I came from an MVCS background, so Angular really resonated with me, and it has served us well this entire time. Recently, however, with our improvements to the scheduling graph, we reached a point where performance has become an issue.

In previous attempts to improve performance, I found myself using lodash’s lightweight template function within Angular, which made me feel like I was cheating on Angular. In times when performance was absolutely crucial, I had to write raw JavaScript, which I have no problem writing, but it’s not exactly ideal for quick prototyping or iterating.

Now that our next big feature is time-tracking, I wanted to take this opportunity to try a new approach. What’s exciting this time around is that 1) there’s this new thing called the virtual DOM, and 2) almost every modern framework has adopted it. I was eyeing React last year (because my west coast friends wouldn’t stop raving about it), but it never really resonated with me—especially once you get into the whole Flux, Redux, Muppetdux, Hufflepux thing (those last two aren’t real). At one point, React was the only option if you wanted the performance of a virtual DOM, but one-by-one, every other framework caught on. Now, there are faster frameworks and some with a more familiar syntax to what I’m used to—like Vue.js.

I tried Vue a very long time ago, before it was even 1.0. The creator, Evan You, borrowed a few ideas from Angular and had a similar syntax, so Vue felt like a younger brother to Angular—a lightweight alternative, but not quite there yet. Because of its solid grounding, I chose Angular. While I started building Cushion on Angular, Evan began working full-time on Vue. Fast-forward several years and Vue is the new hot framework. It also feels much more solid than when I previously tried it, and it’s incredibly fast because of the virtual DOM—perfect timing for me.

I gave Vue another shot and was immediately blown away by how powerful it is while still so easy to use. I also appreciated all the nice touches that were added, like shorthands (@click instead of v-on:click) and event modifiers (@click.prevent to call event.preventDefault). Vue is now the lightweight alternative I’ve been searching for, and more importantly, it immediately makes sense to me.

The problem now is that I have years of code written with Angular and there’s absolutely no chance of me taking the time to replace Angular in one shot—especially if I want to make any progress on actual features. As a first step, I wondered if I could take advantage of Vue’s speed by replacing Cushion’s graphs with Vue components. I reached out to Evan—would it be possible to “embed” a Vue component within Angular? Apparently, yes! Evan pointed me to this article that suggests wrapping the Vue component with an Angular directive and using the ng-non-bindable directive to tell Angular not to interact with Vue’s HTML.

I tried using this technique and it does, in fact, work... but it’s not ideal. If I ever wanted to communicate between Angular and Vue, I would need to trigger Angular’s $apply method with every change within the Vue component. While this would be possible with some inconvenience, it didn’t feel right. Even though I could do it, doesn’t mean I should do it. The same could be said about embedding Vue within Angular, but I wasn’t ready to give up. I had one last idea.

Similar to React’s Redux, Vue has its own state management library—Vuex. Vuex keeps the app’s state in a centralized store rather than in the components. In my previous Angular/Vue experiment, Angular would’ve been the centralized store, as long as I called $apply with any changes—and I could only send data to Angular through the directive. With Vuex, a new possibility presented itself—what if I built time-tracking as a Vue app within Cushion? (I know this sounds so wrong, but hear me out.)

Without using Angular’s state management or http services, it serves simply as a router in Cushion. And considering Cushion’s tabbed navigation structure, I could contain an entire app in one of its tabs. Vue’s view model instance is incredibly lightweight and performant, so when a user switches to the time-tracking tab, Cushion can mount the instance and make any requests to the API from Vuex. Then, when the user navigates to a different section, Cushion can destroy the instance and clean up any loose ends. At this point, I no longer need to write hypothetically because I actually did this and it works really well.

I decided to go a step further. At its core, Vuex is simply a JavaScript object that stores data. What if I wanted more than one Vue instance to communicate with each other? For example, if a user starts a timer in the time-tracking section of Cushion and navigates to another part of the app, I’d still want them to know there’s a timer running. As long as Vuex is still available for the timer, I could destroy the time-tracking instance and mount another instance for the timer. Apparently, it couldn’t be simpler.

In Angular, there are providers, which are by far the most confusing aspect of Angular. (After three years writing an Angular app, I still need to reference the documentation for these, and they still confuse me.) One of these providers is called a “service”, which can be used to create a single store to reference throughout the app. All it needs is a function that returns an object. With a single line of code, I can return Vuex as an Angular service. Now, I can reference the Vue app’s state across the rest of Cushion and inject it into any Vue instance by simply specifying the store. On top of that, this opens up a way for me to migrate Cushion from Angular to Vue in steps, rather than rewriting the entire app all at once.

When we’re ready to start migrating other sections of Cushion, I can start treating each section as an app. I could rewrite the Angular controllers as Vue instances, and simply create new store modules in Vuex. Everything remains modular, and I wouldn’t abuse $rootScope as much as I do right now. Eventually, I’d be able to replace Angular’s router with Vue’s router and shake all the excess weight.

So far with Vue and Vuex, I’ve been able to create a much more clearly defined app structure, with state that “can only be mutated in a predictable fashion” (to quote the Vuex docs). I’m also taking this opportunity to ween myself off CoffeeScript in favor of ES6. As long as I’m improving one aspect of my dev life, I might as well kick another habit.


Learn more about Vue & Vuex.

Archive

  1. Restructuring an Evolving App: Part 1
    Design
  2. My Typical Week as a Founder
    Story
  3. Building Components in a Sandbox
    Dev
  4. Reactive Time with Vue.js
    Dev
  5. Visualizing Daylight Saving Time
    Dev
  6. Recording Screencast GIFs
    Dev
  7. Writing a Job Listing
    Story
  8. Using Feature Flags to Run Betas
    Dev
  9. Our First Company Lunch
    Story
  10. How to embed Vue.js & Vuex inside an AngularJS app... wait what?
    Dev
  11. Funding Cushion
    Story
  12. Hiring a Team of Freelancers
    Story
  13. Taking a Real Break From Work
    Story
  14. Slack as a Notification Center
    Dev
  15. Document Your Features
    Story
  16. 300
    Story
  17. Vacations
    Design
  18. Offering Discounts
    Design
  19. Waves of Traffic
    Story
  20. Less Blogging, More Journaling
    Story
  21. Retention Through Useful Features
    Design
  22. The Onboarding Checklist
    Design
  23. Spreading the Word
    Story
  24. From Beta to Launch - The Subdomain
    Dev
  25. From Beta to Launch - Sign up
    Design
  26. From Beta to Launch - Messaging
    Design
  27. Launch
    Story
  28. Authenticating with 3rd Party Services
    Dev
  29. Intro to Integrations
    Design
  30. Inspiration vs Imitation
    Story
  31. The Emotional Rollercoaster
    Story
  32. Designing Project Blocks
    Design
  33. Everything in Increments
    Story
  34. Deleting Your Account
    Design
  35. Designing the Subscription Page
    Design
  36. Rewriting the Timeline
    Dev
  37. Restructuring the Individual Project Page
    Design
  38. Project Blocks
    Story
  39. Redesigning the Homepage
    Design
  40. Multiple Timelines
    Design
  41. Archiving and Estimate Differences
    Design
  42. Multiple Financial Goals
    Design
  43. Zooming in on the Timeline
    Design
  44. Currency
    Dev
  45. Preferences, Accounts, and a Typeface Change
    Design
  46. Sending Out the First Email
    Story
  47. Currency Inputs, Notifications, and Invoice Nets
    Design
  48. Dots and Lines
    Design
  49. Calculating in the Database and Revealing Tendencies
    Dev
  50. Improved Form UX
    Design
  51. Cushion is Online
    Story
  52. Schedule Timeline Patterns
    Design
  53. A Slimmer Schedule Timeline
    Design
  54. The Schedule Timeline
    Design
  55. Plugging in Real Data for the First Time
    Design
  56. Transitions and Project Lists
    Design
  57. Death to Modals
    Design
  58. The Individual Project Page
    Design
  59. Estimated Incomes and Talks with Other Freelancers
    Story
  60. Statuses to Lists and the Paid Beta
    Story
  61. The Timeline
    Story
  62. Invoice Terminology
    Dev
  63. Modal Forms
    Dev
  64. Wiring the Backend to the Frontend
    Dev
  65. Balancing Design and Dev
    Story
  66. Timecop, Monocle, and Vagrant
    Dev
  67. Going with Ruby and Sinatra
    Dev
  68. Ditching local-first and trying out Node.js
    Dev
  69. Switching to AngularJS
    Dev
  70. Building the Table with Vue.js
    Dev
  71. Clients, Projects, and Invoices
    Dev
  72. Introduction
    Story

Running Costs

Take a close look at the costs that go into running a web app and why we use specific services.

View the Costs

How It’s Made

Follow along with the journal for insight into the overall experience of building an app.

Read the Journal