Contents
::: 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:
- Technical Instability: Breaking changes to syntax, tooling, or libraries requiring refactoring.
- 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
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:
- Language Churn: Breaking changes to syntax and semantics. (e.g., Zig’s current state vs. C++’s backward compatibility).
- Ecosystem Churn: Breaking changes to tooling and libraries.
- Complexity Quirks: Corner cases requiring deep expertise to navigate (e.g., C++ ABI issues vs. Go’s simplicity).
- Onboarding Friction: Time required for new contributors to become productive. This is crucial: in large firms with high engineer turnover, a language that requires significant retraining for every new hire acts as a massive maintenance tax.
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.
- Period 1: You must build a Minimum Viable Product (MVP) of size $y_1$.
- Period 2: You maintain and evolve the software to size $y_2$.
- Constraint: You have limited engineering capacity $H$ (human hours available per period).
The core insight is that languages lie on a spectrum. Let’s index them by $\ell \in (0,1)$:
- Productivity increases with $\ell$: High $\ell$ means you build MVPs faster.
- 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.
- Low Capacity: You cannot afford the risk of high churn. You need a safe language where you won’t have to refactor just to survive, even if it takes longer to build. $\to$ C / Bash / Legacy Tech.
- High Capacity: You have enough engineers and buffer that stability is paramount. If you can absorb churn costs easily, why accept the friction of an unstable language when a stable, productive one exists? $\to$ Go / Rust / Modern Java.
- Medium Capacity: This is the “Zig Zone.” You are close to the threshold where you need speed to ship (Productivity) but not rich enough to ignore churn risks. A high-productivity experimental language lets you hit the MVP faster, which is worth paying for in maintenance costs later. $\to$ Zig / Haskell / Julia.
{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.
- Choice: C, Fortran, Bash, Stata.
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.
- Insight: When complexity dominates capacity, even large firms act like low-capacity teams regarding language choice.
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.
- Low Capacity / Low Buffer: Avoid maintenance taxes. Choose boring, ubiquitous tech.
- Medium Capacity: Speed to MVP matters most. “Exciting” languages win here because the opportunity cost of delay outweighs the churn risk.
- 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
- Webkit Source Age: See GitHub Blame Analysis.
- Haskell@Meta: Simon Marlow’s talk on Sigma at Meta regarding language churn and productivity loss for non-Haskellers. Link.
- C++ ABI Stability: Open Standards Committee Papers (e.g., p3466r0).
- JWST JS Choice: Balzano and Isaacs (2006).