Gábor Nyéki

Why Your Favorite Language Might Only Get Adopted by Small Teams

::: tldr For long-lived software, “engineering capacity” determines the best language to use. Engineering capacity is not just money or headcount; it is the buffer an organization has against churn. Low capacity favors low-overhead languages like C or Java. Moderate capacity opens the door to exciting, experimental languages (the “Zig Zone”). At high capacity, the ideal language is productive but has limited overhead (think Go or Rust). :::

::: note On Churn: Stability vs. Friction

In this model, churn ($\delta$) encompasses two things that look the same in an optimization problem:

  1. Technical Instability: Breaking changes to syntax, tooling, or libraries requiring refactoring.
  2. Human Onboarding: The time it takes for new engineers (who join large teams frequently) to learn the language and contribute effectively.

A “high churn” environment means you lose productivity when things break, but it also means you lose productivity every time a new hire joins, because they slow down their colleagues to learn the system. :::

Introduction: The Hidden Age of Software

Most program code running on our computers is years if not decades old. On my desktop, nearly half of all packages had their last upstream version update at least one year before Ubuntu LTS’s feature freeze. About a quarter had it at least two years before.1

1

This doesn’t include Ubuntu’s security updates which don’t normally involve an update to a new upstream version.

{style=“text-align: center” id=“ubuntu_display”} :::

<!--
  Note: Ensure the JS on the site initializes these plots using the IDs provided.
-->
<div><img class="light-mode" width="500" src="hist_package_dates_light.svg" alt="Median upstream version was published 6 months before Ubuntu feature freeze." /></div>
<div><img class="dark-mode" width="500" src="hist_package_dates_dark.svg" alt="Median upstream version was published 6 months before Ubuntu feature freeze." /></div>
<br />
<p style="margin-top: 0; font-size: 80%; font-style: italic;">Median upstream version was published 6 months before Ubuntu feature freeze. 75th-percentile upstream version was published 25 months before.</p>

:::

The other half had a newer upstream release. But the calculation only tells us about the age of the release, not the age of the source code for that release. Of course, most of the source code is older, sometimes by quite a bit.

For a concrete example, here is the age distribution of lines of source code in Apple’s browser engine, WebKit. Half of all lines were last modified more than 4 years ago. A quarter were last modified more than 8 years ago.

{style=“text-align: center” id=“webkit_display”} :::

<div><img class="light-mode" width="500" src="hist_webkit_line_blame_dates_light.svg" alt="Median line of WebKit source code was modified 44 months before release." /></div>
<div><img class="dark-mode" width="500" src="hist_webkit_line_blame_dates_dark.svg" alt="Median line of WebKit source code was modified 44 months before release." /></div>
<br />
<p style="margin-top: 0; font-size: 80%; font-style: italic;">The age distribution of source lines of code in the WebKit repository. Only includes C++ source and header files.</p>

:::

This pattern is not unique to WebKit. Even Ardour, one of the “freshest” projects I’ve looked at, has half of its source code modified more than 2.5 years ago. Each of these projects depends on its old codebase’s maintainability.

What counts as maintainable varies by project, since project resources determine what kinds of maintenance overhead its developers can absorb. A well-resourced (“high-capacity”) project could find a high-churn codebase maintainable in a language that an ill-resourced (“low-capacity”) project wouldn’t.

So how exactly does capacity affect language choice?

The Trade-Off: Productivity vs. Stability

To answer this, we need to distinguish between two forces that govern language choice.

1. Overhead

Maintaining a codebase incurs overheads affected by language choice. High-overhead languages typically feature high complexity or instability in these four areas:

2. Productivity

Productivity ($\varphi$) is the inverse of effort per unit of functionality. Most languages exist to improve on the productivity of their predecessors (Assembly $\to$ C $\to$ High-Level). This is influenced by tooling, expressiveness, and standard library completeness.

The Mathematical Model

I use a two-period optimization model to describe language choice.

The core insight is that languages lie on a spectrum. Let’s index them by $\ell \in (0,1)$:

  1. Productivity increases with $\ell$: High $\ell$ means you build MVPs faster.
  2. Churn rate increases with $\ell$: High $\ell$ means maintenance costs are higher (due to instability + onboarding).

The total usable software over two periods is the objective: $$ \begin{align*} \max_{\ell,h_1,h_2} \quad & y_1 \mathbf{1}{{y_1 \geq \underline{y}}} + y_2 \mathbf{1}{{y_2 \geq \underline{y}}} \ \text{s.t.} \quad & y_1 = \varphi(\ell) h_1 \ & y_2 = (1 - \delta(\ell)) y_1 + \varphi(\ell) h_2 \ & h_1, h_2 \leq H \end{align*} $$ Where $\underline{y}$ is the MVP threshold required to launch. If $y_1 < \underline{y}$, the software has zero value (it never ships).

Why “Medium Capacity” Chooses “Exciting” Languages

The most surprising result comes from the MVP constraint.

{id=“plot_1” style=“text-align: center”} ::: slider_display

<div><svg id="svg_1"></svg></div>
<div>
    <label for="slider_1">Minimum viable product threshold ($\underline{y}$):</label>
    <br/>
    0.00 <input type="range" id="slider_1" min="0.0" max="0.2" step="0.001" value="0.1" style="width: 400px;" /> 0.20
    <br/>
    <button class="play_pause">Play</button>
    <button class="reset">Reset</button>
</div>

:::

The plot shows that as engineering capacity grows, there is a discontinuous jump in usable software at the point where you can finally afford an MVP with a high-churn language.

{id=“plot_2” style=“text-align: center”} ::: slider_display

<div><svg id="svg_2"></svg></div>
<div>
    <label for="slider_2">Engineering capacity (H):</label>
    <br/>
    0.85 <input type="range" id="slider_2" min="0.85" max="1.3" step="0.002" value="1.25" style="width: 400px;" /> 1.30
    <br/>
    <button class="play_pause">Play</button>
    <button class="reset">Reset</button>
</div>

:::

Real-World Applications

Large Firms and Low Effective Capacity

Contrary to popular belief, large firms often have lower effective capacity per engineer for specific projects than expected. Sean Goedecke notes that in big tech, average tenure is short, and compensation structures incentivize churn (vesting periods).

“The longest I have ever stayed on a single team or codebase was three years… Many of the services I work on are a decade old or more… That means many big tech engineers are constantly ‘figuring it out’.” — Sean Goedecke

This turnover acts like high churn overhead. Even with billions in revenue, if half your engineers are learning new languages/contexts every 6 months, you cannot afford an experimental language that requires deep expertise. You stick to boring but ubiquitous languages where hiring is easy (e.g., Java, Go).

Low Capacity: The Solo Developer

Solo developers or small teams often have high effective capacity because they do not suffer from onboarding friction (there are no onboarders). However, their absolute engineering time ($H$) is low. They prioritize stability. If a library updates and breaks your project, you might be the only person fixing it for months.

Complex Domains: The High Bar

Some projects have massive budgets but extremely high complexity (James Webb Space Telescope). In these cases, the MVP requirement is astronomically high. Any overhead becomes a risk that kills the mission. They chose JavaScript for JWST because it was known and stable in 2003, despite being “weak” compared to emerging options.

Stabilizing Languages

As languages mature, the curve shifts. Rust is moving from a “Zig Zone” language (high churn) to a “High Capacity Stable” language. As ecosystems stabilize, their productivity advantage grows without the maintenance tax increasing proportionally. This widens the viable space for new engineers to use these tools safely in production environments.

{id=“plot_3” style=“text-align: center”} ::: slider_display

<div><svg id="svg_3a"></svg><svg id="svg_3b"></svg></div>
<div>
    <label for="slider_3">Stabilization of more productive languages ($p$):</label>
    <br/>
    1 <input type="range" id="slider_3" min="1" max="4" step="0.01" value="2" style="width: 400px;" /> 4
    <br/>
    <button class="play_pause">Play</button>
    <button class="reset">Reset</button>
</div>

:::

As languages stabilize (increasing $p$), the “Zig Zone” widens. Fewer languages require that specific sweet spot of capacity, but those that do allow high-productivity engineering without sacrificing long-term maintainability.

Conclusion

Language choice is an economic calculation, not just a technical one.

  1. Low Capacity / Low Buffer: Avoid maintenance taxes. Choose boring, ubiquitous tech.
  2. Medium Capacity: Speed to MVP matters most. “Exciting” languages win here because the opportunity cost of delay outweighs the churn risk.
  3. High Capacity: Stability matters most. Mature technologies win as you can absorb any overhead.

The surprising prediction is that organizations with the resources to adopt new languages shouldn’t necessarily do so immediately. Until they have high enough capacity to decouple productivity gains from maintenance costs, the “safe” choice remains the most rational one.

Notes & Sources