Scoping a Personal-User Push

Toward the end of 2019, Dropbox recognized that while it had shipped significant new products like the new Dropbox and Dropbox Transfer for professional users, its personal-user base hadn't seen meaningful new value in some time. To address this, the company formed a team focused exclusively on personal users. After weeks of customer research, a clear set of wants emerged: a dedicated folder for important files, help with password management, and a way to share a plan with family without sharing an account.

Computer backup was also a priority, made possible by the rollout of a new sync engine completed in June. These needs led directly to the creation of Dropbox Vault, the integration of the password manager Valt, and a new Dropbox Family plan.

Engineering the Family Plan

The Family team was brand new, formed while the product spec was still being written. Before any code, the team had to answer foundational questions about user expectations and product scope, ranging from whether users wanted a shared folder or shared quota to what fundamentally differentiates a family from a workplace team.

Planning Before Code

The team invested heavily in architecture decisions upfront, aiming for a design that would fit current needs while leaving room to grow. It was understood from the start that plans would shift as theory met practice, but laying out a rough data model, API composition, and payment integration strategy prevented wasted cycles later.

Team Structure and Early Decisions

A critical early decision was to avoid building on the existing Dropbox for Teams infrastructure. That system is built for professional use cases with advanced sharing and permissions, features that would be unnecessary for family plans. Retrofitting it would slow the project down and force awkward abstractions. For example, the ability for a team admin to access any team member's files runs counter to what many families would expect.

The core team worked in a physical pod in the New York office, with each engineer contributing different expertise. One led frontend architecture, others handled APIs and the data model, and they sat close enough to constantly align designs across all layers of the stack.

Prototyping Speed

One key choice was building all Family management pages as independent applications rather than embedding them immediately into the existing Account Settings page. Testing directly on the real page would mean creating a Plus user, navigating to the account page, and inviting family members before testing core features—an edit/refresh cycle of two to three minutes. That was far too slow during the rapid iteration phase. Instead, the team built a developer sandbox with test fixtures, embedding only the root Family management component so it could be tested in isolation within seconds.

The team also introduced an in-house technology called API-QL, a layer between Apollo and REST endpoints built on Apollo's local resolvers. It implements a lightweight GraphQL server in the client, letting the team build REST endpoints while still benefiting from Apollo's caching and React hooks. All APIs were designed with API-QL in mind.

Alpha and the Value of Cutting Scope

Milestones for the project ran through internal alpha, external alpha, beta, and general availability. The internal alpha goal was simple: ship a minimal viable product to Dropbox employees to test basic plumbing and build confidence.

To reach that milestone quickly, the team constantly cut scope. Confirmation modals and nice-to-haves like contact suggestion and email reminders were pushed to later milestones. Those features weren't unique to Family plans and weren't what the team needed to test with their internal audience.

The milestone-based planning also proved valuable for onboarding a new frontend engineer joining mid-quarter. The contact suggestion feature was perfect as a starter project: it touched every layer of the stack but wasn't needed until a later milestone, letting the new hire ramp up without blocking progress.

Shifting Context to Vault

A few weeks later, the team that had built Dropbox Vault's minimal viable product in under nine weeks needed frontend technical direction. As my time on the Family team was wrapping up, I was set to begin ramping up on Vault in the first week of April.

This coincided with the COVID-19 work-from-home transition announced on March 13. Starting with a team onboarding during a pandemic required a deliberate approach: setting up one-on-one meetings with every engineer, product manager, and designer on the team—not waiting until the official transition date. Over time, weekly check-ins with the people I'd work with most helped keep things in sync remotely.

Once ramped up, the mandate shifted from individual features to codebase quality. The Vault frontend had been built quickly and was hard to test. The goal was migrating to a stronger foundation without slowing the team down—a process documented as "Vault Frontend: Refactoring in the Right Direction."

Making Team Standards Stick

Defining coding standards was only half the battle. To make them effective in a real product, the initial draft had to survive contact with the rest of the frontend team. After sharing the first version with two other engineers and iterating until the three of us were aligned, we had to justify the rules. Beyond the appeal of following modern best practices, there were concrete business benefits: cleaner code is more readable, easier to update, and simpler to maintain. Our measurable target was simple—fewer incoming bugs each week.

When we presented the document to the wider team, everyone bought in, and the codebase steadily improved. But no standard survives contact with reality indefinitely. One decision we made eventually proved to be a drag.

When a Good Idea Doesn’t Transfer

Based on my recommendation, the team adopted API-QL for API calls. It worked well for the Family team, which operated in a greenfield codebase where APIs were built with API-QL in mind from the start. For the Vault team it was another story. There we were retrofitting API-QL onto existing APIs, and the data models didn’t line up. Development on the Trusted Contact feature slowed noticeably.

The lesson was clear: an approach that works flawlessly in one context can be a poor fit in another. API-QL remains a strong technology and one Dropbox continues to invest in, but it wasn't the right choice for this project at this time.

Fortunately, the standards doc gave us a foundation we could amend. Reverting that decision and continuing to refactor in the right direction cut down on incoming bugs, made the codebase more pleasant to work in, and improved developer velocity overall. Just as importantly, the process of co-authoring the standards meant we all shared a definition of what good code looks like. That shared frame of reference made code review both faster and more consistent.

Entrypoints: Integrating With Code We Didn’t Own

As I wrapped up frontend technical direction for Vault, I moved to a related effort: entrypoints for new features. Features like Vault, Passwords, and Computer Backup are useless if users can’t find them. Dropbox can’t assume people will go hunting for new functionality—the product needs to meet them where they already are.

Although it sounds straightforward, this was a large collaboration involving product, platform, and infra teams. We had to dig into other teams’ codebases and integrate our new features with their existing surfaces. Two engineers were already working full-time on this, but one was about to go on paternity leave, so I joined to take over the frontend work. We had about two weeks of overlap, and then it was down to just me and Michael—I owned frontend, he handled backend.

This project had its own distinct challenges. With Family we had started from scratch. With Vault I joined a product in motion. For entrypoints, we needed to make a large number of small changes scattered across the entire codebase—from the web UI to the sync engine. Unlike the Family or Vault teams, which each had roughly ten developers, this was just the two of us. And unlike those projects, where we were concerned with architecture and code quality, here we were touching code we didn’t own. The priority was simply getting it done.

One memorable stretch had us pairing for half a day with an engineer from the sync team. We needed to show a warning when a user deleted one of their file system entrypoints, and neither Michael nor I knew Rust, the language the sync engine is written in. Bringing in an expert for a day solved that. Pair programming in a language you don’t know feels like having superpowers, and it was a kick to say I’d touched the new backup feature—even if it was only a few lines. We got the change into the client just under the wire and watched nervously as it shipped to users.

Launch and a Payments Scramble

The rollout to market used Stormcrow, Dropbox’s internal gating technology, which lets us expose a feature to a chosen percentage of users. We turned the new features on for 1% of users, then 10%, 25%, 50%, and finally 100%.

It did not go off without a hitch. Shortly after the announcement email went out, so many users tried to sign up for Plus at once that the payments system couldn’t keep up. We rolled the features back, fixed the payments system, and then flipped the features back on. After adjusting our email cadence to account for traffic spikes, the features went live for everyone. Within a few weeks, millions of users had tried the new Dropbox Plus.

Appendix: Milestones and a Coding Standards Reference

Feature Rollout Milestones

For new features like the Family plan, we set a clear sequence of milestones:

  • Internal Alpha (Plumbing): The first milestone, focused on testing the plumbing. Dropboxers and their families upgraded, invited family members, and exercised the surfaces in production.
  • External Alpha (Early Feedback): Opening the feature to a broader set of real users to gather data on upgrade rates, family size, and general feedback that sharpened product decisions.
  • Beta (Public QA): Once product decisions were made, beta was about ensuring stability, removing unknown unknowns, and surfacing the bugs real users encounter.
  • General Availability (Stable): Removing the beta tag and releasing a complete product, confident in both the product decisions and software quality.

Vault Frontend Refactoring Guidelines

These rules were in place to ensure future diffs corrected preexisting code that strayed from team standards:

  • Don’t block progress: The mission is shipping a product for sensitive files. Maintainable code is a means to that end, not a goal in itself.
  • Keep refactoring to a per-file basis: Avoid rabbit-holing. Limit a diff to one refactored file and its tests.
  • Maintain the interface in the first diff: First refactor the internal workings of a component, then change its props in a followup.
  • Write new tests: Many existing tests were ineffective. Any component change should come with new tests based on the guidelines below.

Component Standards

  • Component library first: If a UI component imports from our design system, it belongs in the team-level component library.
  • Function components over class components: Hooks are standard practice, so the team worked to refactor class components into function components with hooks.
  • i18n at the bottom of the file: Create a getStrings function or a useStrings hook to return the strings a component needs. Don’t inline i18n in the component.
  • Declarative over imperative: Avoid patterns like Modal.showInstance. Track the open/close state of a modal explicitly.
  • Design System styling: Avoid overriding styles within design system components. Where possible, work with the Design Systems team to find supported configuration options.

Testing Rules

  • Use react-testing-library: It is geared towards functional testing rather than testing DOM output. Don’t use Enzyme; it is semi-deprecated and has poor hooks support.
  • Avoid duplicating test coverage: If a component’s behavior is effectively covered by its subcomponents’ tests—such as testing that a modal opens and closes when it uses the underlying design system modal—don’t rewrite those tests.
  • Never use a className for anything other than CSS: Use data-testid to reach components, never classNames or inner text.

State Management and Logging Decisions

Note: These state management decisions were later reverted. See the earlier section on shipping team coding standards.

  • Avoid Redux for API state: API integration should go through API-QL, with a setup diff forthcoming.
  • Consider removing Redux entirely: Component or UI state belongs in context or a hook.

For logging, the rule was to place log statements at the bottom of the file to reduce noise while reading business logic:


  const onInviteModalSend = () => {
    logProductAnalyticsEvent({
      eventAction: AnalyticsEventAction.SELECT,
      eventObject: AnalyticsEventObject.SEND_CONTACT,
      actionSurface: AnalyticsEventActionSurface.VAULT_SETTINGS,
    });
    closeModal();
  };

const onInviteModalSend = () => {
  loggingFuncs.logInviteModalSend();
  closeModal();
};

const loggingFuncs = {
  logInviteModalSend: () => {
    logProductAnalyticsEvent({
      eventAction: AnalyticsEventAction.SELECT,
      eventObject: AnalyticsEventObject.SEND_CONTACT,
      actionSurface: AnalyticsEventActionSurface.VAULT_SETTINGS,
    });
  }
}

import loggingFuncs from 'component-file';
spyOn(loggingFuncs, 'logButtonClicked')

Code Isolation and Styling

  • Avoid importing from elsewhere in the server repo: If you need to import something from outside modules/clean/react/vault or the vault component library, look into dependency injection. These cases will likely be handled case by case, probably with React context.
  • Avoid advanced SCSS: Keep classnames easy to grep for, and easy to map back to their components.

Do this:

.vault-link {
  // styles
}
.vault-link:hover {
}
.vault-link-bold {
}

Don’t do this—these classes are hard to trace back to the code that generated them:

.vault-link {
  // styles
  &-bold {
  }
  &:hover {
  }
}