Making the right decisions in terms of ensuring our code is modular, well-tested, and reusable enables the code to adapt to future needs. We emphasize quality code because it makes everyone’s life easier: Engineers have an easier time reusing code, and customers using a bug-free product.
We periodically review our code organization and data structures, and devote time to DevOps and refactoring projects. We don’t like getting bogged down by technical debt. Is this class a service or a repository? Should this method be a in a trait or in a parent class? You’ll have team members who care about answering those questions.
We invest in quality code by making it a priority and building it into our process, even setting aside a dedicated 25% of time for engineers to address technical concerns. At every planning meeting, we prioritize engineering work in addition to our product work. Our process emphasizes code quality through pair programming, and continuous refactoring as we add features. Throughout our interview process, we look for candidates that care about writing clear, maintainable code, and ask questions about their approach to testing and refactoring. We take pride in keeping things transparent around here with regularly scheduled Showcases with key members talking about features, releases, and QBRs (Quarterly Business Reviews).
We call this workflow "pragmatic craftsmanship". Pragmatic craftsmanship means knowing when to invest vs when to ship. As a result, the codebase has engineering quality proportional to its impact. We also believe that product quality is a key differentiator for Asana. Thus we are willing to invest more to build the best experience.
Beyond our commitment to pragmatic craftsmanship, we heavily invest in new hire onboarding. This onboarding process allows us to teach developers the best patterns and practices. The process also enables developers to be valuable members of the team faster.
14 Open Positions
We define syntax and pattern standards as a group, use code linters accordingly and respect our rules throughout the codebase. We pay more attention to quality than speed because we are constantly thinking about what the next engineer would think. Maintainability and developer happiness is always more valuable than short-term efficiency and self-rewarding productivity. We constantly improve our codebase quality by cleaning up old code and coming up with better ways. Every day, we apply our favorite mantra and company value: Leave it better.
We’re currently at 96% unit test coverage of our entire codebase. Testing is fundamental to our culture, and engineers are expected to not only write tests for all their code, but also manually test each feature they develop. We run lean when it comes to manual QA testers, as this isn’t scalable.
Choosing how to tune the knob between expediency and elegance is one of the toughest (and most fun!) types of day-to-day decision that engineers face at a fast-paced early startup. However, we believe that this tradeoff is correctly achieved by compromising on the requirements of a problem or the generality of a solution, not in the quality of a system.
We have a high bar for robustness and have no problem prioritizing and working on highly technical tasks, or tasks that non-engineers would have difficulty understanding. We are constantly challenging each other to write better code, improve our processes, and generally improve ourselves and the codebase. We are intentional about what we build and spend lots of time up front designing solutions and choosing abstractions before we dive into a project. As a result, we have a clean codebase, have very healthy coding practices, and spend very little time fighting technical fires.
We refactor frequently whenever we encounter technical debt, and like to be on the bleeding edge of technologies and coding practices. It makes our engineers happier and allows us to ship faster!
Our stack consists of Koa.js, MongoDB, and Angular – because they were the hottest things a few years ago. These days, we’re playing with React, React Native, Flow.js, and Postgres – because Postgres is cool again :)
That said, we don’t just follow trends; we adopt new technologies only if it can significantly increase the quality of our code base. So while we adopt a micro-service architecture, we don’t overdo it and use our best judgement and apply the 80/20 rule to balance perfect with scrappy. Fun fact: our core algorithm is written in Common Lisp!
All code gets peer-reviewed before it goes into production. We strive for 99% test coverage on all the code we ship. This makes refactoring easier too!
This means we know what’s coming in our future, and we’re continuously planning for that future while also knowing how critical it is to deliver value to our customers today.
Our experience shows in the way we make choices. Every engineering team is faced with important decisions every day, what matter is how you make them. We don’t trade off “product” against “technology.” Instead, we see it as our task to deliver innovative products to our customers now, while preserving our ability to continue innovating for them in the future.
We absolutely ship “mvp” products that need follow-up, and sometimes we build a feature on top of existing code that wasn’t really built that well for it. However, we balance these decisions by making bigger investments elsewhere. We create deploy tooling packages and reusable component libraries (which we then open source), and refactor core structures and components before making significant new product investments in those areas. We invest time into building solid test harnesses and coverage, and also replatform core capabilities to stay abreast of industry movements (e.g. we’re moving a lot of stuff from parse to graphQL this year). We have a love for the craft and what we do as a company.
We use planning and estimation to help clients understand what feature set fits within their budget, not what level of quality they can afford. We closely collaborate on user stories and make sure we have as much information about current and upcoming work. This helps us make the right design decisions that balance immediate needs with long-term maintenance.
When we program, we prioritize quality in a number of ways. We make extensive use of pair programming, code review, and test-driven development to create an environment where design is collaborative and the codebase is collectively owned. Since we have many long-term projects with clients, we spend a lot of time working on code we wrote months or even years ago. This helps us have a long-term outlook when writing features or fixing bugs instead of rushing to ship as much as possible every week.
Our technical screen is a short coding exercise and we emphasize to our candidates that completion is not a criterion for success, and we instead look for insight into a developers process: Do they ask questions if something’s ambiguous? Do they use tests? Do they document assumptions? What is their design process?
Not only is great code a joy to work with, it’s the only way to move quickly over the long-run. We take pride in our work, do thorough code reviews, and leave time every week to work on technical debt. Within the engineering team we review everything that goes into production, both to improve the quality of our code and to share knowledge between team members. We automate the checking of style so that code reviews can focus more on architecture and maintainability.
Our coding exercise during the interview process is primarily about assessing an engineer’s craft, or level of care that they put into their code. We prefer simple, clean, well-structured code over flash or speed. That said, we still value moving quickly, so we put time parameters on the exercise to give us a relative sense of what can be produced in a given amount of time. We hope to learn as much about you and your code quality during your interview as you do about us and our code quality.
As our customers often require similar functionality, we help them save money by reusing code from our existing codebase, that we call the Atlas Framework. The code that is part of our shared codebase is well written, tested, and maintained because we have many projects using it (any bugs are found and addressed quite quickly). When a customer comes to us asking for some new functionality that we don't already have in our shared codebase, we make a decision on whether to try and separate this new functionality out into something that can be reused, or to bake it into their application.
To help ensure that our code remains high quality, we always ensure that at least two developers work on each project and don't have anyone working in isolation. If a developer ever sees any issues with anything that has been written, we often see who wrote that code and go back to them with some feedback to help them write better code in the future. When we work on a software project we also factor in extensive software testing by our dedicated team of International Software Testing Quality Board (ISTQB) qualified software testers and we use our comprehensive software testing plan documents.
When Honor first started, we had a team of 10 before we were even funded. Our engineering team was made up of the best people we knew from our previous companies, which also meant we brought on a lot of the best practices we learned from our previous experiences. We have to ship high quality code because people’s lives are on the line. It’s certainly a balance in being nimble as well, but we understand that everything we do has profound effects on operations (and the bigger we get, the more profound that gets). That is why we implement good practices, code reviews, team discussions, and have deep conversations about architecture and tradeoffs. There’s a lot of communication around good code.
We also highly prioritize ownership. Ownership is everything. If something breaks, it’s a team effort to fix it so it’s important that you do your part in raising the flag. In a way, this also keeps things at a high quality because no one is thinking that they’ll be punished for shipping a bug.
We don’t have QA and instead rely on comprehensive testing including unit and integration tests. Each team supports their own applications in production, so there is high incentive to ship a stable, quality product. We use software linters to enforce a share style and, for the most part, our software development manifesto has held true over the years. Our core platform is a collection of highly available microservices built with Node.js, ReactJS and ES2017 backed by MongoDB, RabbitMQ and Redis. In the future we plan to expand into new domains including iOS application development and software that runs on embedded devices in our hub (IoT).
Article I of our Engineering Constitution is titled: “Never Compromise Quality.” We have a strong bias towards clarity over complexity, simplicity over cleverness when writing code and collaborative, constructive code reviews; from continuous integration to blue/green deploys; and a preference for care over speed when it comes to production. Our platform powers our partners’ day to day operations, and as a result, we strive to build our engineering culture on a bedrock of quality.
Engineers have wide latitude to make sweeping changes across the entire codebase to eliminate lava antipatterns, and we always attempt to fix entire classes of bugs at a time. When mistakes do occur, we practice a no-blame culture and follow the five whys during postmortem.
Want to List Your Company?
Submit a team profile!
Select 8 Values
Contact me (Lynne 👋)
Qualify Your Values
Reach Thousands of Devs
Find Value-Aligned Candidates
Have Meaningful Initial Conversations
Don't Fill Roles, Hire Teammates
You can post as many job openings as you want.