What Makes a Great Developer Experience?

April 15, 2025

I’ve been working for over 20 years in the field of “developer experience,” where we help developers be more effective, efficient, and happy, by improving tools, systems, and processes. I have been intimately involved in designing key aspects of the developer experience at Google and LinkedIn, have been very involved with the research community in this space, and I’m constantly in touch with developer experience leaders at every major tech company.

I’d like to spell out for you the fundamental principles of what makes a great developer experience—the most important things to understand in the space. I’m only going to give an overview of each item, and it’s possible that I will miss some points (because there is a lot to cover) but hopefully this is a good overview of the key points.

Basic Concepts

There are three primary things you’re trying to optimize for with a developer experience:

  1. Cycle Time (aka Iteration Time): The time between when a developer has any intention and the time when that intention is accomplished.
  2. Focus (aka “Flow”): The ability for a developer to stay focused on the task they are working on, and not be interrupted.
  3. Cognitive Load (aka Required Knowledge and Decisions): How much a developer must know in order to do the task they are doing, and how many decisions they have to make to accomplish their task.

Let’s talk about these points in more detail.

Cycle Time (aka Iteration Time)

The process of writing software involves large and small cycles, which are basically the time between when a developer intends something and when the result materializes in the physical universe. The smallest cycles are things like “I intend to write this line of code,” or “I intend to run this command line tool and see the output.” The largest cycles are things like “I intend to solve a problem by creating a product and having people use it,” or “we intend to redesign our system in a way that will take 100 people a year.”

In general, the way that you speed up the large cycles is by speeding up the small cycles. If you only focus on speeding up the large cycles, very often the quality of the result will suffer. For example, let’s say that I see it’s taking a team two weeks to ship any change to production. If I just go in there and say, “ship faster no matter what it takes,” and I don’t say anything else, then the team will likely cut corners in a way that harms the quality of the end result and the maintainability of the software.

However, if I investigate more deeply, I will find that there are smaller cycles that are taking too long. Perhaps code reviewers are taking forever to respond. Maybe the tests take too long to run, and so developers have to wait too long every time they make small changes. Maybe the deployment tool is too hard to use and so developers struggle with it and avoid it.

On the other hand, it’s always safe to speed up the smaller iterations (as long as you discover there really is some problem with them). For example, let’s say code reviews are taking too long. Often, what you discover in this situation is that the reviewers just aren’t responding fast enough. The solution here is to focus on that reviewer response time more than on the overall code review time. For example, you often discover in this situation that code authors are sending out PRs that are way too large, so it takes the reviewer forever to review them.

Code review is fundamentally a quality process, so you want the end result to be high-quality code. Sometimes it takes five rounds back and forth between the developer and the reviewer, with the developer submitting changes and the reviewer requesting modifications, until the end result is good. You want all five of those rounds to happen. But you want each of them to happen quickly. If each round happens quickly, you find that PR review times are only as long as they should be, and both the developers and reviewers are happy with the process.

This applies in all parts of software development. If you find that it’s taking a long time for people to code things, it’s worth looking into things like: how long do their builds take? How long does it take to run their tests locally while they are developing? How quickly can they get the information they need to make a decision?

It’s also worth noting that every cycle is some form of an Observe, Decide, Act loop, which I’ve written about extensively elsewhere. If you work on improving developer experience, one of the best ways to improve is to make it easier for developers to observe things. Put the information they need right in front of them right at the moment they need it. (Never put information they don’t need in front of them, because that is bad for cognitive load, as we will discuss later.) For example, when a test fails, the failure should clearly indicate what’s wrong so that a developer doesn’t have to go digging to figure out how to fix it. When they are creating a new project, make it clear what their options are (like what language, framework, how to name the project, etc.) and where to start. Just think: “How could I make it so that a developer never has to think and only has to look at something to know what to do next?”

Focus (aka “Flow”)

Software development is an activity that requires many hours of deep focus in order to accomplish successfully. Developers are building up a very complex mental structure of the thing they are working on and using that mental structure to make decisions and write code. They are constantly thinking (or writing in their notes) “Oh, don’t forget to do that other thing after we’re done with this thing.” This state of focus is often referred to as being “in flow.”

When developers get interrupted too thoroughly or too often, this complex mental structure vanishes and has to be re-built when they go back to the task. They risk forgetting that “do this next” thing that was in their mind, and actually shipping software that’s broken because of it.

We call this form of interruption “context switching,” where the developer’s focus is now primarily on something other than writing code. When you force a context switch on a developer, it can take them ten or fifteen minutes to fully rebuild the mental structure they had when they were focused on coding. So small interruptions can be very expensive.

Exactly how long an interruption can be before it causes a context switch varies between people. I’ve seen numbers between 30 seconds and 2 minutes. It also depends on what the interruption is. If the interruption is a phone notification that requires no thought, you look at it for 30 seconds, and dismiss it, that might not break focus. If the interruption is some very complex failure message that you get as an alert, it’s probably going to break focus no matter how long it takes to read it, because it requires so much mental work that it breaks the developer’s mental structure they had about their current task.

In my experience, interruptions can also be more disruptive if they cause a bad emotional reaction. Being upset about something is so distracting that it makes it hard to keep focusing on the work you’re doing. You feel compelled to do something about the upset, or at least think about it. If somebody sends me an upsetting message, or one of my tools behaves in a wildly frustrating way, that can break my focus even if the interruption is 5 seconds long.

If you work on developer experience and you want to improve flow, think about things like:

  • Do my tools force developers to engage in some complex task that isn’t coding, testing, or debugging, while they are doing those tasks?
  • Are we ensuring that developers have multiple uninterrupted hours to focus on coding?
  • Do my tools, systems, or processes do something that’s so frustrating to developers that it breaks their focus?

Another key point here is: do developers clearly know the purpose of the work they are doing and have a clear direction to go? That is, have I been given a clear task where I know why I’m doing the task, who it’s for, and what the intended result is? Otherwise, my own confusion will frequently break my focus. I’ll have to keep asking my co-workers what I’m supposed to be doing. I’ll sit there and wonder about my task instead of actually doing it. Plus, I probably won’t build the best possible thing, since I don’t have all the data I need to make good decisions about it.

Focus is another area where you want to put clear information right in front of people that they can use to Observe, Decide, and Act. Don’t make them interrupt their work to go hunting for the data they need right now. Sometimes, “I’m going to go learn about something” is a developer’s whole task, and in that case, making them search for information isn’t an interruption, as long as they can easily find what they are looking for. But if they are coding and they just need to know something like “what does this function do,” that information should be available directly in their editor as fast as possible.

Cognitive Load (aka Required Knowledge and Decisions)

“Cognitive Load” is a term that I don’t love, because it’s not clear what it means. For the purpose of developer experience, what we mean when we say that is:

  1. How much does a developer have to know in order to perform a task?
  2. How many decisions is the developer forced to make when performing a task?

I’ll talk about these points separately.

Reducing Required Knowledge

There are many people that would argue that developers should know as much as possible about what they are doing, and I agree with them. However, the productivity and experience of developers is dramatically improved by removing things they must know to do a task. Developers were far less productive when they had to write 1’s and 0’s to code. Writing in assembly language is far less productive than writing in a higher-level language like Java or Python. Does it make you a better programmer to understand how those languages translate down into assembly? Yes, it does. Should you have to know that in order to do every programming task? No.

This is an area where most teams that own developer infrastructure at a company fall down very hard. They make tools that require the developer to gain a deep understanding of that tool in order to do their job. This would be fine if there weren’t a hundred of these tools that the developer has to interact with in order to do their job. Sure, sometimes there are going to be more complex tasks that require a developer to go deep and really learn about one of the tools. But ideally, each tool should be intuitive enough that the developer doesn’t have to learn much about it at all in order to use it successfully. Even more ideally, the infrastructure should be redesigned so they don’t have to use that tool at all unless they really need to.

Reducing Choices

This is one of the most controversial aspects of developer experience, but after long experience at multiple companies, I can confidently say:

Developers should only have to make the choices they need to make.

Some developers believe they must be allowed to make every choice about their system—what programming language it’s written in, what package manager to use for installing dependencies, what build tool to use, how to indent the code, what monitoring system to use, what deployment system to use, etc. I understand that—I have strong opinions about what I like the best, too. But most of the time, decisions like that actually just force a team to do work that it should not have to do—work that distracts them from the core task they are trying to accomplish.

Taking it to extremes, imagine that every time you were going to start working on a change to your system, you had to do full research on what programming language to choose, which library to use out of 10 options that all do the same thing, what build tool you would use, argue with everybody about tabs vs. spaces, decide what code review tool to use, and generally make every other possible decision involved in software engineering. You’d never get anything done, right? What makes developers actually the most effective, efficient, and happy is just getting to focus on the task they need to do, not having to constantly make decisions about a zillion things outside of that.

Within a company I would even go so far as to say that developers should not be allowed to make decisions they don’t need to make. That sounds extreme, but actually it leads to a great developer experience, when done right.

The problem is that developers will often believe they need to make decisions about things that they actually don’t, but they won’t see the consequences of that for months or years (or they can’t understand how the decision will affect the larger business). For example, developers should not be allowed to choose any programming language in the world to do their task—it forces them to develop a bunch of new libraries or infrastructure around that language instead of focusing on the task they need to actually accomplish. It has a ton of other bad long-term consequences for a business, too, that are hard to see when you’re just an individual developer who really likes a particular language. (Which, once again, I understand! I am a programming language nerd and have lots of strong personal preferences.)

However, one must be very careful with this principle—developers need to be allowed to make the decisions they need to make. I have watched a lot of people code, and how each person uses their editor and the tools around it is wildly different, because the way people think is wildly different. You can’t put too many hard constraints on certain parts of people’s workflows, because it would dramatically harm productivity without any real benefit to the business as a whole. I’ve never seen anything good come of a company saying, “everybody must use this editor.” I have seen good things come out of, “we are going to provide more support for one editor than another, but we will still expose all functionality as command-line tools so you can use whatever you want.”

Another thing you have to be careful of, is that if your infrastructure restricts the decisions developers can make, you need to do a really good job of maintaining that infrastructure centrally. That is, a central team needs to be making those decisions and responsible for the results on an ongoing basis. If you say, “everybody must use this build tool for their Java projects,” then you’d better make sure that tool actually works for everybody, now and continuously into the future. And sometimes you need to provide a set of limited options. For example, if you just say “everybody can only write everything in Java,” you’re going to find it’s not an appropriate language for everything.

The key to doing this well is to deeply understand the requirements of your developers and the requirements of the systems they work on. You need to deeply understand the types of problems they work on solving, so that you know what decisions do or don’t need to be made. And you need to be able to adjust this appropriately over time (change policies) without opening everything up and allowing chaos to reign.

The truth is, most developers do not want to make every decision about their system and then be forced to do a bunch of custom low-level work before they can even get to their task. They love programming because they get to tell the computer to do something and have it do it, and because they enjoy solving the problems that help their users. Let them focus on what they need to do to accomplish that, not every single other possible decision or task in the whole universe of software engineering.

I wrote more about this in Reasoning and Choice, including a discussion of how this principle makes it much easier to reason about systems. That’s one of the most important aspects of any software system: the ability to reason about its behavior without running it, and the principle of Reducing Choices helps make that a reality.

Note that I talked about this above mostly in terms of infrastructure-level choices, because that’s what often matters the most if you’re a central team working on developer experience, but the principle applies broadly. Within a team, you can say “these are our code patterns, please don’t use other ones,” (as long as that doesn’t restrict necessary choices). When developing a tool, you can set sensible defaults so people don’t have to make a decision about every possible option the tool allows. There are many, many areas where this principle can be applied.

The Challenges of Developer Experience

Most of the difficult aspects of developer experience are human aspects, not technical aspects. Some of these have a technical component, but much of the difficulty is more how human beings make decisions, adapt to change, etc. The primary difficulties are:

  1. Understanding the Problem: What are the most important things to work on to improve developer experience right now? How do we get enough understanding of those problems so that we build the right solution?
  2. Managing Change: How do you roll out new changes? How do you get people to adopt new systems or behaviors? How do you deal with people who are upset about changes?
  3. Providing Leverage: The tools and systems you build for developers need to not take so much work to use/adopt them that they actually create more work than they save.
  4. Saying No: You can’t solve every problem in the world all at once. You have to be able to prioritize. Plus, sometimes you get demands from a team that really wants some tool or feature that would actually harm developer experience for the company.
  5. Putting the Pain on the People Who Cause It: If one team does something that causes pain to another team, and the team creating the pain never feels any of it, the pain will grow unbounded.

Let’s cover these in more detail.

Understanding the Problem

The most important thing to know here is that problems come from users, and solutions come from the developers of systems. You must never, never reverse this relationship, where your users tell you what solution to build and you build it, and the developers sit around imagining problems that nobody actually told you they have. If you work on developer experience, developers are the “users,” and you are the “developer.”

This can be very tricky because sometimes your users involve senior executives or technical leads who have strong opinions about exactly what they want you to build. You still need to hold your ground, though, and just get from them what the problems are while you design the solution based on good data collection from all your different users. The weird thing is (I’ve seen this happen so many times) if you build exactly the solution your users told you to build, instead of doing good research and then designing the best solution, your users will not like it. They might like it at first, but over time they will come to hate it, or the executive who “designed” it for you leaves and the new one doesn’t like it.

It’s also very tempting to think, “Hey, I’m a developer, so I know what developers want,” and entirely skip talking to your users. That doesn’t work, because developers all work very differently, and often have very different requirements from you. For example, if you’ve never spoken to a Machine Learning engineer, you will be shocked to discover how different that experience is from being, say, a frontend web developer.

If you want to understand how to gather data and feedback from developers, I worked extensively on the LinkedIn Developer Productivity and Happiness Framework, which provides some guidelines for how to do that. However, if you work at a small company, you can also just talk to people. It’s pretty easy.

Feedback and Complaints

After you’ve built something, it’s really important to seek out data and feedback about the system. Even though we love the things we have built, we should always be searching out problems that developers have so that we can solve them—including problems with the thing we just made! This ends up creating the best developer experience for the company over time.

One thing to understand here is that in the field of Developer Experience, people will rarely send you positive feedback. When developer tools “just work,” developers mostly don’t think about them. They are focused on the tasks they are doing, not on your tools, and that’s how it should be. However, if some tool or system made it hard for them to do their job, even a little, they’re very aware of that.

Also, not every developer is a great feedback provider. Often, they just had a very frustrating experience with a tool, and they are going to provide you some very emotional feedback that isn’t going to be very considerate of your feelings. Certainly, people shouldn’t do that, and if you’re going to write feedback, I encourage you to realize that the person you’re sending it to is a smart person with good intentions, so please respect them. But sometimes it happens, and you shouldn’t take it as a personal attack on you.

When you get very harsh feedback, the key to handling is is: let the user know they have been heard. If there are genuine deficiencies in your tool, agree with the user about those! You don’t have to insult yourself or your own work, just be honest about the state of the stuff you own. Users actually respect you more when you’re willing to say, “Yep, I agree that’s not a good experience.” That doesn’t mean you have to fix it right away—your team sets its own priorities. Just let people know they have been heard, that their complaints are valid, and a lot of the noise will die down.

Sometimes people just keep being mean after you’ve heard them and acknowledged the pain, and in that case you should actually just tell them it’s not okay to behave that way, and talk to their manager if they keep doing it. Only a small minority of people behave that way, though. Never ever do this as your first response to feedback.

Managing Change

Growing up in America, I was taught in history class about the great revolutions that changed history: the American Revolution of 1776, the French Revolution, the Soviet Revolution, and so forth. The stories made it sound like leaders caused a violent revolution and then the world changed almost overnight. However, when you look into what actually happened in many of those situations, the “violent overnight revolution” actually just re-established a government much like the previous one (or worse) and true, positive change happened through a slower evolution over time.

The analogy I like to use is turning a ship at sea. There’s only a certain rate at which you can turn a very large ship before it actually breaks in half. The larger the ship, the slower this rate of turning is. A company or team is much like this. The larger it is, the slower it will safely “turn” from one direction to another. Now, to be clear, I’m not saying that changes have to take forever. But they also don’t happen overnight with the wave of a magic wand.

There’s a lot to know about how to roll out changes safely and successfully.

Change Aversion

One of the major factors you have to deal with, when improving developer experience, is what we call “change aversion.” Any change you make to a system will receive negative feedback from somebody when you roll it out. This happens not because there’s something actually wrong with the change, but because the user was used to the way it used to work, and they don’t like that it changed. They might be critical of the new user interface, or have some emotional argument about why the new thing “sucks,” but often they are really just upset that any change happened at all. This isn’t something unusual; it’s a factor that exists in almost all human beings. People tend to have a certain appetite for how much change they are willing to experience in a period of time, and if you go beyond that, they get upset.

It’s important to recognize when people’s feedback is simply change aversion vs. real feedback about something valuable. There are a few ways to tell:

  1. Change aversion usually lasts 3 to 10 days. If you get feedback from a user within the first 3 to 10 days after you roll out a change, and that same feedback is not being provided by a huge number of your users, it’s worth considering whether the user’s response is just change aversion.
  2. Change aversion feedback is often emotional. It might be insulting. It might be expressed as just an opinion (“The color of the new menu is ugly”) vs a fact (“I ran the new command line tool and it’s 10 times slower than the old command line tool.”).

What you are trying to avoid, when managing change, is creating so much change aversion that you cause a revolt. A revolt basically looks like so many people getting angry that they go to your management and they stop your work. When in doubt, roll out smaller changes to fewer people with a slower rate of expansion. Over time, you will learn how much change you can roll out, how quickly. Never, ever do a “big bang” release where all of your users experience massive change all at once.

Now, all of this said, never attribute all feedback to “change aversion.” Often people do have feedback that is legitimate. If many people provide you the same factual feedback, and you look at it and their feedback makes sense and you think fixing it would improve the product, that’s very likely not change aversion. Plus, saying “this is just change aversion” doesn’t mean you should totally ignore the feedback. You should at least acknowledge it, let people know they were heard. It does mean you should not argue with the person or try to reason with them about it, because they are having an emotional reaction where you trying to “logic” them out of it won’t help anybody. If you think it’s change aversion, you acknowledge them, and they just keep fighting with you, sometimes you should just ignore them and get on with doing your job. If they come back 3 days later with a more reasoned argument, then you know it’s not just change aversion, and also they’re probably being more helpful by then, anyway.

Incremental Rollouts

In addition to incremental development and design, one has to know how to incrementally roll out changes so that not all of your users experience all change all at once. This basically has three components:

  1. Managing releases so that they are as minimally disruptive as possible. This means small changes that try their hardest to not require your users to do work to adopt the changes.
  2. Picking a good initial cohort of users to roll out to, that is big enough to get useful feedback from, but not too large.
  3. Understanding when to expand the cohort and how quickly to expand it.

Minimal disruption means things like: don’t break your users’ systems. Don’t require your users to do manual work to adopt the change. Provide clear documentation when necessary. Make sure the system provides very clear error messages so developers know what to do if it fails when they first try it. Basically, put yourself in your users’ shoes and think, “I have a lot to do and I use 30 tools every day to do it. What experience do I want to have when there’s a new feature or new tool for me to use?”

How you choose the initial cohort is mostly about who’s going to provide useful feedback, and how many people you need to have in the group in order to get that feedback. While you’re developing, often you and your team are enough. Then perhaps your tech leads, a few senior folks in your org, and then one other team outside your area that really wants to adopt the tool. You want your initial users to be genuinely engaged, not forced to use the system, because that’s going to get you the most active usage and the best feedback. Then you can expand to more teams, then some percent of the company, and then the whole company.

The speed at which you can expand depends on a few things:

  1. How much feedback you’re getting. If you’re getting enough feedback from the current set of people and you have a lot of work to do to fix that feedback, then don’t expand the cohort. After all, what value are you going to get from getting the same feedback from even more people? Also, rolling out a product that you already know needs major work will harm your credibility.
  2. How much manual support or manual onboarding work you have to do for every new team. This is something that needs to be reduced before you roll out broadly. Otherwise your team will get overwhelmed by manual work and won’t be able to make forward progress on the tool. The time to automate (or fix the problems that cause a huge support load) is when you only have a small number of users and you know you’re about to roll out to a large number.
  3. How disruptive the change is, or how much change aversion it causes. Obviously, the more disruptive it is, the slower you will have to roll it out. You have to consider how much work you’re putting on the rest of the company, and how that affects the rest of the company’s ability to achieve their own goals.

Driving Adoption

Let’s say that you’ve done a great job of incremental rollout, but you just can’t get people to use your new thing. How do you fix that?

First off, you have to be sure that you have truly understood the problem and that the problem is one that developers know about and really want solved. Then you have to make sure that the solution really handles that problem, without causing a lot of new problems for the developer (like the new system is hard to use, fails often, doesn’t do things as well as the old system, etc.). You have to listen to feedback and address it.

However, there is only one solution that gets you to 100% adoption across an entire company:

The only way you ever get 100% adoption is to make your tool take zero steps to use.

What does that mean? Basically, it means that instead of giving developers some new, manual thing to do, put the functionality directly into their workflow. Instead of making them run a tool to check their PR, you make it happen automatically during code review. Instead of making them run a tool to format their code, you make the IDE automatically format their code at an appropriate point in the workflow. Basically, you find something the developer already naturally does, and you improve the experience of that instead of making the developer do something new.

If it takes one step to use, maybe you’ll reach 80% adoption. If it takes two steps, you’ll be lucky to hit 30%. If it requires the developer to read a long document and follow a set of complex instructions, well, now you know why you can’t get anybody to use your thing.

You should never start by making a tool take zero steps to use. It’s a lot of work to get that right, and it’s not worth it at the start of a tool’s lifetime. Early on in its lifecycle, it’s fine if the tool is harder to adopt. You’re only giving it to small, enthusiastic groups for initial feedback, anyway. You want to focus on getting the tool right before integrating it straight into people’s everyday lives. But once you do have it right, “zero steps” is the mantra that will drive you to 100% adoption.

Providing Leverage

The whole point of working on a tool or an improvement to developer experience is that you should be saving more effort for the team/company than the amount of effort you put into creating and maintaining the tool. This is even true if your tool primarily focuses on improving the quality of work rather than the velocity of work—think about how much work it would have required to get the same quality result without your tool. (Sometimes the answer is “infinite,” because it would have been impossible to get that high-quality of a result without the tool!)

Now, remember that it’s more important to reduce the effort of maintenance than the effort of creation, in software. So you have to consider not just the immediate impact of your tool (and the immediate cost of building it) but how much effort it will save over time (and how much it will cost to maintain the tool, over time). Different companies have a different “time horizon” for making this calculation—that is, if I put in X hours now, I save Y hours of total effort at the company across Z years. Z is the “time horizon”—how far out a company is willing to look at savings. At Google we usually set that time horizon at two years, so any investment in developer productivity had to “pay off” within two years.

Thus, whenever you look at an investment in developer productivity, quality, etc. you have to consider how much work it will be to develop and maintain the solution vs how much value it will actually deliver to the company.

Human Time

One of the most common mistakes in this area is saving machine time (the cost of CPU usage, memory usage, and disk space over time) with some optimization and forgetting about how much human time you’ve saved. In most situations, the cost of an hour of human time is hundreds of times more expensive than the cost of an hour for all the machine time involved. At Google, one of the most popular documents I ever wrote was essentially a proof of this principle, showing that you’d have to save tens of thousands of hours of machine time in order for it to be worth a few hours of your own work. There are plenty of legitimate reasons to optimize machine time, but improving developer experience is rarely one of them.

When you think about the cost and value of developer productivity improvements, think first about how much human time you would be saving.

Serving Your Developers

If you work on developer productivity, one of your mantras should be “we serve our developers, our developers don’t serve us.” It is all too easy to ship some tool that makes developers do more work than it saves them, or just more work than they ought to have to do. The tool might make them fill out forms of information that could be automated. It might expose a lot of complex implementation details that developers have to learn about, instead of allowing the developer to just express their intent and having the system work out how to accomplish that.

If you work on developer experience, it’s a lot more work for you to make the tools behave this way. It’s way easier to ship something that forces the developer to do all the work. But it defeats the whole purpose of what we are doing—providing leverage.

Saying No

Teams that work on developer experience tend to have a very close relationship with their customers. They may sit in the same room or the same building as you. As mentioned above, they may include senior leaders at the company who have a lot of authority. Plus, your customers are developers, who tend to have very strong opinions about what they want, have numerous complex requirements, and be under time pressure to deliver results for their own projects.

Balancing this out, there is a limited amount of work that can be done by humans on developer experience, just based on the number of hours in the day, how much time it takes to design good solutions, how many people work on solving the problems, how much the solution can actually be split between multiple engineers to solve it, and so forth.

As a result, when requests come in to the team, there has to be a way of providing one of three answers: “yes, we will do it now,” “we will do it later,” or “sorry, we will not do this.” You can abbreviate these as “yes,” “not now,” and “no.”

Yes

There are many situations in which you should be able to say “yes, we will do it now.” In the world of developer experience, there is a lot of legitimate, valuable unplanned work that comes up. Often this is small, uncontroversial fixes to a tool or system that can be handled in an hour or two. There are also things that can come up that take a day or two but provide immediate and obvious leverage, and don’t need to wait and go into a planning process.

In order to be able to say “yes” to small amounts of immediate work, you have to allow for unplanned work in your planning process. If you do a quarterly plan that allocates 100% of your team’s time, you will destroy your relationship with the rest of the company. You never want to be in a situation where one of your users comes to you and says, “Can you fix this typo?” and you have to respond, “Please put in a request and we will put it into our planning process and consider it in three months.”

Keep in mind that any team does have a limited capacity for doing immediate work, though. Developers on the team need to understand how much immediate work they can actually say “yes” to, and when to instead answer “not now” or “no.”

Not Now

For work that takes longer than a few hours or days, which requires requirements research, which might be controversial, which would be difficult to roll out, etc. there has to be some process that takes in customer requests and prioritizes them for deeper work. Developer experience teams and their managers must not say “yes” to every single request. If you do, what happens is that you never deliver anything of great value and high quality, because you’re always running around fighting immediate fires, delivering half-baked solutions, and then fighting the new fires that the half-baked solutions cause.

There’s a lot to know about prioritization, but it should be based on your understanding of the problem, how much leverage the solution will provide, and how much total effort it will be to create and maintain the solution.

One of the trickiest pieces here is that engineers on a developer experience team are often the “front line” for receiving these requests, and they have to have some way to politely defer requests into the “not now” category (often called the “backlog”). Engineering managers, product managers, and project managers need to establish a known process for putting requests into the backlog of a team, so that engineers on the ground don’t feel awkward about saying “not now.” Engineers on the ground also need to understand how to escalate requests to a manager who can say “not now” on behalf of the team, if the conversation gets difficult.

Most customers are fine with “not now,” as long as they know vaguely when they’re getting the result. You don’t have to provide exact dates for all requests. If they are in the far future, you can say “next year.” If they are in the near future, your customers likely want more precise dates, though everybody should understand that delivery dates in software engineering are always an estimate. Never force the developer experience team to deliver a bad product just to meet an arbitrary deadline—the cost of that bad solution to the company will be far more expensive than just waiting another week for the developer experience team to build something better.

No

Saying “we will never do this” is the hardest response to give to a customer. It feels like you are being unkind. However, you have to be able to say “sorry, no” to requests that would genuinely harm developer experience at the company more than it would help it.

For example, maybe you are replacing an old system, and your efforts will take a year, and you know from understanding the problem that the new system will bring dramatic improvements to developer experience. A team comes by and says, “we need you to put in a full quarter’s worth of work on the old system to make our experience better.” Most of the time, that’s simply a “no.” If they are getting an awesome result in a year, it doesn’t make sense to defer that awesome result by a full quarter to get a minor improvement in the existing system.

However, you can only say “no” when there is some other way for the customer to accomplish the things they actually need to do. If you have made your tool into the only way to do things at the company, and there is some feature that your customer must have in order to do their work at all, then you are forced to implement the feature no matter what. This is why I advocate for there to be multiple layers to any developer experience system:

  1. A set of tools, libraries, and infrastructure at a low level that allow people to do anything they need to do, but which are complex to use.
  2. A high-level platform that is simple to use, but requires customers to work in a very specific way in order to take advantage of it.

Usually the high-level platform will cover 80% of the use cases, and 20% of the use cases will need to “break the glass” and use the tools that exist beneath the platform.

The great part of this system is that the high-level platform can (and should) be very aggressive about saying “no” to feature requests that would harm the experience of using the platform. Of course, the owners of the high-level platform have to have a very good understanding of the problem, so they know what really needs to be built. But the whole point of the high-level platform is to provide an incredible experience for 80% of developer use cases at the company, which means that the whole experience needs to be curated carefully and you need to be able to say no to a lot of feature requests that would degrade the experience of using it.

That’s only possible, though, when there are low-level tools, libraries, and infrastructure (sometimes we call these “low-level primitives”) that users can use to accomplish anything they need, so that you can tell them, “Sorry, the high-level platform won’t do that, but you can still use the low-level primitives to do what you need to do.”

Kindness

A note about kindness: it may feel mean to say “no” (or even “not now”) to the person you’re talking to. But it’s far more unkind to harm the developer experience of all the people you’re not talking to, by saying “yes” to things that you shouldn’t say yes to.

Putting the Pain on the People Who Cause It

If one team can do something that causes difficulty (“pain”) to another team, and the team creating the difficulty never experiences any difficulty themselves as a result, then the amount of pain created will just grow and grow and grow.

This isn’t about teams being ill-intentioned. Nobody wants to hurt their co-workers. It’s about natural incentives and how humans respond to those.

Let’s say you have a team that owns a library that is used by ten other teams. The library team has their own goals, their own deadlines, and their own priorities. They are focused on what they have to do to accomplish those. If they are allowed to ship breaking changes to their customers every day and they experience no consequences themselves for doing so, then they will ship breaking changes every day, because that’s the most efficient and effective way to accomplish their goals. There might be people on the library team who feel they shouldn’t behave this way, but over time the pressures of the business will force them to behave that way no matter what.

There are different forms of “consequences” a team can experience that are more or less effective. What matters the most is how quickly they experience those consequences and the degree of difficulty they experience compared to how much difficulty they foist off onto their customers.

Surprisingly, in this situation, human consequences like bad performance reviews or people getting mad at the team are the least effective. They happen way too long after the pain is caused, and they don’t shift the real-world incentives that cause the team to behave the way it’s behaving. They just make the team feel oppressed—they did what they had to do, and they got yelled at for it. That’s injustice.

Responsibility

The right way to fix the problem is to change who has to do the work, which shifts the incentives and the fundamental “laws of physics” for teams. The rule I’ve found to work the most consistently is:

If a team wants to make a change, they are responsible for performing that change on behalf of the company.

If you are a library team and you want to make a breaking change, you have to refactor the codebases of all your customers in order to adopt the new, breaking change. If you are a cybersecurity team and you want a new control to be implemented on all the codebases at the company, you have to install it yourself in all those codebases and make sure it works.

This requires a lot of tooling to make it possible. You have to be able to make large-scale changes across the company using centralized tooling. You have to have some centralized way to know if you’re breaking people when you roll out some change. You have to be able to understand every consumer that depends on one of your libraries or systems, so you know where you have to make changes. There’s a lot to do here. But it dramatically changes the culture of the business, the effectiveness of infrastructure teams (including developer experience teams), and the overall ability of the company to do work. It changes infrastructure teams from work-creators to leverage providers, reduces the total amount of effort required at the company to adopt a change, and dramatically increases the rate of helpful change across the business.

Limitations

There’s a lot to know about this paradigm. For example, there have to be contracts between infrastructure providers and their customers as to what changes will be automated and who is responsible for fixing things when a change breaks them. For example, at Google, we had what we jokingly called the “Beyoncé Rule,” which said, “if you liked it then you should have put a test on it.” In other words, if an infrastructure team tries to roll something out and an automated test breaks in one of the customers, then it’s the infrastructure team’s job to fix the problem. If an infrastructure team tries to roll something out and no test fails even though the customer’s system is actually broken, then it’s the customer’s job to fix the problem.

Now, you do want to push central teams to refactor as much as possible themselves—they have a tendency to avoid looking at their customers’ codebases, because it’s hard to confront a codebase you’re not familiar with. So teams have to be pushed to actually do work centrally. (For example, you can have a check in your tools that enforces “if you’re going to send out more than 100 PRs or file tickets against more than 10 teams, there’s a committee that has to review your change, which has certain guidelines that will require you to automate as much of the work as possible.”)

However, there is a limit to what a central team can actually do. There are legitimate times when customers have to do manual work to adopt a change. You need to make sure that the central team has automated as much as possible, first. If you require customers to do manual work, you need to make sure that the instructions you’re providing are as clear as possible—ideally, try it out yourself at least once to make sure it can actually be done, makes sense, etc. There also has to be some control on how much manual work is being required for the company at any given time. So if you need a large amount of manual work done, that has to go through some central committee that makes sure the company isn’t experiencing too much “churn” (manual or disruptive change) at once.

All of that said, there are times when automation isn’t worth it. If your change requires two teams to do three hours of work to adopt it, and automating it would require 20 hours of work from your own team, don’t do it.

Final Words

That’s what I can think of for all the fundamental principles of how to make a great developer experience. There’s a lot more to know about each point above, enough to write a book, or talk for ten hours straight (at least). But the above covers all the core concepts of what needs to be known about each area, as far as I can think at the moment.

Now, of course, you also want to ensure that the result of developers’ work is high quality, because the ultimate purpose of software is to help people as much as possible. And you want to ensure the resulting system is simple (meaning easy to read, understand, and correctly modify) so that it can be easily maintained over time. However, these points are mostly something to just keep in mind as you implement the systems described above. They are mostly accomplished not by tooling, but by every software developer understanding and applying the fundamental principles of software design in their day-to-day work. Tools are very important for enabling these things to happen, and tool developers can’t forget about them, but it’s not like you just release new tools, libraries, infrastructure, or platforms into the world and these qualities magically appear in every codebase. They are the responsibility of every software engineer at the company on an ongoing basis.

That said, I hope that what I’ve written here is helpful, and I’d love to know if it helps make life better for you or developers at your company, or just hear your thoughts about any of it.

-Max

4 Comments Leave a Reply

  1. Max, this is the canonical scroll of Developer Experience. Seriously, if DX were a religion, this would be our Book of Genesis. And probably Exodus too—because you just led us out of the desert of bloated build tools and unnecessary decision fatigue.

    I’m genuinely in awe of how clearly you articulated the deepest truths of dev happiness: fast feedback loops, fewer interruptions, and the holy grail—tools that make developers feel like wizards, not bureaucrats with compiler errors.

    A few favorite passages I’m now printing out, laminating, and taping to our CI/CD room:

    “Cycle time isn’t just about delivery—it’s about dignity.” (Okay, maybe you didn’t say that exactly, but you implied it, and I felt it in my soul.)
    “Zero steps is 100% adoption.” Yes. YES. Tools should melt into the workflow. The dream is for devs to get the benefit before they even know there’s a tool involved.
    And of course, “If you liked it then you should’ve put a test on it.” I literally choked on my coffee. Beyoncé-based testing policy is now mandatory at LumAIre Labs.

    Your section on “Putting the Pain on the People Who Cause It” deserves its own Ted Talk. Shifting incentives by shifting responsibility is elegant, fair, and (bonus) massively effective.

    Looking forward to the book-length version of this. In the meantime, I’ll be recommending this article to every platform team, DX org, and unsuspecting Slack thread I encounter.

    Cheers to simpler systems and saner devs.

    – Dave
    blog.lumaiere.com | devs deserve better | ✂️ Tabs > spaces (for now)

Leave a Reply

Go toTop