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.
1 Open Positions
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.
We intend to build a company that will last decades. Our product, funding, and market position make a long-term orientation for our engineering team not only possible, but necessary. For one thing, our product has enough fundamental complexity that adding incidental complexity by taking on too much technical debt would cripple our velocity much sooner than a company with a more straightforward, boilerplate product.
Additionally, we have a product that stands in a well-differentiated category of its own. This gives us breathing room to build durable competitive advantage by innovating, rather than constantly racing to eke out a razor-thin marginal advantage over similar competitor products. Our focus on code quality grows from deep roots in the nature of our business; it’s not just a pet preoccupation of some engineers on our team.
To this end, we embrace the usual repertoire of quality software development practices: code review, linting and static type checking, unit and integration testing, continuous integration, manual QA, deployment automation, issue tracking, and a thorough style guide that focuses on practices that improve code clarity without too much bikeshedding. These practices aren't engraved on stone tablets; we're always discussing how to do better, and ideas can be raised by anyone from our newest hire to our cofounder CTO.
At a higher level, we have a culture of circulating ideas in design documents well in advance of implementing them. When done right, a design review process does not slow down development. Good teams are perpetually bursting with ideas. If people are encouraged to share those ideas early, then features or changes can be proposed, discussed, and refined long before the team has bandwidth to implement them. Reflecting on design or implementation approaches as a team leads to greater simplicity and clarity. Building the simpler, clearer thing nearly always leads to greater development velocity, even in the relatively short term.
That said, we recognize that there are times when fast turnaround is imperative. Sometimes a defect that affects users must be fixed urgently, and the fix is clear, and in such cases we prioritize appropriately. And sometimes the best way to deliver quality in the long term is to put early iterations of a feature in contact with users so we can learn from their feedback. In these cases, we strategically deploy well-contained technical debt. (One tactic for containing debt in these situations is to ensure that decisions made in this mode are highly reversible. For example, exposing a limited beta of an Airtable block to a small audience constrains future development much less than a public UI or API in the core product.)
18 Open Positions
The GitHub for training data and machine learning teams
San Francisco, CA or Remote (Global)
One of our core values is craftsmanship. The way you see the world shapes how you build your product, and your users will then see the world through your eyes when they use your product. This is incredibly powerful. Our perspectives on the world and even our emotions can traverse lines of code and be communicated through the things we make for others. We are engineers and product managers, but above all, we are craftsmen and women.
Our engineering team focuses on shipping reliable features with the aim to continue building and growing our customers’ trust in Labelbox. We consider ourselves to be partners with our customers. At our current stage, we still do one-off feature builds for individual customers in order to help them leverage our product as much as possible. We will likely grow out of this as we scale, but for now, this has proven to be the best for both our customers’ success and our own understanding of how people use Labelbox.
We pride ourselves on writing high-quality code that is both easy to read and maintain. We operate using agile processes and two-week sprints. Each engineer owns a product for six weeks and we have a backlog of stories that people can pull from. Part of what contributes to our overall code quality is iteration. We do code reviews, deploy 4-5 times per week, and try to get things out quickly in order to get feedback that informs us on what to improve. We use LaunchDarkly for feature flagging which helps us roll our new features quickly and safely. Perfection never blocks us, but we do aim for it.
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.
20 Open Positions
It can be hard to find the perfect balance of all of these factors, but one thing we particularly care about at Lightstep is good instrumentation. A high-quality code base that's easy to understand and diagnose is what makes a great work environment.
To that end, we take code reviews extremely seriously. It's an opportunity for shared learning, so we spend a non-trivial amount of time understanding each other's code. We use data and metrics to track software quality, and discuss these metrics at our weekly meeting. We also have a streamlined process for prioritizing and triaging all reported bugs; they are automatically assigned an owner so none fall through the cracks.
In our previous work as engineers at big companies like Google and Amazon, we were responsible for maintaining code quality and writing our own automated tests. This practice is something we brought to Lightstep – we do not have a separate QA team, so we are accountable for the quality of the code we write, and are on-call for issues that arise in production. This motivates us to build robust monitoring, comprehensive alerts, and automated CI/CD pipelines.
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 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!
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.
They cover code consistency, using data to make decisions, technical leadership, learning from our mistakes, focusing on automation and developer productivity, writing safe, well tested and monitored code, and shipping small changes quickly and continuously.
Additionally, we have an org-wide proportional investment policy that provides the opportunity to prioritize work that helps improve the codebase and developer productivity. While we value urgency and moving quickly, we also favor doing things the right way as opposed to the fast way.
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?
Increasing code quality is a high priority for us. We are given the capacity to do things right, even if it takes longer. Everything is a trade-off: if we can spend a week fixing technical debt now to save us time in the future, then we’ll do it. We’re striving to increase test coverage across our whole codebase.
By using feature flags inside of the product, we can ensure that new customers use the latest and greatest features, while leaving older customers on stable grounds without changing things. Our Customer Success team actively works to migrate customers to the latest version of features so they can be deprecated and removed from the code. Examples: client website migrations, new API explorer, and new markdown parser.
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.
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.