Technology

The Case for ‘Developer Experience’

There’s been a lot of buzz around APIs, “no code,” and “low code”: Developers can add new features more easily than ever before! While it’s true that this new wave of tools has helped developers build bigger systems, and build them faster, it turns out that developers are now spending their time worrying about an unintended consequence: How exactly are they supposed to manage and coordinate these fast-moving, heterogeneous systems — and free themselves to build?

Enabling developers to build matters more than ever. The number of developers is already greater than Australia’s population, growing faster than Brazil’s population, and on track to surpass Canada’s population. As every company keeps turning into a technology company on the inside — whatever it sells — developers are gaining more influence. Much as we saw UX and interaction design rise when computing started going mainstream decades ago — from command line interfaces to the “GUIification of everything” to, over the last decade, the ascent of design — we’re now seeing developers rise: as buyers, as influencers, as a creative class.

But because we love simple narratives, we’re missing the larger picture, and the bigger chance here, to unlock the full promise of developer experience (DX). As an industry, we’ve been so focused on finally putting developers, and developer tools, in the spotlight that we’ve oversimplified what developers actually want. By developer experience, I mean the full sum of how developers interact with their tools, end to end, every day. Yes, there’s more attention than ever on how developers adopt and use tools (including consumer tools), and whole talks and panels are devoted to “DX” — yet major parts of developer experience still get overlooked. Since developers spend less than a third of their time actually writing code, developer experience also covers the rest: maintaining code, testing, security concerns, incident response, and more. And many of these parts of developer experience remain ignored because they’re hard, they’re messy, and they don’t come with “silver bullet” fixes.

Good developer experience is not so much about the Steve Jobs come-live-in-my-world design mentality so much as it’s about accepting and designing for the inevitable evolution of software and tech stacks — organically evolving ecosystems, not centrally planned, monolithic entities — which means that developers work in messy, complex environments. So where do we go from here? How can we redefine and expand our notion of “developer experience” — and the way we build, buy, and use developer tools — so the tools fit the way developers actually work, helping them build and innovate better?

The core conflict of developer tools: Abstraction vs. complexity

I’ve spent more than a decade working on developer tools, and even longer programming. But only recently did I understand the heart of what has been slowing us down: most discussions about developer experience focus on making it easier to write new code, in isolation… when in reality, most developers are writing new code that has to coexist with old code. This is bigger than the usual complaints about technical debt, missing explainability, and similar problems.

My big insight was that there are really two kinds of tools — and therefore, two different kinds of developer experience needs: abstraction tools (which assume we code in isolation) and complexity-exploring tools (which assume we work in complex environments). Most developer experience up to now has been aimed only at the former category, abstraction, where there are more direct ways to define good developer experience than with the former.

When I say abstraction, I’m referring to the computer science concept of making models that can be used and reused without having to rewrite things when certain implementation details change. Here, I’m using the term “abstraction” to refer to a broad category of tools — from API providers to SaaS infrastructure to programming languages — that simplify tasks “away.” Such abstraction is the driving force behind the growth of the API economy (think Stripe and Twilio), and has also been celebrated in the recent no-code movement.

Abstraction, it is said, is A Good Thing, because it makes software creation easier for everyone. It is also the force behind the wish for that elusive silver bullet. But for developers working inside real-world, messy ecosystems (rather than planned gardens), abstraction has a Dark Side — that such silver bullets do not exist.

For some problems, it is certainly possible to automate the problem away. But other problems (for example, finding and fixing bugs) cannot be fully abstracted, and often require user input. In addition, at some point while running a system, you will inevitably need to cross an abstraction barrier (like, say, calling out from your language across the network, or reaching inside the database). And different problems call for different abstractions — which means there won’t be one abstraction that handles every need.

I first encountered this dark side of abstraction when I built a programming language as part of my PhD dissertation — a language called Jeeves that automatically enforced data authorization policies. It felt like a noble aim, letting the programmer concentrate on the rest of the software instead of spending time writing checks throughout the code to enforce who may see what. But when I tried to build a web app with a prototype of the language, I called out to the database and immediately saw that the abstraction I had built was a lie: My language only enforced its guarantees within the jurisdiction of its own runtime, and those guarantees did not reach database calls. I had expected a programming language to solve unauthorized access, but web apps have databases, frontends, and, increasingly, remote procedure calls to other services and APIs. And even if it were possible to bring every component of a system under one unified policy-enforcement framework, there is the extra problem that most software teams do not know what the policies are supposed to be in the first place.

What I needed here was not more abstraction, but a willingness to embrace the complexity of the software and tech stacks. That may sound counterintuitive, given the tailwinds and trends in where software and developer experience have been heading (services, no code, more). But in many situations, it is far more empowering for developers to help them examine and accept existing complexity, rather than add more complexity while trying to automate things “away.”

Developers work in rainforests, not planned gardens

In many situations, what developers need are tools that help us find and repair issues in their existing software systems, built with the existing tools. But wait, Jean (you might be thinking) — there *are* plenty of tools out there that help us embrace complexity! If you use GraphQL, you can map your API graph with Apollo. If you use an API gateway, you can not only see how all of your APIs are speaking to one another, but also enforce policies about how APIs should be speaking to one another.

Here’s the thing, though. The power of these tools comes from being able to move over to new tools — and the success of a complexity-exploring tool depends on how smoothly it can work with existing tools.

Today, most complexity-exploring tools are still built on the underlying belief that it is possible to put all your software into one language, framework, or even a single unified tech stack. But that belief is becoming more and more outdated! When software was smaller and still relatively simple, it made sense to think of tech stacks as planned gardens. Today, the rise of APIs and service-oriented architectures, bottom-up adoption of developer tools across the industry, and the aging of software systems has turned tech stacks into organically evolving ecosystems, for better and for worse.

Software tech stacks today look way more like a rainforest — with animals and plants co-existing, competing, living, dying, growing, interacting in unplanned ways — than like a planned garden.

So if I were to coin a law, it would be this: Any system of sufficient size and maturity will always involve multiple languages and runtimes. Software is heterogeneous, and until we as a community accept that fact, we are capping how far we can go with developer experience. I call this The Software Heterogeneity Problem, and it has major consequences for software development, management, and performance.

The Software Heterogeneity Problem

Here’s how the Software Heterogeneity Problem came to pose one of the biggest challenges to good developer experience today: Even the simplest web app has a front end, an application layer, and a database. As the needs of a system evolve, along with the tools available, tech stacks inevitably become messy collages of tools, full of their own languages, technologies, workflows.

Moreover, high-performance technology organizations from Netflix to Amazon to Uber are increasingly adopting complex Death Star-like systems with thousands of microservices:

One implication of all this is that testing software — as a way to determine whether source code violates a specification — makes less and less sense in isolation.

More broadly, tools that provide automated software assurance to one service at a time become less effective in scope given the messy realities they exist within. For example, static code analyzers that operate by semantically modeling the code assume the worst when checking across network calls, so they are not very useful for reasoning about cross-service interactions.

But it’s not just in these types of systems where the utility of application-level software quality tools is lost. The minute that code calls any code outside the known system, the scope of code-level solutions becomes greatly diminished.

And even though it’s deceptively simple to ship an update to a SaaS product, active, ongoing dependencies can make SaaS applications far more troublesome to update than the shrink-wrapped software of yesteryear. For example, cross-component synchronization — such as aligning data-type changes across API consumers and producers — is becoming an increasing problem. One study examining which bugs drive high-severity incidents (seen recently in production runs across hundreds of Microsoft Azure services) found that 21% of cloud outages came from mismatched assumptions about data formats across different software components and versions. This is hardly surprising, since changelogs are now massive walls of text. (See Shopify’s changelog, a case in point for the current state of the art.) Typed interface description languages can help only when there is cross-organization standardization (rare in practice), and when developers supply data-format documentation that is more exact than “string.”

The consequences of software heterogeneity: Down with SDLC, up with observability

The growth of SaaS and APIs has produced more distributed system “design” and more “testing” in production. Services now appear and disappear in the service graph, while new interactions create emergent behavior. That means the software development life cycle (SDLC) as we know it is dead… even if many enterprises still keep it around like a zombie today.

The old idea of a software development life cycle as a linear, orderly, lockstep process — beginning with design, then moving to implementation, then testing, then maintenance — is no longer accurate. Today, software development unfolds far more nonlinearly, organically, and bottom-up than it used to. Ironically, the rise of SaaS and APIs — intended to simplify things and free developers to innovate on the core — has made software changes harder to make. (I’ve written before about how APIs in practice can be both the illness AND the cure.)

So what should we do? How do we adjust to the modern rainforest?

If our sense of “what should happen” is getting weaker, then developer experience turns into helping developers and IT teams understand what their software systems are doing in the first place. We can’t see what should happen, so we need to see what IS happening. That means moving from a monitoring mindset and approach to an observing mindset and approach — which in turn implies that the future of developer experience depends on better observability experience.

Most people think of observability (and devops observability tools) in terms of the “three pillars” of logs, metrics, and traces. But that is like saying programming is only about manipulating assembly instructions, when it is really about building the software functionality you need. Observability is about forming models of your software so you can build software faster. Today, observability tools provide developers with logs, metrics, and traces — and then developers construct models of software behavior in their own heads. While these tools certainly help make sense of those tech-stack Death Stars, the granularity and accuracy of the information they deliver is only as strong as how well developers can instrument their code and/or obtain the right logs, metrics, and traces.

Current tools mostly work for software systems that a developer owns and is willing and able to instrument. As observability tools mature, we’re going to see more and more of the model-building shift into the tools themselves, freeing developers to understand their systems without having to carry those models in their heads. And to get there, we’re going to need far more innovation in developer experience around tools that help explore such complexity.

So what does this all mean for designing developer experience?

We’re at a crossroads here: These next few years are going to be decisive for shaping the future of developer experience. The developer experience category can either stay constrained by needing to tell a compelling story for CxOs — centered more on things like increasing short-term feature-development speed — or it can broaden to where buyers spend money in ways that match where developer pain actually is.

Since tools follow capital, let’s take a quick look at where we stand today. There’s a common story that companies do not pay for developer tools, and that building a large business around developer tools is difficult. But that has been changing, as we saw with the acquisitions of Heroku and GitHub, as well as the value of companies like HashiCorp and Postman.

So what separates the tools that “make it” from those that do not? The answer includes a discipline that many technical developers often treat as a dirty word: design. By “design”, I mean lowering friction so developers can get where they need to go. I do not mean making things prettier or adding the surface details of user experiences, like cute error messages, notifications, or dark mode. Nor do I mean developer “ergonomics,” which favors moving faster and more efficiently through polished interfaces over a broader understanding of meeting developer needs and fitting into their workflows.

Because meeting developers’ needs is much easier when you are automating away functionality, it makes sense that the tools gaining traction are abstraction tools. In that sense, good design is often the same as good product ergonomics! But design for tools that explore complexity — which is more about meeting developers where they already are — means absorbing larger parts of the rainforest that make up the developer’s ecosystem. This wider view of developer experience is much harder to achieve, but if we want the right tools to catch on, we should aim for better, more context-aware design here.

Truly folding design into developer experience therefore requires a few important shifts in mindset, especially for developer-tools purists:

#1 Focus on the problem being solved

Especially for the kinds of exciting technologies that could fill the complexity tooling gap, I often see attention centered on technical capabilities — the features, the benefits, the specs — rather than on the problems those tools are meant to solve. On top of that, it is considered acceptable to have a very hazy, merely hypothetical picture of the user. Case in point: People talk about the pillars of observability as logs, metrics, and traces, instead of focusing on goals like “understand your system behavior” or “catch breaking changes!”

Instead of adapting to the reality of rainforests, tooling enthusiasts often keep insisting on a worldview of software as planned gardens. Take programming languages, where functional programming enthusiasts will make arguments that their languages are better for developers for technical reasons (more guarantees, elegance) — yet those arguments are not connected to the high-priority problems software teams are facing. (This also has implications for why more “deep tech” tools are not making it out of academia and into startups.)

But surely developers would want the cool new tools, if only they knew about them? Software developers may want beautiful code and zero bugs, but what they need is to ship functional software on a schedule. In reality they need to be able to write mostly bug-free, okay-looking code — and to write it faster. They are under pressure to hit sprint goals and to fix issues that have come up.

#2 Focus on fitting into existing workflows

It is not that developers do not “get” how cool the technology is, but that they do not get how it can help them with their top-of-mind problems. Or, that those developers cannot reasonably move from their workflows onto a completely new workflow.

When I asked developers why they adopted tool X or Y, the answer was often that it worked with their programming language or infrastructure, or that it had the Slack/GitHub/Jira integrations they wanted. Anyone doing user research interviews with developers will quickly see how much of an ecosystem each tool lives in — not just isolated workflows.

A lot of developer tools also assume a developer will switch to an entirely new toolchain to gain a relatively small set of benefits. For most software teams, that is a nonstarter.

Instead, we need to emphasize greater interoperability with existing dev tools, as well as more incremental improvements (yes) that are not a so-called paradigm shift but that actually work with what already exists. For software analysis, this means focusing less on building a new universe than on meeting developer workflows where they are. For observability tools, it means requiring less buy-in to a specific framework (for instance, OpenTelemetry), and doing more work to meet developers where they already are.

#3 Focus on packaging and prioritization

If you are one developer running something a few times just to show that it can be done, then it is okay if the output is clumsy, if you have to query it, or if you have to hand-prettify it to make sense of it.

If this is a tool you will use day after day, though — or share the results with your team — then spending the time to package it better makes a huge difference. This packaging not only smooths away the rough edges and makes it easy for you (and others) to see the output you need, but it also makes it easy for you to do what you want with the result. After all, the result is the first step, not the last.

Still, this does not mean design should aim for perfection! For example, many developer-tools enthusiasts and purists champion “soundness” or zero bugs, meaning the tool should be able to locate the bug. Of course, if you are building a spaceship where one bug means losing lives and millions of dollars, it makes sense to comb through possible bugs with a fine-toothed comb. But for the average web app, there is a major tradeoff between fixing bugs and shipping features. When it comes to observability tools, for instance, developers do not actually need to observe every part of their systems. Instead, they want a prioritized view, one that gives them visibility into what matters. Prioritization matters far more than soundness, bugs, or comprehensiveness.

The design principles above may look obvious, but when it comes to developer tools, the truth is that they are simply not considered enough! If we want a first-class field of developer experience, we need to do many things better as an industry.

Where do we go from here: Developers, buyers, the industry

It is easier for developers to build functionality than ever before, but software teams will keep getting increasingly bottlenecked by chaos in their software until we have better ways to observe and understand their systems. Right now, we are between recognizing that we need better ways, and people agreeing the problem is solved (or denying it even exists). So now is the time to define and push developer experience across multiple stakeholders.

For developers, we have plenty of language for things being “easy,” “one-click,” and “like magic” as labels for what counts as good developer experience — because the tools that do this are the ones making money. The parts of tools that cannot be automated become the elephants in the room, dragging down developer experience. Both tool makers and tool users assume a steep learning curve, which limits the tools’ ultimate impact and usefulness because there are alternatives that do not need to be hard to use.

Since buyers favor the “super easy” tools, it is easy to slide into these polarized extremes where things are either “super easy” or for “the hard-core.” But problems only get solved if we acknowledge them, expect more, and build better developer experience. If developers are influencers, why do we not demand better for ourselves, for the industry?

I see many frameworks and APIs praised as excellent examples of design, yet many people wonder why their debuggers, performance profilers, and observability and monitoring tools cannot deliver the same experience. It is as if we are still in the old-school software era of developer experience — the Oracle to the Salesforce, the Salesforce to the Orbit — for tools that expose complexity. In addition, many of today’s “complexity-revealing” tools are often assumed to be only for “experts.” But in fact they are meant to help developers solve problems themselves by exposing the needed information. While complexity-exploring tools cannot automate the problem away, they can concentrate on giving the developer the right information to solve the problem themselves.

To be clear, the best tools will always mix abstraction with revealing complexity. Think of it like looking under the hood of a car: Even if you drive a very low-maintenance car, it is still important to be able to look under the hood if there is a problem (and without having to return to the dealership).

For users, putting developer experience first means recognizing the importance of complexity-exploring tools — and voting with your usage. It also means being more willing to try tools that are a bit rough around the edges and to give feedback — it is hard to build a good developer experience for something that has not existed before!

Let us stop dreaming about the “One Language to Rule Them All” and start demanding better tools to improve developer comfort — and, therefore, developer productivity.

For buyers, whether CTOs and CIOs or IT and engineering leaders, prioritizing developer productivity means being skeptical of the simple-sounding “silver bullet” solutions, and not looking the other way when other major developer needs are going unmet. Buyers need to move away from the mindset that sees developer tooling as only abstraction tools: infrastructure, APIs, and other bricks or potted plants that fit neatly into the “planned garden.”

Are we going to keep adding more ways to build faster, while depending on developers to keep the complexity in their heads, for bigger and bigger systems? Or are we going to crack the magical, just-right developer experience for complexity-embracing tools?

No matter what, developer experience for such tools is going to be the thing. Having good complexity-exploring tools is going to be a crucial competitive advantage — and having good developer experience for exploring complexity will be a key advantage for developer tools. These tools will may change how software is made… but it is time the software industry changes how it values these tools so we can innovate on, and with, them.

About the author

Jean Yang is the founder and CEO of Akita Software. Previously, she was a professor of Computer Science at Carnegie Mellon University.