What developer products can learn from language design
Programming languages and developer products have similar adoption drivers
I learned to program in Java but rarely encountered Java in production codebases. I haven't written Java in years, and neither have the vast majority of young programmers as far as I can tell. I've recently been reflecting on what makes programming languages come and go.
A handful of extrinsic and intrinsic factors in language design make a language popular. These lessons are interesting to examine because they apply more broadly to all developer products.
Power laws govern popularity
Popularity for programming languages follows a power-law distribution. Only a handful of languages reach mainstream adoption. All other languages end up with niche use cases. For example, if you looked at the top 50 languages by pull request on Github in any given quarter, you would notice that the top 10 most popular languages account for over 80% of pull request activity.
Let's dive into some of the factors behind this.
Factors that affect adoption
Simplicity: worse is better
Languages that start small and grow over time tend to outlast languages that attempt to do too much from the start. This is because small languages have a lower barrier to entry. "Small language" is a term I am borrowing from Guy Steele's timeless talk on growing a language. A small language is one where there are fewer concepts to learn. The base primitives are simple and intuitive.
I often think of developers learning a new language as having a specific amount of "RAM" in their mind to devote to the fundamental concepts. The fewer concepts there are to remember, the less you check the docs or StackOverflow for syntax or semantic help.
Low “RAM” simplicity is difficult to achieve. It often looks like doing less instead of more.
Richard Gabriel emphasized this notion of doing less when designing a new language in an essay titled "Worse is Better." He argues that simplicity should take precedence over other language features such as correctness or completeness. Simplicity, he claims, is why C spread much faster than Lisp. C was easier to pick up because it had fewer concepts to master relative to other languages at the time. Simplicity is simply — pun intended — a better bet for virality.
That said, if a language is too small to be useful, it will not get very far. This brings me to the final driver of language adoption.
Composability: creating new vocabulary
Simplicity is necessary for adoption, but it is not sufficient for a mainstream language that endures. Programming languages need to evolve with users' needs, and composability is the key to that evolution.
Composability makes it easy to define new language primitives and abstractions. This is the holy grail in language design because it empowers users to mold the language and expand its capabilities. Creating new abstractions is how languages grow their TAM and reach more developers.
We can think about language growth along two axes 1) new feature direction: open-sourced versus centralized 2) how easy it is to add new primitives that feel native. Native here means that there are no syntactic differences between using a module you’ve created versus using a built-in module.
There aren’t many surviving languages today that don’t have an easy way to publish new modules. One that comes to mind is APL — referenced in Guy Steele’s formulation of the above framework. APL was a promising language based on the premise that applying functions to arrays gave you a cleaner shorter syntax. However, APL struggled to gain widespread adoption mostly because its design was the brainchild of one person and it was challenging to extend via modules.
Final thoughts: what this means for you if you're building for developers
In my day-to-day, I spend a lot of time thinking about what makes for a great developer product and, consequently, a good investment. It turns out that a good developer product has a lot in common with a good programming language. This makes sense when you think of programming languages as the ultimate developer tool. So if you're building for developers, the shared drivers of adoption between programming languages and developer tools are worth paying attention to. I have written about some of these before. I'll recap a few key points here.
Adoption is a function of extrinsic factors and intrinsic factors. Platform shifts are an extrinsic factor. You don't control them. The growth of your developer product is a function of the growth of the underlying platform. What matters is aligning your value prop and messaging to emerging user needs on the new platform.
Extrinsic factors are only a part of the equation and apply to all products in a category. Developer products that go mainstream have superior intrinsic qualities.
Simplicity and composability are the two intrinsic qualities that matter most. They are a function of product/API design and should expand the skill range of developers who can use your product. The wider the skill range, the larger the TAM and the greater the odds of mass adoption.
The idea of a skill range for programmers is worth dwelling on, particularly regarding simplicity and composability. Skill range is a concept borrowed from gaming. It has two essential elements 1) the skill floor: how easy is it for new users to get started 2) the skill ceiling: how complex a task a user can accomplish with your product. Simplicity lowers the skill floor for any developer to get started with your product. Composability increases the skill ceiling for developers to build more complex abstractions on top of your product.
In summary, winning broad adoption for a new developer product is a race to expand the skill range of your audience while riding a new platform wave.
Of course, this is all easier said than done. If you're building for developers and are thinking through adoption, I would love to hear from you.
: I still recommend Java as a great intro language. The core OOP principles create a good structure for picking up useful concepts that show up in other languages.
: One of the best articulations of this concept I have come across is Joel Quenneville’s reflection on who is empowered by proposed changes to the F# language.