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.
Money is critical, and we must deliver an awesome infrastructure that enables our clients to provide the best financial experience to their customers.
To meet this requirement, we invest in quality code by making it a priority and building it into our processes. We don't like getting bogged down by technical debt and anyone in the future should be able to understand the previous technical decisions taken to build a service, this greatly increases maintenance productivity and avoid repeating past mistakes.
This means we:
We strongly believe that having a quality code base is the foundation for fast iteration in a safe environment. Our commitment to quality code is not just lip service either. We have CI tests, follow a style guide, and do all of our code reviews at the design stage. Any non-trivial changes require a design engineering doc to outline and discuss how you’d approach the problem and what tradeoff you may have to make. For those who refer to the Joel Test, we are 12 out of 12 on his list. Of course, we are not 100% perfect and cut corners at times, but we always fully understand the consequences before committing to it.
Traditional wisdom would advise against a full rewrite of our code base, but GoodNotes 5 which we launched in January was actually a full rewrite. We did this because we needed to lay down a new foundation for conflict-free collaboration and cross platform development.
Building the future of mobile app discovery with deep links
Redwood City, CA; Seattle, WA; and Bangalore
8 Open Positions
Find awesome products designed by independent artists.
San Francisco, CA and Melbourne, Australia
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’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.
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 don’t care just about “if it works.” It’s easy to make something work by hacking it together, but to write code that another person stepping in a year later can read and improve upon is much harder. We enforce best practices and avoid shortcuts, and our goal is to constantly think about scalability and maintainability.
When enforcing code styling we use Prettier TypeScript to keep the codebase more organized. By using more strict rules with TypeScript, it helps us maintain more consistency. We also use CI/CD to encourage small PRs and easier reviews, and we’re constantly deploying and testing our own code. We always schedule time to pay off tech debt. There’s a small amount of tech debt that you can have before it slows down your entire team/product/process, so we are always fighting against it.
In our early days (circa 2011), we shipped code with the sole goal of meeting customers’ needs as quickly as possible. Back then, we had no choice but to ship one-week versions of features even when they had loads of technical debt. We focused on surviving to the next day and racing against declining bank balances. However, TeamUp now has the luxury of profitability. With founders and directors who code, we are deliberate about what technical debt we take on, and are always thoughtful about budgeting sufficient time to pay it off.
We still ship as quickly and as often as we can today, but we balance that with specs and research, comprehensive unit testing, internal feedback loops, and code reviews. While we’ve matured out of working out of desperation, we’ve maintained our sense of urgency. We use Github Flow and do thorough code reviews, and we’re not afraid to prototype something in code, discuss in a pull request, then throw it out to start over again.
We see our own engineering skills and processes as a WIP. We prioritize learning, refactoring, and cleaning up technical debt. We think long term about the engineer who will read our code a year from now. We’re always improving our documentation and testing skills. We want to hire engineers who prioritize those things too. We want to hire people who get excited about writing code that someone else will be delighted to read.
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!
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.
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.