I talk to a fair number of software engineers about working at Asana. They’ve usually heard a lot about how it’s a great place to work, and they want to know more about the tech stack and the challenges we work on.
In the spirit of highlighting the technical challenges that have kept me excited since I started working here three years ago, I wanted to share an overview of our current architecture along with some of the problems we’re working on today.
At Asana, we want to make teams of any size (1 to 100,000+) more effective by organizing their work in a single connected tool. This drives our product roadmap, along with our architecture and its challenges.
At most companies, each team has their own work-tracking tool (think JIRA for Engineering, Zendesk for Customer Support, Salesforce for Sales). If people didn’t have access to another team’s tool, they’d have no idea what was being worked on, who was working on it, or why it mattered. Anyone working with multiple teams would have their work scattered across multiple tools with no guarantee each one was up-to-date.
In contrast, Asana believes that every team in a company should be using the same tool to track (or sync) their goals, projects, and tasks. This shared source of truth gives everyone clarity about who’s working on what, by when, and why—without the headaches of switching between tools and information silos.
This motivates some architectural requirements:
Feature-rich and flexible enough to support the plans and workflows of teams as different as HR, Engineering, and Sales
Performant, reliable, and synced across multiple devices so users stay focused on their meaningful work (not “work about work”)
Easy for users to learn and set up their workflows
Integrated with a wide range of team-specific and company-specific tools
Secure & scalable enough for organizations of 100,000 and beyond
Add all these factors together, and you have a software suite with challenges across the stack!
One of Asana’s product differentiators is the flexibility and number of connections that users can create in the data model. Almost every object can be “multi-homed” into other objects, creating a dense graph of objects and relationships.
This presents constant challenges around feature scaling, data integration, and product modeling.
As an example, users might have read access on a particular task for many reasons. They could be the task’s assignee, they might be in the task’s collaborator list, or the task might be public, or they might be a member of one of the task’s projects, or they might be a member of a team associated with one of those projects. It’s important to have the right data permissions and controls in place, but standard approaches to SQL queries wouldn’t be able to run this logic performantly.
To address these challenges, we’ve built a MySQL-backed persistence layer called OKVStore. It’s inspired by graph databases and object-oriented languages and enables engineers to declaratively define their properties & access control rules in an object-like configuration file without worrying about the object-relational impedance mismatch or LunaDb’s reactivity pipeline (more on that below).
Of course, we still have many challenges in this space that keep us busy:
Mapping the data model to team-specific productivity tools
Modeling team relationships in matrixed organizations
Data pipelines for replicating to Elasticsearch and ML datastores
Integrating data governance into our frameworks
Scaling to multiple clusters distributed across the globe
To keep the quality of the product and pace of development high, we’ve been working on several challenges:
Flexible spreadsheet, calendar, and timeline views for planning big projects
Richtext document editing for kicking off projects & keeping notes
Extracting a library of accessible React components across dozens of teams
Integrating Bazel for speedier incremental builds & test runs
Automating releases and rollbacks based on error reporting
Bundle-splitting to speed up page loads
Users expect that when they add a comment on a task in Asana, every other browser will immediately show that comment without needing to refresh the page. This could be straightforward to build for a single comment feed, but becomes a lot harder when users expect the same reactivity from every feature in the app (even for edits that fan out to thousands of connected clients).
Asana built a custom service in Scala called LunaDb to power this app-wide reactive data-loading. Clients issue queries for graphs of related data and business logic calculations (similar to GraphQL), automatically subscribing to any updates to those query results. LunaDb then watches for changes in the underlying databases and pushes query updates down to all subscribed clients.
Writes are also routed through LunaDb, executing in our Node-based LunaServer backend application framework.
Asana first introduced this technology in 2015 (around the time GraphQL became public) and it’s going through a renaissance to meet our newest challenges:
Refactoring LunaDb to support more backend languages for product developers
Isolating stateful and stateless components for scalability and modularity
Instrumenting requests with distributed tracing to give developers insight into costs
Migrating to GraphQL’s query format
Bringing reactive data-loading to mobile clients
Asana’s been around for 14 years and it feels like we’re just getting started. We have an ambitious vision full of new problems we’re excited to solve and a strong foundation of a well-loved product.
There’s a lot of opportunity to bring new technical knowledge to Asana, solve these challenges, and learn from the smart & kind engineers already on the team. (In addition to technical excellence, we also pride ourselves on our culture of design docs, close-knit teams, tech talks, and work-life balance)
If any of this is exciting to you, we’re hiring for engineering roles across the stack. Check out our open roles.