My Guide for Great Developer Experience (DX)

What is DX, why it matters, and my guidelines for great UX. 

 

What is DX?

Developer Experience (DX) is the equivalent to User Experience (UX) when the user of the software or system is a developer. DX describes the experience developers have when they use your product, be it client libraries, SDKs, frameworks, open source code, tools, API, technology or service. DX shares some ideas and philosophies from UX design (or HCI), but builds on these with an eye towards modern technology and standards. 

Why does it matter?

DX matters for the same reasons good UX matters. Users of your technology are happier, promote it more, and stay longer when the product has good DX. Happy developers are chatty developers, and when we talk to each other to recommend products, the ones with the best DX are at the top of the list. Follows is my list of best practices when it comes to DX. Do these, and you’re well on your way to success.
 

The Guidelines

Communication

Good communication is key to a successful developer experience. The way you speak to developers is crucial to how they perceive you and your product. 

Tone: Be Authentic, Open and Honest

The way you speak to developers is crucial to how they perceive you and your product. Developers can smell bullshit, and they don’t like fluff. If you be honest with developers, they’ll respect your product more.

If you have a data breach, a bug, a period of downtime, or some data loss, be clear with this. Create a status page and comms system and use it to communicate with your developers. Be honest about your timelines and be prompt with any post-mortems or debriefs. 

Communicate product roadmap with product forums, and let developers contribute to those priorities directly. Engage with them, ask them what they think. 

Great Documentation

Your docs are your voice to the customer. But what makes for great documentation? 

Always write your docs as if the developer is a beginner, not just with your technology (which they definitely are) but also the platform for that technology. If you’re documenting an NPM module, include a link to how to even install NPM in the first place, and how to include the code into other JS files. If it’s a ruby gem, the same thing. 

Consistency of language in the docs is also important. If you have multiple contributors to documentation, make sure they’re all using “function” instead of “method”, or “package” instead of “module”. This also is important for your own product related nouns, such as “Activity Log” instead of “Log”. Being consistent stops readers from becoming lost and second guessing what they’re reading. 

Thirdly, a logical layout and structure to your docs is important. Surface common reading, and lay out docs in order of integration steps. An “Overview” with core concepts then “Getting Started” section with installation and/or set up is a great start. What follows after that depends on your product, but you should know based on what you expect people to do — just document that expectation. 

Finally, verbosity is always great in docs. You can never say too much. It’ll be one sentence or a few extra words that really helps a stuck developer. It’ll mean the difference between a developer who feels comfortable and supported versus an irate one calling your support line. Docs can sometimes feel like a last minute thing, but slowing down, investing in them, will pay dividends in the long run. 

Good Release Notes and Changelogs

When releasing a new version, it’s another important time to be on the ball with communications to developers. Explain clearly what is new, what was broken but now fixed (yes, own your mistakes, please), and what any risks of this version is (eg, this might break x). Even better if you can link to a public issue tracker for these for more detail. Why? Well, when developers want to catch up on versions, they want to be able to quickly scan the release notes or change logs and try find the thing they’re looking for. They might be asking “which version did this get deprecated in?” or “what version did x get added?” or “what version did they fix the bug I’m experiencing?”. If you can answer these in your release notes, then you’ve nailed it. Release notes are also a good place to point out any security vulnerabilities that have been fixed, which obviously add more motivation to update. Developers should always be motivated to update to the latest version. Having them on the latest version also reduces support overhead.

 

Respect Standards and Idioms

When writing a client library, SDK, API, etc, it’s important to respect the standards of the industry and/or community and idioms of the platform from those gone before you. 

Respect Platform Idioms and Toolsets

I work on an iOS SDK as part of my day job, and when I do this, I must always be mindful the app developer using this. What Xcode version are they on? What language are they using? Objective-C or Swift? How can I make this API work better with either of them? While just an anecdote, it’s important you also think of the same thing. So, think to yourself, what tools are people using to interact with me? What versions are they? Do they make a difference?

Don’t Assume Programming Paradigms 

It’s easy to think that because you like a certain style of programming, others will like it too, but this isn’t always the case. For example, if you like functional programming, you may be tempted to write your library with a functional interface. Please don’t. Write it the plain, or more traditional or standard way, such as plain JS vs a Fluent Interface or plain Ruby or plain Swift vs a functional style API, or a Java Builder pattern vs just an object to construct, and then also provide a wrapper or facade for a given style. That way, everyone can use it, and no one has to learn a new style of programming just to use your code. If there is no given paradigm, chose what is best, preferably most likely to prevent mistakes and a quick time to learn. 

Consistency

Consistency is key with interfaces for developers. Very much for the same reason as in UX, it stops people from making mistakes. Be consistent with the rest of the language or platform. For example, let’s say you have a simple library that talks to and endpoint and provides a native object for use. If you’re writing Java or C#, your asynchronous methods might expose a Listener pattern, which is very consistent with the language. However, if you’re writing Swift, you the equivalent method might expose a callback. If it’s in Javascript, it might also be callback, but it could be a Promise. Figure out what’s common on the platform by researching other code (with the ultimate source of truth being the platform source itself), and then use that. 

If it doesn’t matter, then be consistent between your different libraries across the different languages, but platform consistency comes first. Methods should be called similar names across the platforms, so developers integrating in multiple languages can gain momentum, and generally because it’s just cleaner. This goes back also to Respect Platform Idioms and Toolsets above, keep this in mind as you ship code for a certain language or platform. 
 

Be a Good Citizen

For code that will be embedded in other people’s code (such as frameworks and libraries), it’s best to make sure your code isn’t going to be a cause of concern. If anything, this code needs to better than the code it’s in as it should never be an issue. 

Method Annotations and Docs 

If you’re exposing methods, try use annotations to indicate a few things. Some languages like Objective C and Swift provide these features, where as some tools like Android Annotations provide this. The first is nullability, can the thing you pass me here be null? What does that mean if it is? And can what the method returns also be null? With these explicitly called out, Developers can change their style to code more defensively or not around these cases. For as something as simple as adding an annotation, you may save your users a NullException or similar. 

Secondly, over-document. Document each parameter and method carefully, and name them well. Probably name them better you would your internal names, in an effort to make them accessible to beginners. The more documentation about how to use the method, any prerequisites or postrequisites it needs, and a hint to how it works, and any equivalents it has or redundancies, and any side-effects. 

Not only will this help external developers, it’s also great for your internal team to get up to speed if they’re making a change to the code. Well documented code is great. The usual rules of “don’t over comment” and “your code should be self-documenting” just don’t apply for external code. The more the better, always. It’s about reducing confusion and stopping that support line phone ringing. 

Method Removal and Deprecations

If you want to change your mind about some external methods, you’ll need to slow down and let your developers adjust. The recommended approach for this is to introduce the new behaviour and deprecate the old. Deprecate refers to functions or classes that are in the process of being replaced by newer ones. What you must not do is just remove functions or change their behavior, as this will almost certainly affect other code that interacts with it. You should always deprecate the old first, an in a future, preferable major version(more on this later) remove the deprecated methods. Deprecated periods should last at least a month, but can last up to a few years. 

You’ve probably felt me defining deprecation above was a bit silly because you’ve seen and dealt with them before, well, exactly — it’s a great way to communicate and you’re used to it — so do it yourself when you’re the one providing the code. 

Non-Colliding Namespaces

If your language that you’re programming with doesn’t have namespace or package support, then you should prefix your code to avoid this. This is fairly common in Objective-C, where a lot of provided classes have the NS- or UI- prefix. My code as SJ in front of it, for my name, or sometimes a acronym based on the name of the project. Using two or three letters (preferably three) reduces the risk of errors during compile time, where your code is named the same as something in the rest of code. 

If your language supports namespaces and/or packages, make sure these are also configured for the same reason. 

Threading

Arguable more aimed at mobile development, be careful with threads and processes. Try use a secondary or background process to do your work such that actions such as UI work aren’t blocked by your code. Because of this, code with common multi-threaded pitfalls in mind, and test carefully. 

 

Be Open Source (At least try!)

While I acknowledge that some code must stay closed source, if not required, have it be open source. Follow the open source best practices, which, in themselves are part of a good DX. This advice is more of the same as above, centering around good communications. 

Why Open Source?

Open source code is more trusted by developers, and anecdotally, more developers are exclusively using only open source dependencies in their products. They're able to look into the source and find and fix bugs, as well as inspect it for any code that is questionable, and remove it. 

Great Read Me

What’s in a great Read Me? A full list here is here, but in short, a good description, installation notes, good (trivial examples), contribution guidelines and any known issues. Links to more docs is also great. Some of the best projects I see also shout out top contributors, or shout out notable users of it. 

Safe Space for Contributions

Having a Code of Conduct for your open source efforts is super important. Your users could be anyone, and their contributions should be welcomed and protected no matter who they are. A code of conduct (even the one Github gives you by default) is great, as long as you action it if it is broken. Create a safe space for contributions, remember, these people are probably adding value to your company, why not do the best for them that you can?

Open CI

If possible, and if it makes sense to, open your CI system too. Most hosted CI systems are free for open source projects, so just hook it up and tie it in. This will make it easier to contribute with pull requests and makes it easier for you to review and accept or reject contributions. Some would even recommend going further, and using bots. Use what you think you need. More active repos will need more automation to scale. 

 

Make It Easy

Make it easy for developers to actually get your product and integrate it. It’s amazing how many products would benefit from developers using it, but don’t understand how they think. 

Clear Pricing

The first of these is clear pricing. No developer ever will ever click a “Book a demo” button. Let us have a free trial, or pop in our credit card and give it a spin. We want to know how the pricing works and what variables control how much it cost. Even better if that makes sense to us, eg things that would cause more costs for you, such as higher number of requests or more compute time used. 

Sure, value-based pricing is a thing, and you may want to work out a deal for larger customers, but if freaking SpaceX can have a pricing page, so can your software product. 

Self Serve Option

As above, we want to be able to use it ourselves. We don’t necessarily want to sign a custom contract, have to get on a call with a sales person, or pay an implementation fee. We just want to sign up, integrate it that day and see if the price makes sense for the value we’re getting. Sure, your business may not work like that (and that’s okay), but that’s a compromise of the model, you miss out on a self-serve market. And as above, developers are a chatty bunch, and they might recommend your product to their friend who works for that customer you’ve always wanted, so you never know. But, of course, use your best judgement.

Versioning

Version your code and products correctly. Semantic Versioning (SemVer) is what the industry has landed on, and it’s good, and you should use it. In short, a version number should have three parts, three numbers. Eg, “X.Y.Z”, or “Major.Minor.Patch”. Patch releases fix bugs. Minor adds small features, and major removes deprecations and releases bigger features. So, when versioning, think carefully about what version number you are really meant to use, such that you communicate to developers the impact of the version. And sorry if you spent two weeks on a change, it might only still be only a patch version. The semantics of the version number matters more.

Packaging

Most languages now have one or more accepted way to distribute code. There’s NPM and Yarn for JS, RubyGems for Ruby, Nuget for C#, Maven for Java, CocoaPods/Carthage/SPM (grr) for iOS, PIP for Python… and the list goes on. Your code should be distributed by this way as well. As a developer scans your installation docs, they’re looking for the line that installs your product via one of these systems, they’re looking for gem 'Sam' or npm install sam in the first paragraph. Take the time to generate the files required to interact with these systems, and use Git Tags and SemVer to create and push releases to your users. 

Secondly, if the packaging system supports code signing or security features like that, be sure to use those, so your developers know the software came from you, and is less likely to be compromised. 



Do Your Best

To finish off, we get onto the obvious stuff about the actual quality of your product. 

Use Your Own Stuff

If you’re shipping anything that a developer will use, be sure to always test it as if you’re a new developer. Create a fresh project, integrate it and test it. Try to rule out any false positives from your dev environment and really just eat your own dog food. The more you do this, the more knowledgable you’ll be about the integration process, and that’ll lead to finding gaps in documentation, ReadMes and much more. Give this task to someone on your team completely unfamiliar with the product (or a friend if you’re solo) and see what they find confusing, and improve from there.

Feature Set

The feature set of your product must match what a developer has come to expect from competitors. However, you might have something they miss, and that might indicate a UX problem, or poor DX. Ask developers what is missing, and use that crucial (and free) feedback to make your product better. A good product also scales under load or is built for purpose well. It is also secure noting the latest concerns and keeps its dependencies up to date.

So that’s what makes for great DX. In conclusion, a lot of DX is just doing what you’d expect if you were a developer. Put yourself in their shoes (and remove your knowledge of the subject) and try see what you’d expect, and do that.

Have Fun

Developers, like everyone like to have fun. Keep all your interactions light hearted and enjoyable for both sides. Remember: If they're using your product, you're on the same team. You're just two people working together to solve a problem. Use your teamwork skills to make the interaction positive and enjoyable.


Need help with this? Get in touch and I can help out you and/or your team.