Christoph Nakazawa

Principles of Developer Experience

Welcome to the inaugural post in my new space, which I hope to keep fresh with insightful content. As a front-end engineer and engineering manager, I focused on building developer tools for open source projects such as React Native, Jest, or Metro for the past ten years.

Prefer listening or watching?
You can also listen via Apple Podcasts, Pocket Casts, or others.

In a hurry? Skip straight ahead to the Principles.


I am restless in searching for a better user experience, and I lose sleep over misaligned pixels. I deeply care about how people feel about the tools they are using every day. This experience informed how I think about building software, and the team I work with lives by several principles we defined together. I am sharing this manifest with you in hopes that it’ll be useful to shape your high-level thinking of developer tooling, too.

Developer Experience or “DevX” is the experience developers have when building, maintaining, testing, deploying, and analyzing software. Think about the tooling you use: Some of them are probably hard to use, slow, and there aren’t any plans to improve them.

Usually, it is not something that organizations get the liberty to focus on: DevX gets de-prioritized, and arguments for investments can be shut down quickly because there is no direct connection between improving DevX and generating business value (💸). This disconnect makes it hard to measure the impact of focusing on DevX. It also means that developers must ruthlessly prioritize any DevX work: how can they bring value to other developers in minimal time? Let’s start by completely reframing how we look at tools:

Developer Tools are Products

Internal tooling is rarely intuitive, documented, or fast. If we want to improve our tools, we must change our perspective and look at developer tools as real products. Products should be well-designed, have carefully crafted workflows, and focus on achieving satisfactory outcomes quickly. The best products are delightful.

Let’s think of developer tooling as a means to increase the velocity at which engineers deliver solutions. We can define metrics that can substantially impact user satisfaction or developer velocity: Suppose common repetitive tasks can be completed ten times faster or more efficiently. Developers will ship projects sooner, end up switching context less often, and likely be happier. Productivity is hard to measure, and we’ll approximate metrics in a future post. For now, we’ll focus on building a framework for thinking about DevX.1

The DevX Principle Stack

I’m going to list principles in order of priority.2 The first principle is the lowest in the stack, meaning it’s the foundation we are least likely to violate. A stack is necessary because when we have to make trade-offs on principles, we’ll always choose to stick with the one lower in the stack. Imagine a stack of books: it’s easy to take off the book on the top, but removing the book on the bottom requires a lot of effort, and every time you do, you end up with a different order, or you may even get rid of some books in your stack. You’ll only want to touch the book on the bottom when absolutely necessary.

DevX Principle Stack

The great thing about principles is that once we define them, decisions become more obvious because everything turns into a matter of trade-offs with clear resolution paths. However, as we’ll see in the examples below, it’s painful when we do have to violate a principle. That’s the entire point of principles, though!

Here are the five principles we are going to explore today:

  1. Focus on the User
  2. Incremental Migration
  3. Clarity
  4. Continue to re-evaluate Assumptions, Constraints, and Trade-Offs
  5. Maximize Option Value

1. Focus on the User

The most essential principle is to put the user always first. For developer tooling, that means:

  1. Workflow performance: How fast does it execute everyday tasks?
  2. Signal: How actionable is the tool’s signal to support the developer in achieving their goal?
  3. Reliability: How reliable is the tool in completing a task?
  4. User Satisfaction: How do users feel when they use the tool?
  5. Documentation: How clear is it for a user to get started, and can they effectively unblock themselves?
  6. Accessibility: Can everyone use the tool and understand its output?
  7. Scalability: Does this tool scale for the growth of the organization and codebase?

Two angles to this often make it challenging to focus on this principle:

  • Focus on tomorrow’s user: It’s essential to focus not just on today’s users but to make systems scalable and maintainable for future users and the growth of a codebase, product, or organization. Even though we may focus on user value today, it may make more sense to prioritize the value the tool will bring to tomorrow’s users. De-prioritizing immediate work is in direct conflict with providing value to users quickly but will bring more value to everyone over a longer time.
  • Do the boring work: Some developers will go to extreme lengths to justify going after shiny and fun projects. This list of priorities can serve as a countermeasure. An explicit user focus will almost always shut down grand ideas about complete technology rewrites in favor of monotonous work. For example, instead of rewriting a dependency manager, what if you started managing your dependencies?

Here are two concrete examples of a user focus on “error signals”:

  • The primary goal of a testing framework such as Jest is to ensure correctness. The secondary goal is to provide highly actionable signals immediately when something goes wrong. That’s why hundreds of hours of (free and paid open-source) work went into designing Jest’s error output and intelligent scheduling to give actionable feedback to developers quickly.
  • Rick Hanlon completely redesigned how to display errors in React Native. This change improved user actionable feedback and speeds up how quickly users can resolve programming mistakes.

2. Incremental Migration

Most tech stacks grow out of necessity, not through careful planning. And that’s a good thing! If all your business focuses on is your tools’ folder structure, you are unlikely to be successful.3 As organizations scale, more and more infrastructure tightly connects with other systems and becomes harder to change. It’ll usually take far longer to rewrite most existing systems from scratch than it would take to migrate them incrementally.

While it may be great to consider the ideal solution to all problems if we could start from scratch, the cost of change or the sunk cost is often so high that it’s simply not worth considering a full rewrite. Instead, I generally recommend creating incremental value. Done is better than perfect, and a few rough edges are acceptable if there is a viable path forward to a system that’s close to ideal.

I learned this principle through the incremental rewrite of Jest that started in 2015. Jest brought a few good ideas, but nobody was working on improvements. At the same time, Facebook heavily depended on it for various large projects. It wasn’t feasible to build an entirely new test framework and migrate tens of thousands of existing tests. Instead, I chose to separate concerns and rewrite individual pieces in place.

Incremental Migration is the perfect principle to highlight what will happen when we violate it and why we might choose to do it. Let’s say we exhausted all other options and concluded our system wouldn’t keep scaling for the codebase and organization’s growth. We have to go for the daunting rewrite. We’ll start from scratch to do a long-term rewrite with a high chance of failure while pausing maintenance and improving the existing system. That’s painful! The point of priorities is not always to stick with them but rather to be aware of which trade-offs we are making when we do violate them.

But beware, I was involved in multiple projects that ultimately failed to achieve their goals:

  • A rewrite of a package manager changed so many constraints it wasn’t applicable any longer.
  • Our JavaScript compilation pipeline’s rewrite took too long, ended up being too complicated, and used the wrong toolchain. It eventually failed as people lost interest.

To make sure we are forming the right opinions, we need to fully understand the system, bringing us to the next principle: Clarity.

3. Clarity

I mentioned before that it is hard to justify work on developer experience. We have to focus on clarity, beginning with plans, narrative, and the definition of metrics or goals. We can apply clarity from the project management level down to the separation of concerns, APIs, and maybe even variable names.4 Focus on essential words to describe complex topics. Not only will it help with the narrative of plans or help people better understand the proposed work, it almost always ends up with less complicated infrastructure, too!

I learned this the hard way: I once had what I thought was a fantastic proposal to improve JavaScript infrastructure, but I got feedback that it didn’t add up to much and was hard to understand. I thought, “It’s all right there in the document. What’s hard to understand?” Upon reflection, I realized that I had good raw ideas and project plans, but I had no overarching narrative that clearly explained the plans’ purpose. I switched everything around and broke down my proposals into multiple phases. The first one was fully specified and focused on a single critical workflow that every developer would use for numerous hours a day. Once I turned around the project plan, I got buy-in from everyone, and I discovered a few more concrete ways to reduce the system’s complexity by looking at it from a different perspective.

This experience taught me that it’s never too early to share a draft. Nowadays, I test high-level ideas or specific code changes I’m planning with people who have the most context. That could be people who worked on this infrastructure in the past, people I trust to give honest feedback or a group of people willing to listen and bounce ideas back and forth. It frequently helps me understand downstream effects I wasn’t aware of previously.

Clarity ended up in my Principle Stack because I used to make the mistake that tools should get out of the way and be invisible. Now I am convinced this was misguided. I was masking complexity in the name of simplicity instead of surfacing the right level of complexity at the best interaction point with the user, which also directly led me to discover another principle:

4. Continue to re-evaluate Assumptions, Constraints, and Trade-Offs

Diving into an existing system often yields variants of harsh statements like “Wow, this is the worst design I’ve ever seen” or “Who would ever write such complex code for such a simple problem?” 5

Assumptions, constraints, and trade-offs tend to change over years or even in just a few months. It’s even worse to be aware of that and not do anything about it. At the same time, it’s essential to recognize that what was built in the past probably wasn’t bad, just that at the time, the constraints or assumptions were different. Gaining context on the past will help make better decisions and more extensive changes to existing systems in the future.

Be honest about trade-offs because most technical solutions will have negative trade-offs. Don’t pick solutions that will put you or your organization in a worse position in the future. Secondly, aim to make things easier instead of building complex abstractions on top of complex systems.

For example, in the past, I used to believe that all JavaScript tooling should be built using JavaScript because that’s most familiar to JavaScript developers. That way, they can change and improve tools as they like. However, it turns out there aren’t that many product developers who go and make changes to JavaScript infrastructure. We have been making the trade-off to spend a large amount of time building more complex solutions to keep tools fast. Maybe it is time to start building better toolchains using other languages with better performance guarantees. If we go for this, we can hold on to the above principles and try rebuilding an isolated part of our system to stick with the “Incremental Migration” principle.

5. Maximize Option Value

The final principle is about retaining or gaining option value, which means any change to a system should unlock more options for improvements and significant future changes. If we zoom out, these principles exist because we often got into a state with developer tooling that made it hard to introduce major yet gradual improvements. After all, there is usually little option value embedded in the design of existing systems. If we keep option value in mind when redesigning infrastructure, we can naturally adapt to new requirements in the future.

For example, monolithic toolchains cannot move fast as they often don’t have clear API boundaries. In such a case, we gain leverage over all parts of our infrastructure by modularizing it. Once we have a clear separation of concerns, we can have competing implementations of the same pieces that are well-defined in scope and can be swapped out with better versions as needed.

Another example is reducing external dependencies or choosing them more carefully. Yes, third-party dependencies undoubtedly help us move fast initially, but using them means we lose control over our stack. We gain option value and control by removing dependencies or by actively maintaining them.6 At Facebook, taking ownership of external JavaScript dependencies allowed us to reduce our third-party dependency install times by 10x. We removed, upgraded, and replaced many of them over a few months. It was boring work, but everyone now saves dozens of minutes per day and days of CI time every week.

Other Contenders for Principles

The list of principles could be endless. I generally find the sweet spot to be around five principles for any domain, each having a few sub-principles. If there are more, they tend to become hard to remember, and it is overwhelming to apply them to decisions consistently. There are many other contenders for DevX principles, though, and you may choose other priorities in your organization or projects. Some of them could be:

  • Scale: Some organizations or tools may want to define an explicit principle for enabling various levels of scalability.
  • Growth: In some places, especially early in startups that build developer tools, optimizing for user growth may be the primary principle that has a downstream influence on every decision.
  • Complexity / Simplicity: While “Clarity” is good enough for me to touch on both complexity and simplicity, you may want a more well-defined principle on the complexity you’d like to avoid or the simplicity that is core to your product.

Principles in the Wild

I shared high-level principles for how I think about the DevX space in general. Individual tools can apply these principles to identify their tool-specific principles. If you are the maintainer of a popular and well-scoped tool, you should define your principles, write them down and share them with your users. Ideally, your principles won’t change much over the years, and if they do, that might mean your project is changing substantially, too.

Here are three of my favorites:


If you squint, you’ll find that many of the principles also apply to other domains. In a follow-up post, we’ll explore how to measure DevX.

Footnotes

  1. See this excellent post about developer effectiveness in the meantime.

  2. Check out my all-time favorite podcast by Exponent on Principle Stacks.

  3. Unless, of course, your business is “Folder Structure as a Service”.

  4. Don’t be the senior developer who uses all the big words to paper over their imposter syndrome.

  5. Often followed by the sudden realization they were the original author of that piece of code, and then quickly taking back all the mean words.

  6. Check out this timeless article by Orta Therox on owning third-party dependencies.

Tweet about this article, or share it with your friends. Discuss with the community, or email me. Thank you for reading, and have a great day!

Check out Athena Crisis

Athena Crisis

Ready for Another Post?

Subscribe for More