Orthogonality is a somewhat fancy, definitely mathematical word. But it’s a crucial property of good developer experiences. From programming languages to API design, orthogonality makes all the difference between experiences that “just work” and experiences that are frustrating.
Orthogonality is a mathematical term that shows up in several mathematical fields. The most common one is geometry, where lines that are orthogonal are said to intersect at a 90 degree angle and be perpendicular. In other words, two orthogonal arrows are pointing in completely unrelated directions.
This mathematical property can be generalized to any kind of system, including computer systems! Two features are considered to be wholly separate if they are orthogonal to one another. But how does this relate to developer experience? Let’s use the perpendicular lines as an analogy.
Let’s say that there’s two lines at a 90 degree angle: a horizontal line represents the unix command curl and a vertical line represents the unix command grep. One way to interpret these lines is that at any point you use curl, you can always use grep too. That’s pretty intuitive to most developers: you can use curl or grep independently of one another, and you can grep the results of curl any time you want.
grep isn’t related to curl. You aren’t limited in your use of grep such that you can only use it every fifth time you call curl. You aren’t required to use grep after curl either. The fact that you can grep the results of curl doesn’t affect curl in any way. It is orthogonal.
Orthogonality shows up everywhere in developer experiences, big and small:
- Some of the best APIs let you make specific calls independently of others, letting you compose them together, or perform a CRUD operation from any arbitrary state you might be in
- Unix command-line tools are based on the idea that their inputs and outputs are files, and so they can be arbitrarily combined to process data in nearly any way you might need
- VSCode lets you install and use any arbitrary collection of extensions at any time
- Programming languages are designed with orthogonality in mind:
- Access modifiers in C# don’t depend on if the type is a class or struct
- Generics in Java, TypeScript, C#, and Go let you write operations on data that don’t require you to say that it’s for a specific type (like arrays vs lists) etc.
- Tools like Gitpod let you create a developer environment from any git repository with a URL, not just GitHub repos
Non-orthogonality also shows up everywhere, and it complicates developer experiences:
- APIs that aren’t idempotent - the same operation made N times will have N different behaviors - can lead to untold problems that confound developers
- Source-incompatible and/or binary-incompatible changes in libraries force developers to try and figure out the right combination of versions of packages to get their build working
- Observability and/or Monitoring tools with pricing schemes that differ based on data type force developers to optimize their consumption differently, for different reasons, at different times
- The Windows API used to restrict path lengths to 260 characters, leading to the frustrations of millions of developers for decades
- Some APIs are “spooky action at a distance” - the behavior of some code changes depending on whether a different file or config exists
At this point, you might have the impression that orthogonality is “everything good” in developer experience and non-orthogonality is “everything bad”. That is often true, but there are cases where it’s not. For instance, if you’re doing high-performance work when building an app, you’ll usually end up using stuff that trades off orthogonality for performance.
An example of this is the
Span<T> type in .NET
- Span is an abstraction that let you easily write zero-allocation code, but it comes with a bunch of special rules about where and when you can use it because it’s a type that’s guaranteed to never cause a managed heap allocation. That makes Spans inherently harder to use than “normal” stuff in .NET, but the tradeoff is often worth it because it’s still a lot cleaner than equivalent C/C++ or Rust code but runs just as fast.
Although there are cases where orthogonality isn’t worth it, generally speaking it is better to have a developer tool or API be orthogonal than not.
The reason why is because developers need to be able to piece together all kinds of wild, disparate components in ever-increasingly creative and complicated ways. It’s not uncommon for a modern system to have a big frontend app using abstractions built on top of React, a backend deployed in a cloud that uses several different services in that cloud, a robust IaC system with Terraform or Pulumi to provision things, and other services (such as Observability tools, error tracking tools, or just other B2B service providers), and supporting systems to aid pre-deployment lifecycle concerns that all need to “talk” to one another to a degree.
If you’re working on developer experiences of any sort - anything from a cloud provider to an internal tool for your organization - it is paramount that you consider if what you’re building is orthogonal. If it’s not, what do you benefit from being non-orthogonal? Why does that matter? Should people be able to use your experience in ways you can’t predict up front? Is that fine?
If you’re a developer who is just a user of these tools - demand more! Push vendors who build tools you use to make their experiences more orthogonal. Threaten with your wallet if you have to. We have a long way to go as an industry of developer tools, and your experience matters.