<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Mara Averick</title>
    <description>The latest articles on DEV Community by Mara Averick (@dataandme).</description>
    <link>https://dev.to/dataandme</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F858%2F8ed854c1-3e73-4d1d-b25f-74aea30e669e.png</url>
      <title>DEV Community: Mara Averick</title>
      <link>https://dev.to/dataandme</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/dataandme"/>
    <language>en</language>
    <item>
      <title>The 2026 stdlib community survey is open</title>
      <dc:creator>Mara Averick</dc:creator>
      <pubDate>Fri, 11 Sep 2026 13:30:13 +0000</pubDate>
      <link>https://dev.to/stdlib/the-2026-stdlib-community-survey-is-open-13ip</link>
      <guid>https://dev.to/stdlib/the-2026-stdlib-community-survey-is-open-13ip</guid>
      <description>&lt;p&gt;We closed our &lt;a href="https://blog.stdlib.io/lessons-learned-100-interviews-about-numerical-javascript/" rel="noopener noreferrer"&gt;last post&lt;/a&gt;, on a hundred interviews, by admitting that "a project's confidence about its users and a project's evidence about its users may be close to unrelated quantities"—so rather than keep guessing, we're asking. The 2026 &lt;a href="https://stdlib.io/survey" rel="noopener noreferrer"&gt;stdlib community survey&lt;/a&gt; is now open.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/stdlib-js/stdlib" rel="noopener noreferrer"&gt;stdlib&lt;/a&gt; is a standard library for JavaScript and Node.js, with an emphasis on numerical and scientific computation, and this survey is part of a National Science Foundation award supporting a sustainable scientific computing ecosystem on the web.&lt;/p&gt;

&lt;p&gt;What would have to be true—or visible—for you to reach for an existing numerical routine in JavaScript, instead of writing it yourself or leaving for another language?&lt;/p&gt;

&lt;p&gt;So we're asking how you decide if you can trust the numbers a library gives you, whether you've ever had numerical code return an answer that looked plausible and turned out to be wrong, what you did the last time you needed a numerical routine and couldn't find one, and how you check numerical code an AI assistant wrote for you. Short answers are fine, and "I don't know" is real data.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;You don't need to have used stdlib to answer.&lt;/strong&gt; If you've never heard of it, you're exactly the person we're missing. If you tried numerical work in JavaScript and gave up on it, you're the person we can least afford not to hear from. And if you build or maintain numerical libraries and tools of your own, answer as a builder, not only as a user.&lt;/p&gt;

&lt;p&gt;We kept it short on purpose. Five to ten minutes; all but the first question are optional.&lt;/p&gt;

&lt;p&gt;Your answers help shape what we build and document next. We won't publish anything that identifies you, and we won't quote you without asking first. The last question asks for an email address only if you're open to a follow-up conversation—it's optional, we'll use it only to contact you about this work, and leaving it blank changes nothing else.&lt;/p&gt;

&lt;p&gt;This isn't a signup for anything. It ends when you submit it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://stdlib.io/survey" rel="noopener noreferrer"&gt;Take the survey&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;
    &lt;em&gt;Mara Averick is a developer advocate at &lt;a href="https://quansight.com/" rel="noopener noreferrer"&gt;Quansight&lt;/a&gt; and contributor experience lead for &lt;a href="https://github.com/stdlib-js/stdlib" rel="noopener noreferrer"&gt;stdlib&lt;/a&gt;.&lt;/em&gt;
&lt;/p&gt;




&lt;p&gt;&lt;a href="https://github.com/stdlib-js/stdlib" rel="noopener noreferrer"&gt;stdlib&lt;/a&gt; is an open source software project dedicated to providing a comprehensive suite of robust, high-performance libraries to accelerate your project's development and give you peace of mind knowing that you're depending on expertly crafted, high-quality software.&lt;/p&gt;

&lt;p&gt;If you've enjoyed this post, give us a star 🌟 on &lt;a href="https://github.com/stdlib-js/stdlib" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; and consider &lt;a href="https://opencollective.com/stdlib" rel="noopener noreferrer"&gt;supporting&lt;/a&gt; the project. Your contributions and continued support help ensure the project's long-term success and are greatly appreciated!&lt;/p&gt;

&lt;h2&gt;
  
  
  Acknowledgments
&lt;/h2&gt;

&lt;p&gt;This work was supported in part by the National Science Foundation under &lt;a href="https://www.nsf.gov/awardsearch/showAward?AWD_ID=2449410&amp;amp;HistoricalAwards=false" rel="noopener noreferrer"&gt;Award No. 2449410&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Disclaimer: Any opinions, findings and conclusions or recommendations expressed in this material are those of the author(s) and do not necessarily reflect the views of the National Science Foundation.&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>opensource</category>
      <category>devrel</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Lessons learned: 100 interviews about numerical JavaScript</title>
      <dc:creator>Mara Averick</dc:creator>
      <pubDate>Thu, 03 Sep 2026 13:09:31 +0000</pubDate>
      <link>https://dev.to/stdlib/lessons-learned-100-interviews-about-numerical-javascript-33ln</link>
      <guid>https://dev.to/stdlib/lessons-learned-100-interviews-about-numerical-javascript-33ln</guid>
      <description>&lt;p&gt;We ran a hundred customer-discovery interviews in seven weeks earlier this year, reaching out to anyone we could think of who might have something to say about high-performance numerical and scientific computation in JavaScript (which is the thing stdlib exists to do), as well as those with knowledge about sustaining open-source ecosystems. We also searched the Journal of Open Source Software (JOSS) for work involving JavaScript—a remarkably short list—and wrote to the people behind the papers.&lt;/p&gt;

&lt;p&gt;So, the sample is what two people's address books could reach, plus what one literature search could find. Our networks largely held people we already knew, which shaped the corpus.&lt;/p&gt;

&lt;p&gt;Projects overestimate how well they know their users. It's in the corpus over and over—maintainers confident about what users need, users describing something adjacent but not the same when you ask them directly with no maintainer in the room. Which is not a maintainer failing, or a developer failing. Expertise and certainty aren't correlated the way we'd like them to be, and Tavris and Aronson have the bleak version of it in &lt;em&gt;Mistakes Were Made (But Not by Me)&lt;/em&gt;: "training does not increase accuracy; it increases people's confidence in their accuracy."&lt;sup id="fnref1"&gt;1&lt;/sup&gt; That's also how a wrong assumption persists—nothing in the loop is set up to catch it, so it just keeps being the thing everyone knows. We ran the interviews, found the pattern, and still catch ourselves in it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What we did, and what the numbers can't say
&lt;/h2&gt;

&lt;p&gt;The interviews were the "customer-discovery requirement" of the &lt;a href="https://new.nsf.gov/funding/initiatives/i-corps" rel="noopener noreferrer"&gt;NSF I-Corps&lt;/a&gt; program, which stdlib went through as part of its &lt;a href="https://new.nsf.gov/funding/opportunities/pose-pathways-enable-open-source-ecosystems" rel="noopener noreferrer"&gt;POSE&lt;/a&gt; award. The count (100) and the timeline (seven weeks) came with it. Two of us doing them.&lt;/p&gt;

&lt;p&gt;Recruiting a hundred people that fast doesn't leave much room for a sampling strategy, and two of our three channels were just us. My side of that reach was R and data visualization; Athan's was scientific Python and numerical JavaScript.&lt;/p&gt;

&lt;p&gt;The third channel wasn't a network. To find academics actually using JavaScript in a lab, we used a mix of GitHub repo surveying and &lt;a href="https://joss.theoj.org/papers/in/JavaScript" rel="noopener noreferrer"&gt;JOSS as an index—papers citing JavaScript&lt;/a&gt;—and cold-emailed the authors we found that way. Most had no idea who either of us were, but were generous enough to give us their time.&lt;/p&gt;

&lt;p&gt;The interviews were also confidential, which is a routine research condition rather than a dramatic one. A &lt;a href="https://forms.gle/nZRnmqQUPGvzpVq17" rel="noopener noreferrer"&gt;brief survey&lt;/a&gt; runs alongside this, because the gap in the corpus is a specific gap and the people who could close it are, by definition, not in it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Four major themes
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. The ecosystem doesn't answer &lt;em&gt;is this right&lt;/em&gt;
&lt;/h3&gt;

&lt;p&gt;People arriving at JavaScript from R or Python don't say &lt;em&gt;I can't do the thing I want to do in this language.&lt;/em&gt; They say &lt;em&gt;I can't tell whether this is right.&lt;/em&gt; The problem they're describing isn't capability, it's epistemic: there's no obvious mechanism in the JavaScript ecosystem for distinguishing a numerically sound implementation from one that merely runs and returns a number of roughly the right magnitude.&lt;sup id="fnref2"&gt;2&lt;/sup&gt;&lt;/p&gt;

&lt;p&gt;Several people named this before we'd asked anything about stdlib, which is what makes it their framing rather than ours. The named counterexample, repeatedly, was CRAN—the &lt;a href="https://cran.r-project.org/" rel="noopener noreferrer"&gt;Comprehensive R Archive Network&lt;/a&gt; that runs checks before a package is listed, and where the listing itself carries information.&lt;sup id="fnref3"&gt;3&lt;/sup&gt; In the eyes of those used to the CRAN model, &lt;a href="https://www.npmjs.com/" rel="noopener noreferrer"&gt;npm&lt;/a&gt; (the predominant package manager for Node.js) appears to be an open field of sorts. Things grow in it. Nobody inspects them.&lt;/p&gt;

&lt;p&gt;So people reach for the proxies that exist. Stars. Weekly installs. Download counts. At least one person described using install counts as a quality signal while being entirely clear-eyed that it isn't one, because no signal of statistical validity is on offer and something has to bear the load. Popularity is what you use when correctness isn't measurable from the outside.&lt;/p&gt;

&lt;p&gt;A large share of the people opening pull requests against stdlib aren't using it in their own work—filling in the ecosystem canvas is where we had to write that down.&lt;sup id="fnref4"&gt;4&lt;/sup&gt; And the actual grounds for trusting the library are the reference implementations the tests check against, the fixtures generated from them, the benchmarks. All of it stays invisible to the people standing closest to it. Those grounds are inspectable, which is what open source has instead of &lt;em&gt;trust us&lt;/em&gt;; being able to read the source is how trust here gets earned rather than granted. Reading it is also a cost, unevenly payable, which is most of what documentation conventions exist to lower. What people have in the meantime is what they've been able to watch: how a maintainer handles a bug report, what a review looks like, behavior over time. That's real evidence and it's rational to use. It just isn't transferable—you can't hand it to your team lead, and sooner or later you'll be asked to.&lt;/p&gt;

&lt;p&gt;A project looking at its own trusting users is looking at the wrong evidence. What it sees is people relying on it. What it tends to read that as is &lt;em&gt;our quality is legible from the outside&lt;/em&gt;. What it's often actually seeing is the people who got close enough to watch it work. And the people for whom the quality wasn't legible didn't file an issue saying so—they used something else, or went back to R, and the project never saw them at all.&lt;/p&gt;

&lt;p&gt;More than one person described building a small solo tool that achieved unexpectedly large downstream use with essentially no feedback reaching them. That's silent adoption, and stars and downloads can't detect it at all—they count use, not whether any of it ever gets back to the person who built the thing.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Trust us, we're careful&lt;/em&gt; asks for exactly the thing the finding says is unavailable. It also misplaces the work: robustness and reliability aren't a claim you make &lt;strong&gt;about&lt;/strong&gt; the product, they're a key part &lt;strong&gt;of&lt;/strong&gt; the product. So the job isn't to ask anyone for trust—it's to show that what we do lines up with the things they already trust.&lt;/p&gt;

&lt;p&gt;stdlib already has the material for that, built deliberately and for engineering reasons—to know the implementations are correct—rather than to demonstrate anything to anyone. The expected values in the special-function test fixtures aren't hand-written, and they aren't captured from stdlib's own output—they're generated by external reference implementations and committed next to the tests with the generator script included. Close to two hundred packages check against Julia; the rest mostly against NumPy and SciPy, Boost.Math, and R.&lt;sup id="fnref5"&gt;5&lt;/sup&gt; You don't have to believe us about any of that. You can check that we match what you'd have used anyway.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. AI is the densest theme, and it doesn't converge
&lt;/h3&gt;

&lt;p&gt;Roughly thirty of the interviews touch AI-assisted contribution.&lt;/p&gt;

&lt;p&gt;We went in with a prior, and the prior was wrong. We expected sentiment to sort by seniority—junior contributors enthusiastic, senior maintainers alarmed, some tidy generational story. It doesn't. Senior maintainers of substantial projects show up alarmed and enthusiastic in roughly equal measure, and neither that story nor its inverse holds.&lt;/p&gt;

&lt;p&gt;Across people who disagree about everything else here there was one near-universal claim: AI has lowered the cost of submitting a contribution much further than it's lowered the cost of evaluating one. That asymmetry is the one thing nearly everyone sees. What they profoundly disagree about is whether it's a crisis, a nuisance to route around, or a net win.&lt;/p&gt;

&lt;p&gt;Three registers are clear in the corpus, and they're different arguments:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;There's alarm about &lt;strong&gt;quality&lt;/strong&gt;—volume up, signal down, review time is finite.&lt;/li&gt;
&lt;li&gt;There's alarm reframed as &lt;strong&gt;security&lt;/strong&gt;, which is a different claim entirely: a contribution pipeline without process isn't just noisy, it's a risk surface, and treating it as a code-quality problem understates it.&lt;/li&gt;
&lt;li&gt;And there's a rarer &lt;strong&gt;fatalist&lt;/strong&gt; register that doesn't really argue—the technology arrived, it's here, and whether it should be isn't a live question.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;A project's posture with respect to newcomers, described in &lt;a href="https://blog.stdlib.io/do-you-want-contributors/" rel="noopener noreferrer"&gt;Do you WANT contributors?&lt;/a&gt;, is a separate matter from the register in which it lands.&lt;/p&gt;

&lt;p&gt;There may be a fourth: that code review was never only quality control, it was how a project handed its judgment to a new person, and something that produces acceptable diffs without that exchange has broken the part that mattered even when the diff is fine.&lt;/p&gt;

&lt;p&gt;Two positions invert the dominant frame entirely. One imagines AI as the &lt;em&gt;filter&lt;/em&gt;—the triage layer that protects a maintainer's attention rather than the flood that overwhelms it. The other argues that a project should deliberately optimize its own legibility to models as a discoverability strategy, on the reasoning that training cutoffs are why newer libraries are structurally invisible to the tool an increasing number of people ask first.&lt;/p&gt;

&lt;p&gt;Sentiment may sort instead by which side of the submit/review transaction someone spends their day on; that hasn't been checked against every interview that touches AI (and may have legitimately changed over the subsequent months).&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Who showed up, and what they came from
&lt;/h3&gt;

&lt;p&gt;Twenty-six of the hundred people we interviewed had contributed to stdlib. Seventy-four hadn't. That split matters because it means this isn't contributors imagining what users want. It's users, or prospective users.&lt;/p&gt;

&lt;p&gt;Within it, one relationship is about as strong as a hundred conversations can show: &lt;strong&gt;whether someone has contributed to stdlib predicts what language they compute in.&lt;/strong&gt; The contributor group is JavaScript- and C/C++-native at roughly three-quarters. Not one of the twenty-six contributors arrives primarily from Python. Exactly one arrives primarily from R, and that applied-statistics-to-Node path is the origin story of stdlib's own precursor project—which isn't evidence that R people find their way to stdlib, it's evidence that one of them founded it. Meanwhile, half the non-contributor group is Python- or R-primary.&lt;/p&gt;

&lt;p&gt;Both groups came out of the same biased pool, which is what makes the comparison usable. Whatever our networks did to the absolute numbers, they did to both columns—so the &lt;em&gt;difference&lt;/em&gt; between them survives the sampling problem even though neither column on its own does.&lt;/p&gt;

&lt;p&gt;Two negative findings sat alongside it, and we predicted neither. &lt;strong&gt;MATLAB is primary for nobody.&lt;/strong&gt; Two people name it as a language they left. &lt;strong&gt;Julia is primary for nobody either&lt;/strong&gt;, showing up only as a reference implementation people consult when they want to check what a correct answer looks like. Both of those were carrying weight in our persona assumptions, and neither earned it.&lt;/p&gt;

&lt;p&gt;The obvious story to tell here is that stdlib's written material addresses JavaScript developers, its talks address scientists arriving from Python and R, and the contributor population is what happened. The data is consistent with that. It doesn't establish it, and there's a sufficient alternative explanation sitting right there: seventeen of the twenty-six contributors arrived by way of Google Summer of Code (GSoC)—some in the program, others contributing in the hope of getting into it—and either way the sorting is by project.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. The most valuable parts don't scale
&lt;/h3&gt;

&lt;p&gt;Ask people what made their contributor experience valuable and the same answer comes back again and again: a specific human paid individualized attention to them. Eighteen interviews say so, which makes it consistent rather than dominant—onboarding generally turns up in roughly twice as many. &lt;a href="https://blog.stdlib.io/the-stakeholder-journey/" rel="noopener noreferrer"&gt;The Stakeholder Journey&lt;/a&gt; is its own subject; among the maintainers and program managers here who took the question up, it was near-unanimous that individualized attention doesn't scale, and nobody claimed to have solved it. That's a real tradeoff, not a tooling gap. Three things came up reliably about why.&lt;/p&gt;

&lt;p&gt;First, the good-first-issue pipeline isn't the solved problem it looks like from outside: one large project reports that its organic supply of genuine beginner-appropriate issues &lt;em&gt;ran out&lt;/em&gt;, and that beginner tasks now have to be manufactured. That complicates any assumption that onboarding gets easier as a project matures.&lt;/p&gt;

&lt;p&gt;Second, mentorship labor includes a category that no onboarding framing counts. Sustaining the motivation of a contributor who has lost interest isn't review and it isn't documentation, and it doesn't appear on any dashboard. It's emotional work, and the one person who named it described it as the biggest stumbling block they had.&lt;/p&gt;

&lt;p&gt;Third, an actively evolving codebase imposes a contributor cost that maintainers don't always register. Conventions shift; the shift is rarely documented as a shift. This showed up independently from more than one contributor inside the same project, which is about as clean a demonstration of the visibility asymmetry as we got.&lt;/p&gt;

&lt;p&gt;Onboarding and mentorship read as big themes partly by construction. GSoC is a mentorship-heavy program, and it brought in a substantial part of the contributor group. The theme is real. Its rank is an artifact of who we talked to.&lt;/p&gt;

&lt;h2&gt;
  
  
  What we still can't answer
&lt;/h2&gt;

&lt;p&gt;We don't know whether JavaScript developers actually have the ndarray comprehension gap we've assumed they have. Our own framing has been that people arriving from JavaScript need &lt;em&gt;what is an ndarray&lt;/em&gt; explained while people arriving from Python need the subtleties. Half of that has evidence. On the other half, all hundred interviews say nothing at all—not a little, nothing. That could mean the people who bounced off it were never reachable by this recruitment channel, or it could mean it was a maintainer's assumption about users that we've been carrying as a fact. This corpus can't separate those.&lt;/p&gt;

&lt;p&gt;There's a lot we didn't reliably ask. Which programming language someone had come from is one instance: twelve of the hundred can't be classified on the axis that produced one of our strongest findings. Migration friction is another: it came up as the two-language problem—one language, namely Python or R, for analysis, and JavaScript for visualization—not in a form we could count.&lt;/p&gt;

&lt;p&gt;And then there's a limit on the whole corpus that no amount of re-reading fixes, and this one isn't about our sample: it's about &lt;em&gt;what asking does&lt;/em&gt;. Nobody was hiding anything—people said plainly when they weren't sure, and more than one of them worked out mid-conversation that shorthand was all they had. That's the thing. A question hands you a slot shaped like a position, and what's available to fill it is shorthand, because that's what anybody can actually retrieve about a choice like this.&lt;/p&gt;

&lt;p&gt;The summary here runs the operation a second time. A hundred conversations became four themes, and a theme is a position that's been made comparable to other positions—which is why it can be summarized, and it's paid for out of exactly the particularity that made any one answer worth having. So the sections above are the shape of what a hundred people say when they're asked. That's a real thing to know. It isn't the shape of what they know.&lt;/p&gt;

&lt;p&gt;A project's confidence about its users and a project's evidence about its users may be close to unrelated quantities. Trust is where that gap opens widest. The trust a project can see is the trust it earned in view of people close enough to watch, and everyone else's assessment happens somewhere the project can't reliably see. The distrust never shows up at all—it looks like somebody quietly using something else.&lt;/p&gt;

&lt;p&gt;There's a &lt;a href="https://stdlib.io/survey" rel="noopener noreferrer"&gt;short survey&lt;/a&gt; (11 questions). If the language you compute in, your discipline, or your path to numerical computing on the web isn't represented above, that's the hole.&lt;/p&gt;




&lt;p&gt;
    &lt;em&gt;Mara Averick is a developer advocate at &lt;a href="https://quansight.com/" rel="noopener noreferrer"&gt;Quansight&lt;/a&gt; and contributor experience lead for &lt;a href="https://github.com/stdlib-js/stdlib" rel="noopener noreferrer"&gt;stdlib&lt;/a&gt;.&lt;/em&gt;
&lt;/p&gt;




&lt;p&gt;&lt;a href="https://github.com/stdlib-js/stdlib" rel="noopener noreferrer"&gt;stdlib&lt;/a&gt; is an open source software project dedicated to providing a comprehensive suite of robust, high-performance libraries to accelerate your project's development and give you peace of mind knowing that you're depending on expertly crafted, high-quality software.&lt;/p&gt;

&lt;p&gt;If you've enjoyed this post, give us a star 🌟 on &lt;a href="https://github.com/stdlib-js/stdlib" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; and consider &lt;a href="https://opencollective.com/stdlib" rel="noopener noreferrer"&gt;supporting&lt;/a&gt; the project. Your contributions and continued support help ensure the project's long-term success and are greatly appreciated!&lt;/p&gt;

&lt;h2&gt;
  
  
  Acknowledgments
&lt;/h2&gt;

&lt;p&gt;This work was supported in part by the National Science Foundation under &lt;a href="https://www.nsf.gov/awardsearch/showAward?AWD_ID=2449410&amp;amp;HistoricalAwards=false" rel="noopener noreferrer"&gt;Award No. 2449410&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Disclaimer: Any opinions, findings and conclusions or recommendations expressed in this material are those of the author(s) and do not necessarily reflect the views of the National Science Foundation.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;ol&gt;

&lt;li id="fn1"&gt;
&lt;p&gt;Carol Tavris and Elliot Aronson. &lt;em&gt;Mistakes Were Made (But Not by Me): Why We Justify Foolish Beliefs, Bad Decisions, and Hurtful Acts.&lt;/em&gt; Third edition, Houghton Mifflin Harcourt, 2020, chapter 5, "Law and Disorder." ISBN 978-0-358-32961-9.&amp;nbsp;↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn2"&gt;
&lt;p&gt;Of course, as with all of open source, you could always read the source code. But, in practice, this is least likely to be helpful for those used to doing their numeric and scientific analysis in other languages.&amp;nbsp;↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn3"&gt;
&lt;p&gt;CRAN Repository Policy. &lt;a href="https://cran.r-project.org/web/packages/policies.html" rel="noopener noreferrer"&gt;https://cran.r-project.org/web/packages/policies.html&lt;/a&gt;. Packages must pass &lt;code&gt;R CMD check&lt;/code&gt; on the maintained platforms before and after being listed, which is the mechanism the interviewees were reaching for—not the curation &lt;em&gt;judgment&lt;/em&gt;, but the existence of any automated gate at all.&amp;nbsp;↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn4"&gt;
&lt;p&gt;"The Open-Source Ecosystem Canvas." &lt;a href="https://blog.stdlib.io/open-source-ecosystem-canvas/" rel="noopener noreferrer"&gt;https://blog.stdlib.io/open-source-ecosystem-canvas/&lt;/a&gt;. The Community Members cell records the phrase the canvas forced into the open—&lt;em&gt;usually non-users&lt;/em&gt;—next to casual contributors.&amp;nbsp;↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn5"&gt;
&lt;p&gt;Counts of &lt;code&gt;test/fixtures/&amp;lt;generator&amp;gt;/&lt;/code&gt; directories under &lt;code&gt;@stdlib/math/base/special&lt;/code&gt;, taken against the &lt;code&gt;develop&lt;/code&gt; branch on 2026-08-26: Julia 198, Python 23, C++ 20, R 11, C 3, Wolfram 2. Each generator is a script calling an established library in that language, committed alongside the fixtures it produced.&amp;nbsp;↩&lt;/p&gt;
&lt;/li&gt;

&lt;/ol&gt;

</description>
      <category>devrel</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Write It Like C: What V8's Optimizing Compilers Explain About the Zen of stdlib</title>
      <dc:creator>Mara Averick</dc:creator>
      <pubDate>Mon, 24 Aug 2026 14:15:34 +0000</pubDate>
      <link>https://dev.to/stdlib/write-it-like-c-what-v8s-optimizing-compilers-explain-about-the-zen-of-stdlib-2hn7</link>
      <guid>https://dev.to/stdlib/write-it-like-c-what-v8s-optimizing-compilers-explain-about-the-zen-of-stdlib-2hn7</guid>
      <description>&lt;p&gt;JavaScript wasn't designed for numerical computing. There's one number type (a 64-bit float), no operator overloading, no control over memory layout, and a value model where every integer is, in principle, an object. And yet, on a modern V8, a tight loop over a &lt;code&gt;Float64Array&lt;/code&gt; lands within a small constant factor of equivalent C—sometimes at basically the same speed. That's not an accident, and it's not free. It's the cumulative result of fifteen years of compiler engineering specifically aimed at &lt;em&gt;recognizing&lt;/em&gt;, at runtime, that the dynamic-language source you wrote is in fact behaving like a statically-typed numerical kernel—and rewriting it as one.&lt;/p&gt;

&lt;p&gt;If your first association with "V8" is horsepower and max towing capacity, &lt;a href="https://blog.stdlib.io/zen-of-stdlib/" rel="noopener noreferrer"&gt;the Zen of stdlib&lt;/a&gt; is going to read as an elegant list of design principles you're supposed to memorize. And it kind of is one—about two dozen entries. Some are about people (&lt;em&gt;code is read more than it is written&lt;/em&gt;, &lt;em&gt;be kind to your future self&lt;/em&gt;, &lt;em&gt;tend to the garden&lt;/em&gt;), some are general engineering taste (&lt;em&gt;don't be clever&lt;/em&gt;, &lt;em&gt;complexity kills&lt;/em&gt;, &lt;em&gt;simple is beautiful&lt;/em&gt;). Two are unmistakably about machines: &lt;em&gt;write it like C&lt;/em&gt;, and &lt;em&gt;avoid polymorphism by default&lt;/em&gt;, which the long form spells out as "monomorphic is best, polymorphic is not great, megamorphic is terrible." Those last three terms are counting how many different kinds of thing one spot in your code has had to handle: one, a handful, or more than the engine will keep track of.&lt;/p&gt;

&lt;p&gt;What I didn't expect is how many of those middle entries are also about a machine. &lt;em&gt;Don't be clever&lt;/em&gt; means something specific once you know what happens when you try to outsmart a speculative optimizer. Same with &lt;em&gt;mistakes are infectious&lt;/em&gt;, &lt;em&gt;fix them early&lt;/em&gt;, and &lt;em&gt;stability is a feature&lt;/em&gt;. A lot of what looks like taste turns out to be doing real work.&lt;/p&gt;

&lt;p&gt;I'm not a compiler engineer, I've never written a numerical kernel, and I don't write C. My job is making stdlib's conventions legible to people arriving at the codebase, and I couldn't explain half of these without knowing what V8 actually does with a loop—so I went and found out. The useful discovery, for anyone else who doesn't write C either: &lt;em&gt;write it like C&lt;/em&gt; asks for less knowledge of C than you'd think. What it actually asks for is knowing what the optimizer is trying to prove. What follows is the map I wanted when I started, assembled from the V8 team's writing about their own pipeline and from asking stdlib maintainers why the code looks the way it does. You don't need to have gone spelunking inside V8 for it to make sense—I hadn't when I started.&lt;/p&gt;

&lt;p&gt;The part that surprised me most: the rules have barely moved in a decade, while the machinery enforcing them has been rebuilt three or four times. That's why they're worth internalizing rather than looking up.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;On other engines:&lt;/strong&gt; this post is about V8. JavaScriptCore in Safari and SpiderMonkey in Firefox ship their own compiler architectures, and circle many of the same principles by different routes. V8 gets the attention here because it runs Chrome &lt;em&gt;and&lt;/em&gt; it's the runtime under Node.js and Deno, so most stdlib code ends up executing on it. Where I could, I've stuck to general principles rather than V8-specific quirks, since those are the parts likely to travel.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  A note on vocabulary
&lt;/h2&gt;

&lt;p&gt;This post throws around terminology—hidden classes, shapes, inline caches, elements kinds, deoptimization—and uses some of it before explaining it. Don't worry—the explanations arrive later, where the terms are doing more work. If you'd rather have a running start, two pieces cover most of it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Mathias Bynens — &lt;a href="https://mathiasbynens.be/notes/shapes-ics" rel="noopener noreferrer"&gt;JavaScript engine fundamentals: Shapes and Inline Caches&lt;/a&gt; — hidden classes and inline caches, which is most of the vocabulary here; and&lt;/li&gt;
&lt;li&gt;Mathias Bynens — &lt;a href="https://v8.dev/blog/elements-kinds" rel="noopener noreferrer"&gt;Elements kinds in V8&lt;/a&gt; — how V8 tracks what is inside an array.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Feeling a little out of your depth here is fine—I &lt;em&gt;definitely&lt;/em&gt; did.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why a numerical JavaScript lens?
&lt;/h2&gt;

&lt;p&gt;Most V8-pipeline explainers target general-purpose JS frameworks, page-load metrics, or web-app responsiveness. The optimizations they highlight absolutely apply to numerical code—function inlining, hidden-class stability, megamorphic call sites all matter here. They just aren't the &lt;em&gt;whole&lt;/em&gt; story for a &lt;code&gt;Float64Array&lt;/code&gt;-bound inner loop.&lt;/p&gt;

&lt;p&gt;Keep a loop like this one in mind:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;scale&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;out&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;out&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;2.0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nothing about it is clever (which is the point). Assume &lt;code&gt;x&lt;/code&gt; and &lt;code&gt;out&lt;/code&gt; are &lt;code&gt;Float64Array&lt;/code&gt;s and that &lt;code&gt;scale&lt;/code&gt; gets called thousands of times.&lt;/p&gt;

&lt;p&gt;Four of the engine's questions matter much more for numerical work than they do for typical app code, and all four are about that loop. Each names something the optimizer is trying to prove, and each returns further down with the mechanism attached—so this is a map to come back to, not something to hold in your head now.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Boxing and unboxing.&lt;/strong&gt; Does &lt;code&gt;x[ i ]&lt;/code&gt; reach the multiply as a raw 64-bit float in a CPU register, or as a heap-allocated wrapper the engine reads through on every operation? Numerical code multiplies that difference by a million.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Elements kind.&lt;/strong&gt; Is &lt;code&gt;x&lt;/code&gt;'s backing store (the memory the elements actually live in) a contiguous block of doubles, or a tagged array of generic JS values, where every slot has to carry its own type? V8 tracks this per array, and the loop reads through that store on every iteration.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deoptimization.&lt;/strong&gt; The optimizer bets that &lt;code&gt;scale&lt;/code&gt; only ever sees &lt;code&gt;Float64Array&lt;/code&gt;. What happens on the call that breaks the bet—when V8 has to throw the specialized code away? That bet gets tested constantly, because one kernel gets reused against different inputs all day long.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tiering.&lt;/strong&gt; V8 has four compilers rather than one, and they trade compile time against code quality. Does &lt;code&gt;scale&lt;/code&gt; want the last cycle squeezed out of peak code, or to &lt;em&gt;reach&lt;/em&gt; peak code fast enough that a Node service or notebook session spends most of its time there? That second case is the common one for numerical code.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Most of V8's recent architectural work comes back to one or another of these four: Sparkplug, Maglev, the Turboshaft intermediate representation (IR), mutable heap numbers.&lt;/p&gt;

&lt;h2&gt;
  
  
  A 15-year ladder
&lt;/h2&gt;

&lt;p&gt;Today's V8 has four JavaScript execution tiers. It did not start there. Each addition responds to a specific gap left by the previous tier.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fi7az5pi1ts05vz5pfohu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fi7az5pi1ts05vz5pfohu.png" alt="Five snapshots of V8's execution pipeline, one per era. December 2010: Full-codegen feeding Crankshaft, two tiers. May 2017 (V8 v5.9): Ignition feeding TurboFan, two tiers, both new. 2021 (V8 v9.1): Sparkplug inserted between them, three tiers. 2023 (Chrome M117): Maglev inserted before TurboFan, four tiers. March 2025: the same four tiers, with TurboFan's internals rebuilt from sea-of-nodes onto Turboshaft—the same tier rebuilt inside, not a fifth tier." width="800" height="721"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Crankshaft (2010–2017)
&lt;/h3&gt;

&lt;p&gt;In December 2010, Google &lt;a href="https://blog.chromium.org/2010/12/new-crankshaft-for-v8.html" rel="noopener noreferrer"&gt;shipped Crankshaft&lt;/a&gt;, V8's first speculative optimizing compiler. Crankshaft was layered &lt;em&gt;above&lt;/em&gt; V8's existing baseline compiler, &lt;em&gt;Full-codegen&lt;/em&gt;: cold code ran there, and once a function got hot, V8 promoted it to Crankshaft for re-compilation against type assumptions. Crankshaft used two intermediate representations: a high-level static-single-assignment (SSA) graph called &lt;em&gt;Hydrogen&lt;/em&gt;, and a lower-level form called &lt;em&gt;Lithium&lt;/em&gt;. It leaned heavily on inline-cache feedback collected by the running baseline code.&lt;/p&gt;

&lt;p&gt;Crankshaft is the V8 that stdlib-shaped JavaScript was first written against. Its rules still apply today—monomorphic call sites, stable hidden classes, no megamorphism, no surprise types. They were never about Crankshaft specifically; they described the &lt;em&gt;underlying contract&lt;/em&gt; between dynamic source and a speculative optimizer. One of those terms is worth unpacking now: a &lt;em&gt;hidden class&lt;/em&gt; is V8's internal record of an object's layout—which properties it has, in what order, at what offsets. Objects built the same way share one; code that only ever sees a single hidden class is code V8 can specialize hard. That's what &lt;em&gt;monomorphic&lt;/em&gt; means (a fuller treatment comes below).&lt;/p&gt;

&lt;p&gt;What Crankshaft was bad at, and what eventually retired it: keeping pace with new ECMAScript features. Generators, classes, destructuring, &lt;code&gt;let&lt;/code&gt;/&lt;code&gt;const&lt;/code&gt;, async functions—every one was either grudgingly supported through baseline-only paths or left unoptimized. The team had to add every new feature to &lt;em&gt;both&lt;/em&gt; compilers—and to a third C++ runtime that the bytecode-less baseline compiler interacted with. That cost was suffocating the team.&lt;/p&gt;

&lt;h3&gt;
  
  
  Ignition + TurboFan (2016–2017)
&lt;/h3&gt;

&lt;p&gt;Ignition shipped first, in 2016, as a &lt;a href="https://v8.dev/blog/ignition-interpreter" rel="noopener noreferrer"&gt;bytecode interpreter&lt;/a&gt;—V8 hadn't previously had one. Ignition was motivated by &lt;em&gt;memory&lt;/em&gt;, not speed: a bytecode interpreter's memory footprint is dramatically smaller than baseline-compiled machine code, which mattered enormously for the mobile devices Chrome was deploying on.&lt;sup id="fnref1"&gt;1&lt;/sup&gt; As a side effect, Ignition gave V8 a single canonical place where every JavaScript program existed in a stable, structured form before any compiler ever looked at it. That single canonical form is what made the next three tiers possible.&lt;/p&gt;

&lt;p&gt;TurboFan shipped next, and in May 2017 V8 v5.9 &lt;a href="https://v8.dev/blog/launching-ignition-and-turbofan" rel="noopener noreferrer"&gt;made the new pipeline default&lt;/a&gt;: Ignition for first execution, TurboFan for hot functions. &lt;a href="https://v8.dev/blog/v8-release-61" rel="noopener noreferrer"&gt;Crankshaft was then removed in v6.1&lt;/a&gt;, and &lt;a href="https://v8.dev/blog/v8-release-62" rel="noopener noreferrer"&gt;Full-codegen followed in v6.2&lt;/a&gt;—the latter release alone deleted more than 30,000 lines of code. TurboFan introduced two big ideas:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A &lt;em&gt;layered&lt;/em&gt; architecture with explicit phases (typing, simplification, lowering, scheduling, machine-specific instruction selection). Each supported CPU architecture needed only a few thousand lines of architecture-specific code, against more than ten thousand per chip architecture for Crankshaft.&lt;sup id="fnref2"&gt;2&lt;/sup&gt; The layering made it tractable to add new optimizations and new ES features.&lt;/li&gt;
&lt;li&gt;A "sea of nodes" intermediate representation that lets the compiler reorder operations very freely, exposing more optimization opportunities than a strictly ordered control-flow graph would.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For numerical code, TurboFan was much better than Crankshaft at &lt;em&gt;getting out of the way&lt;/em&gt;: it could see through more closures, inline more deeply, eliminate redundant hidden-class checks, and unbox numbers more aggressively. If your code was already monomorphic and shape-stable, TurboFan generated meaningfully tighter machine code than Crankshaft did.&lt;/p&gt;

&lt;h3&gt;
  
  
  Sparkplug (2021)
&lt;/h3&gt;

&lt;p&gt;For the next four years, V8 was a two-tier system: Ignition → TurboFan. The gap between them was real. Ignition's bytecode dispatch is fast as interpreters go but pays a per-instruction decode cost; TurboFan only kicks in when a function has been called enough times to justify its compilation expense. Functions in the middle—called often enough to matter, not often enough to optimize—were stuck paying the interpreter tax indefinitely.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://v8.dev/blog/sparkplug" rel="noopener noreferrer"&gt;Sparkplug&lt;/a&gt;, shipped in V8 9.1 in 2021, fills that gap. It is a non-optimizing baseline JIT that compiles directly from Ignition bytecode in a single pass, with no IR, no type specialization, and no hidden-class-based fast paths. It defers most real work to the same builtins Ignition uses; what it eliminates is the per-bytecode dispatch overhead. Leszek Swirski's framing in the Sparkplug post is what made it click for me: a CPU is itself an interpreter for machine code, and Sparkplug is a "transpiler" from Ignition's bytecode to the CPU's. The function moves from running in an emulator to running natively, but the work it does in each instruction is roughly identical.&lt;/p&gt;

&lt;p&gt;Sparkplug's stack frames are bit-compatible with Ignition's. Debuggers, profilers, and exception handlers don't need to know Sparkplug exists. That compatibility is also what makes mid-loop tier promotion (on-stack replacement) trivial: because the layout matches, the engine can compile a Sparkplug version of a hot loop and patch the stack frame in place.&lt;/p&gt;

&lt;p&gt;For numerical code, Sparkplug mostly matters at the boundaries—for kernels that are called occasionally, and for code paths around the hot loop that handle argument validation and dispatch. The actual hot loop, if you wrote it well, will eventually get to TurboFan or Maglev. Sparkplug is what runs the rest while the type feedback accumulates.&lt;/p&gt;

&lt;h3&gt;
  
  
  Maglev (2023)
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://v8.dev/blog/maglev" rel="noopener noreferrer"&gt;Maglev&lt;/a&gt;, introduced in Chrome M117 in 2023, is a mid-tier optimizing compiler slotted between Sparkplug and TurboFan. It uses the same type feedback TurboFan does, performs many of the same specializations, and emits real optimized machine code. The difference: Maglev compiles roughly 10× faster than TurboFan and 10× slower than Sparkplug, with code quality calibrated accordingly.&lt;sup id="fnref3"&gt;3&lt;/sup&gt; Its IR is a traditional &lt;a href="https://en.wikipedia.org/wiki/Static_single-assignment_form" rel="noopener noreferrer"&gt;SSA-plus-CFG&lt;/a&gt; form—the program as an ordered graph of basic blocks (a control-flow graph, CFG) in which every value is assigned exactly once. TurboFan's &lt;a href="https://v8.dev/blog/leaving-the-sea-of-nodes" rel="noopener noreferrer"&gt;sea of nodes&lt;/a&gt; goes the other way on ordering—operations float free of any fixed order, and the compiler works out afterward when each one happens. Fixing the order up front is what keeps Maglev's register allocation simple, and simple register allocation is most of why it compiles fast.&lt;/p&gt;

&lt;p&gt;Maglev is the moment in the pipeline where speculative type specialization arrives. For numerical code that means two things become true simultaneously, and earlier in the warm-up curve than they used to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Hidden-class checks get folded.&lt;/strong&gt; Where a site has only ever seen &lt;code&gt;Float64Array&lt;/code&gt; arguments, Maglev collapses the hidden-class guard and resolves the element-load to a direct memory offset. The same monomorphism rules that pay off at TurboFan now pay off at Maglev.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Representation selection runs.&lt;/strong&gt; Maglev has an explicit phase that picks the in-register representation for every value: integer, float, or boxed object. A &lt;code&gt;Float64Array&lt;/code&gt; element is unambiguously a float, so Maglev picks the right register kind immediately. A plain &lt;code&gt;Array&lt;/code&gt; of mixed numbers and strings forces a conservative boxed representation through the whole loop. This is the moment unboxing actually happens.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A second consequence of Maglev shows up on the way back. The fall itself is unchanged: a deoptimization returns execution to Ignition, and the invocation that broke the bet finishes in the interpreter—off Maglev and off TurboFan alike, not one tier down. What Maglev changes is the recovery. Sparkplug's code is never discarded, so the next call starts in baseline machine code rather than in the interpreter, and the optimizing tier the function reaches on the way back up is Maglev rather than TurboFan. Older talks describe the deopt cliff as full machine code → interpreter, with a long crawl back. The drop is the same height; the climb out is much shorter.&lt;/p&gt;

&lt;h3&gt;
  
  
  Turboshaft (2023–2025), not an execution tier
&lt;/h3&gt;

&lt;p&gt;Turboshaft is the one entry on this list that isn't a tier, which is why the count above stays at four. In 2023 V8 began an internal effort to migrate TurboFan off the sea-of-nodes IR onto a more conventional CFG-based form, &lt;a href="https://v8.dev/blog/leaving-the-sea-of-nodes" rel="noopener noreferrer"&gt;called Turboshaft&lt;/a&gt;. The reasons were prosaic and accumulated over a decade: sea of nodes is flexible but cache-unfriendly (related operations end up scattered in memory), bugs are harder to investigate, and rewrite passes had grown gnarly. The blog post describing the move shipped in March 2025; by then the entire JavaScript backend of TurboFan had migrated. Compile times roughly halved versus the sea-of-nodes era.&lt;sup id="fnref4"&gt;4&lt;/sup&gt;&lt;/p&gt;

&lt;p&gt;Turboshaft is invisible at the source level—the optimizations TurboFan does are still the optimizations it does—but it shifts the engineering economics. Faster compilation means functions reach optimized code sooner, so a bigger share of any run executes in fast code rather than warming up. For long-running Node services and notebook-style scientific JS, that compounds. It also makes future optimization work cheaper, which means more of it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Putting the ladder back together
&lt;/h2&gt;

&lt;p&gt;A simplified schematic of the modern pipeline, including deopts:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fxm7wfmhbciybl7t46njd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fxm7wfmhbciybl7t46njd.png" alt="A schematic read top to bottom. Across the top, an intake chain: JS source feeds parse, which feeds AST. From AST a single arrow drops into a vertical ladder of four execution tiers. Ignition interprets bytecode and collects type feedback; an arrow down labelled warm leads to Sparkplug, which emits machine code with no specialization; an arrow labelled hotter leads to Maglev, a specializing optimizer with fast compilation; an arrow labelled hottest leads to TurboFan or Turboshaft, the peak optimizer. Two separate dashed arrows run back upward along the right-hand side, one leaving Maglev and one leaving TurboFan, both labelled deopt and both terminating at Ignition—a deoptimization returns execution to the interpreter, not to the tier below, and that invocation finishes there. The baseline code is never discarded, so the next call starts in Sparkplug." width="800" height="622"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Every promotion is gated on type-feedback signals collected by the tiers below. Every deoptimization returns to the interpreter, whichever tier it left. The path each function takes is its own—there is no global compile pass—and a function's currently-running tier is observable with &lt;a href="https://www.thenodebook.com/node-arch/v8-engine-intro#informational-flags" rel="noopener noreferrer"&gt;&lt;code&gt;node --trace-opt&lt;/code&gt;&lt;/a&gt; and friends.&lt;/p&gt;

&lt;p&gt;For numerical code: given enough warmup, an inner loop you care about will end up in TurboFan/Turboshaft (or Maglev, if it's not hot enough for TurboFan). The supporting code around it—argument validation, dispatch, the body of any function called occasionally—will sit at one of the lower tiers indefinitely. The discipline you apply to the hot loop pays off most at the top of the ladder; the discipline you apply to the supporting code pays off mostly by keeping a megamorphic inline-cache site or a hidden-class change from &lt;em&gt;deoptimizing the hot loop&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;One boundary worth naming: V8 also compiles WebAssembly, through its own baseline and optimizing tiers. It's a separate pipeline with a separate story, and out of scope here.&lt;/p&gt;

&lt;h2&gt;
  
  
  The representation primitives that make this work
&lt;/h2&gt;

&lt;p&gt;The compiler tiers are the layer most people think about, but the &lt;em&gt;value representation&lt;/em&gt; they operate on is doing just as much of the work. A handful of pieces show up in every V8 numerical-performance discussion.&lt;/p&gt;

&lt;h3&gt;
  
  
  SMI vs. HeapNumber
&lt;/h3&gt;

&lt;p&gt;V8 uses pointer tagging. A 64-bit slot whose low bit is zero is a &lt;em&gt;Small Integer&lt;/em&gt; (SMI)—a signed integer stored directly in the slot, no allocation, no indirection. The payload is 31-bit on builds with pointer compression and 32-bit on builds without. That's worth checking rather than assuming: Chrome enables pointer compression, official Node builds don't, so on Node &lt;code&gt;2**31 - 1&lt;/code&gt; is still an SMI. &lt;code&gt;node -p "process.config.variables.v8_enable_pointer_compression"&lt;/code&gt; answers it for your build. A slot whose low bit is one is a tagged pointer to a heap object. V8 stores any number that doesn't fit in the SMI range—floats included—as a &lt;strong&gt;HeapNumber&lt;/strong&gt;: a heap-allocated wrapper containing a 64-bit double, referenced from the slot by a tagged pointer.&lt;/p&gt;

&lt;p&gt;That's why the &lt;em&gt;boxed vs. unboxed&lt;/em&gt; distinction matters. In Ignition and Sparkplug, every floating-point arithmetic operation reads through a HeapNumber wrapper, possibly allocates a new one for the result, and adds garbage-collection pressure. In Maglev or TurboFan, once the optimizer has stable feedback that a value is always a float, it strips the wrapper: the raw 64-bit value lives in a CPU floating-point register, and arithmetic compiles down to a single floating-point instruction. A tight loop of thousands of float operations collapses to bare register arithmetic. Without unboxing, every number in your hot loop allocates.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pointer compression (V8 8.0, 2019)
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://v8.dev/blog/pointer-compression" rel="noopener noreferrer"&gt;Pointer compression&lt;/a&gt; shrinks every tagged slot from 64 bits to 32 by allocating all V8 objects within a 4 GB region and storing offsets rather than full pointers. The "base" half is shared per isolate; the "index" half is what gets stored. The headline number: heap memory dropped ~40%.&lt;sup id="fnref5"&gt;5&lt;/sup&gt;&lt;/p&gt;

&lt;p&gt;The visible effect on a numerical workload is mostly cache behavior. Halving slot sizes means each cache line holds twice as many tagged values, and arrays of objects (rare in stdlib's hot loops, common around them) become half as cache-cold. Pointer compression also constrains what the compiler can do—for instance, every dereference now requires a base+offset compute—but the cache wins generally outweigh the new instruction.&lt;/p&gt;

&lt;h3&gt;
  
  
  Mutable heap numbers (V8, 2025)
&lt;/h3&gt;

&lt;p&gt;A subtler 2025 addition: &lt;a href="https://v8.dev/blog/mutable-heap-number" rel="noopener noreferrer"&gt;in-place updates of HeapNumbers in script-context slots&lt;/a&gt;. Previously, a script-level &lt;code&gt;let seed = ...; seed = next(seed);&lt;/code&gt; pattern would allocate a fresh HeapNumber on each reassignment, even when the optimizer could prove only a single number was being threaded through. Now the slot can &lt;em&gt;own&lt;/em&gt; its HeapNumber and mutate the underlying double in place. The case study in the post is &lt;code&gt;Math.random&lt;/code&gt;'s state. For numerical code that uses module-scoped accumulators or threaded random-number-generator state, this eliminates a previously-invisible per-update allocation.&lt;/p&gt;

&lt;h3&gt;
  
  
  Hidden classes and inline caches
&lt;/h3&gt;

&lt;p&gt;Every V8 object points to a &lt;strong&gt;hidden class&lt;/strong&gt; that records what properties it has, in what order, at what offsets, and with what attributes. The naming is genuinely a mess, and it isn't your fault if you've been tripped up by it. One concept, and every engine picked a different word for it:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Engine&lt;/th&gt;
&lt;th&gt;Calls them&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;V8&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;Map&lt;/code&gt; — nothing to do with the JavaScript &lt;code&gt;Map&lt;/code&gt; builtin&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SpiderMonkey&lt;/td&gt;
&lt;td&gt;Shape — widely borrowed, so you'll meet it outside Firefox&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;JavaScriptCore&lt;/td&gt;
&lt;td&gt;Structure&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Chakra&lt;/td&gt;
&lt;td&gt;Type&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Hidden class&lt;/em&gt;&lt;/strong&gt; is the fifth name, and the one this post uses.&lt;sup id="fnref6"&gt;6&lt;/sup&gt; Academic papers use it—but so does V8, constantly, in its own documentation, whenever it's explaining rather than implementing. The heap doc introduces Maps as "also known as hidden classes or shapes" and heads the defining section "The Map (Hidden Class)"; V8's docs page on the subject is titled &lt;a href="https://v8.dev/docs/hidden-classes" rel="noopener noreferrer"&gt;&lt;em&gt;Maps (Hidden Classes) in V8&lt;/em&gt;&lt;/a&gt;. It isn't the obscure choice—it's the one term that travels across all four engines and back into V8's own prose.&lt;/p&gt;

&lt;p&gt;Two objects with the same hidden class are bit-compatible to V8's fast paths. Hidden classes are built incrementally: each added property transitions the object to a new one, which is why &lt;code&gt;{ x, y }&lt;/code&gt; and &lt;code&gt;{ y, x }&lt;/code&gt; end up different despite having the same property names.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fu2p4kxjfndlggaifb13c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fu2p4kxjfndlggaifb13c.png" alt="A transition tree. An empty object C0 branches two ways: adding x then y produces hidden class C2, holding { x: 1, y: 2 } at offsets x@0, y@1. Adding y then x produces a different hidden class C2-prime, holding { y: 2, x: 1 } at offsets y@0, x@1. Same keys, same values, different hidden classes." width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you come to this from C or C++, Benedikt Meurer's &lt;a href="https://www.youtube.com/watch?v=IFWulQnM5E0" rel="noopener noreferrer"&gt;talk on types, classes, and maps&lt;/a&gt; offers the translation that tends to land hardest: a V8 hidden class plays roughly the role of a vtable pointer plus a field-offset table. "Keep the hidden class stable" then reads as the same discipline: don't reshape a struct at runtime.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Inline caches&lt;/strong&gt; (ICs) are per-call-site memoizations of the hidden classes the site has seen. The hot path replaces dictionary lookups with a hidden-class check plus a load from a known offset. As long as a site only sees one hidden class (&lt;em&gt;monomorphic&lt;/em&gt;) it stays fast. With two to four it becomes &lt;em&gt;polymorphic&lt;/em&gt;—a chain of compare-and-load attempts. Past that threshold V8 gives up and falls back to a generic dictionary lookup (&lt;em&gt;megamorphic&lt;/em&gt;), and the hot-loop assumption is over.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fciewdwskky8hnveer4iq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fciewdwskky8hnveer4iq.png" alt="One property-access site, a function that returns o.x, shown in three inline-cache states. Monomorphic, one hidden class: a single guard and a direct load, which inlines and lets repeat checks collapse. Polymorphic, two to four hidden classes: a compare-and-load chain, still inlinable but no longer able to collapse repeated checks. Megamorphic, five or more: no per-site list at all, just a probe into a global stub cache, with no inlining and no type information flowing downstream. The source code is identical in all three; only the hidden classes that reached the site differ. Cache state belongs to the call site, not to the caller: one caller passing a fifth hidden class moves the site for everyone using it—and nobody else did anything wrong." width="800" height="407"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Hidden-class stability buys more than a fast IC, though. As Vyacheslav Egorov works through in &lt;a href="https://mrale.ph/blog/2015/01/11/whats-up-with-monomorphism.html" rel="noopener noreferrer"&gt;&lt;em&gt;What's up with monomorphism?&lt;/em&gt;&lt;/a&gt;, a monomorphic site lets the optimizer treat repeated hidden-class checks as redundant and eliminate all but the first. A polymorphic site structurally cannot get that: each variant needs its own guard.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ValidityCells&lt;/strong&gt; are the trick that lets prototype-based code stay fast. Each prototype's hidden class carries a single-bit "still valid?" flag; ICs that depend on a clean prototype chain can collapse all their lookup checks down to a read of that bit. A mutation anywhere on a prototype chain (including, catastrophically, on &lt;code&gt;Object.prototype&lt;/code&gt;) flips the cell, invalidating every IC that depended on it.&lt;/p&gt;

&lt;p&gt;Anything that participates in a hot dispatch path has to be shape-stable—argument-shape validators, factory functions for option objects, anything that builds an object on the way into the kernel. The cost of polymorphism is not an extra cycle per access; it is the &lt;em&gt;loss of the entire optimizer's bet on this call site.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Elements kinds
&lt;/h3&gt;

&lt;p&gt;For &lt;em&gt;array&lt;/em&gt; objects, V8 tracks a separate tag called the &lt;strong&gt;elements kind&lt;/strong&gt; that describes what's in the numerically-indexed slots. Plain &lt;code&gt;Array&lt;/code&gt; instances live on a six-cell lattice: &lt;code&gt;PACKED_SMI&lt;/code&gt; / &lt;code&gt;PACKED_DOUBLE&lt;/code&gt; / &lt;code&gt;PACKED_ELEMENTS&lt;/code&gt;, plus the three &lt;code&gt;HOLEY_*&lt;/code&gt; variants. The lattice is one-way. An array can be demoted from &lt;code&gt;PACKED_SMI&lt;/code&gt; to &lt;code&gt;PACKED_DOUBLE&lt;/code&gt; (by a single float write) to &lt;code&gt;PACKED_ELEMENTS&lt;/code&gt; (by a single string or object write), or to a &lt;code&gt;HOLEY_*&lt;/code&gt; variant (by &lt;code&gt;delete&lt;/code&gt;, by &lt;code&gt;new Array(n)&lt;/code&gt; without immediate fill, by writing past &lt;code&gt;length&lt;/code&gt;). On the value-type axis the demotion is permanent for the life of that array, even if the offending value is removed—writing an integer back over the float doesn't restore &lt;code&gt;PACKED_SMI&lt;/code&gt;. The holey axis is the one exception: &lt;code&gt;Array.prototype.fill&lt;/code&gt; can return a holey array to packed, including one made holey by &lt;code&gt;delete&lt;/code&gt;.&lt;sup id="fnref7"&gt;7&lt;/sup&gt;&lt;/p&gt;

&lt;p&gt;Typed arrays—&lt;code&gt;Float64Array&lt;/code&gt;, &lt;code&gt;Int32Array&lt;/code&gt;, and friends—sit &lt;em&gt;outside&lt;/em&gt; this lattice on their own elements kinds (&lt;code&gt;FLOAT64_ELEMENTS&lt;/code&gt;, &lt;code&gt;INT32_ELEMENTS&lt;/code&gt;, etc.). They cannot be demoted, cannot be made holey, and cannot have their type changed—the typed-array API enforces all three. From V8's point of view, a typed-array element is already in its raw machine-level representation on the backing buffer; reading one is a memory load, not a wrapper unwrap.&lt;/p&gt;

&lt;p&gt;Typed arrays aren't an optimization; they're a guarantee. Every element is provably a fixed-width number, every access has a well-defined in-bounds outcome, every stored value is provably the right type.&lt;/p&gt;

&lt;p&gt;That guarantee, rather than raw speed, is why numerical stdlib code is built on them—and the distinction matters, because the obvious version of the claim isn't true. A plain &lt;code&gt;Array&lt;/code&gt; of integers isn't automatically slower than an &lt;code&gt;Int32Array&lt;/code&gt;. Typed arrays even do work a plain array doesn't: every write is coerced to the element type, so &lt;code&gt;2**31&lt;/code&gt; stored into an &lt;code&gt;Int32Array&lt;/code&gt; reads back as &lt;code&gt;-2147483648&lt;/code&gt;, and &lt;code&gt;300&lt;/code&gt; stored into a &lt;code&gt;Uint8ClampedArray&lt;/code&gt; reads back as &lt;code&gt;255&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;What they buy isn't a lower floor. It's a narrower band. A plain array is fast right up until something demotes it, and then it's quietly slower for the rest of its life—and nothing in the code says so. A typed array can't be demoted, so the millionth call performs like the first. That's what &lt;em&gt;write it like C&lt;/em&gt; is asking for; the Zen says "prefer predictable performance," not peak performance.&lt;/p&gt;

&lt;p&gt;And one reason has nothing to do with the optimizer at all: a typed array is a view over an &lt;code&gt;ArrayBuffer&lt;/code&gt;, which is the layout a C routine expects on the other side of a native add-on, so the data crosses without being copied.&lt;/p&gt;

&lt;h2&gt;
  
  
  What this means for stdlib-style code
&lt;/h2&gt;

&lt;p&gt;This is where the aphorisms cash out. Each convention below protects one specific decision the machinery above makes, and once you can name the decision, the convention stops reading as a prohibition.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;One dtype, one implementation.&lt;/strong&gt; stdlib ships separate &lt;code&gt;dasum&lt;/code&gt; and &lt;code&gt;sasum&lt;/code&gt;—double-precision and single-precision absolute-sum kernels—instead of one polymorphic kernel that dispatches on argument type. Two stories combine here, elements kinds and ICs: passing a &lt;code&gt;Float64Array&lt;/code&gt; and a &lt;code&gt;Float32Array&lt;/code&gt; to the same function makes its loop body see two different elements kinds, so the IC at the indexed-access site goes polymorphic, and the optimizer's bet gets weaker on every successive call. Two separate functions, each used only with its own dtype, stay monomorphic. stdlib makes this argument in its own source, too. &lt;code&gt;@stdlib/array/base/arraylike2object&lt;/code&gt; exists to normalize array-likes into one fixed-shape descriptor object, and its README says why in as many words: if objects are built with properties in different orders, "then those objects will have different 'hidden' classes," and a function fed enough of those shapes "will cause the function to be considered 'megamorphic'." The workaround is a helper whose only job is to hand every call site the same shape.&lt;/p&gt;

&lt;p&gt;One of stdlib's maintainers frames the whole discipline in terms of the language it's imitating: "Just like in C you have to create every single different typed interface as a new function, we're doing the exact same thing." The proliferation of near-identical kernels isn't duplication that a cleverer abstraction would collapse. It's the point.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Hidden classes have to be stable up to and across the kernel.&lt;/strong&gt; When a kernel takes an options object—a strided-ndarray descriptor, a BLAS-style transposition flag—every call site has to build that object the same way, with the same property names in the same order. If a caller mutates the object after construction (adds a property, deletes one), it transitions the hidden class out of whatever the kernel's IC was specialized on. stdlib's convention follows from that: every property defined at construction time, in a fixed order, never mutated afterward—and, where it matters, that construction centralized in a factory rather than left to each call site to remember.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Typed arrays do double duty.&lt;/strong&gt; They give you (1) shape stability—&lt;code&gt;Float64Array&lt;/code&gt; cannot become a &lt;code&gt;Float32Array&lt;/code&gt;, cannot acquire holes, cannot become megamorphic—and (2) unboxed representation—every element is a raw double on the backing buffer, no HeapNumber wrapper. A plain &lt;code&gt;Array&lt;/code&gt; of numbers can have the second: a &lt;code&gt;PACKED_DOUBLE&lt;/code&gt; array stores raw doubles in a &lt;code&gt;FixedDoubleArray&lt;/code&gt;, no wrappers. What it can't have is the first—and losing the first costs you the second. One string write demotes the array to &lt;code&gt;PACKED_ELEMENTS&lt;/code&gt;, and every element becomes a tagged pointer again. The guarantee is what makes the representation durable.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Loops stay lean.&lt;/strong&gt; TurboFan can optimize an enormous amount, but it cannot prove invariants the source doesn't make available to it. This is the mechanical content of &lt;em&gt;be explicit&lt;/em&gt;—the optimizer is one of the readers whose mental model won't match yours. Three patterns follow from it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;Loop unswitching.&lt;/em&gt; Where a loop body branches on a value that doesn't change inside the loop (an &lt;code&gt;option === 'conjugate-transpose'&lt;/code&gt; flag, say), stdlib hoists the branch out and writes two loop bodies. TurboFan has loop-invariant code motion, but it can't always prove an opaque comparison is side-effect-free, so the fix lands at authoring time instead—as a stdlib maintainer puts it, "if you need to duplicate loops, so be it." (In benchmarks it does a second job: it keeps the optimizer from eliminating the unobserved branch and silently measuring less work than you meant to.)&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Scalar decomposition before entry.&lt;/em&gt; Where a complex scalar &lt;code&gt;alpha&lt;/code&gt; is used throughout a loop, it gets decomposed once before entry—&lt;code&gt;const re = real(alpha); const im = imag(alpha);&lt;/code&gt;—and the loop reads &lt;code&gt;re&lt;/code&gt; and &lt;code&gt;im&lt;/code&gt;. Even where TurboFan would have hoisted the calls, being explicit removes the uncertainty.&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;No allocation inside the loop.&lt;/em&gt; Calling &lt;code&gt;.get(i)&lt;/code&gt; on a &lt;code&gt;Complex128Array&lt;/code&gt; inside a loop allocates a complex-number object every iteration, so kernels cast to a &lt;code&gt;Float64Array&lt;/code&gt; view of the same memory with &lt;code&gt;@stdlib/strided/base/reinterpret-complex128&lt;/code&gt; and read pairs of doubles instead. It's the same memory layout a C implementation would walk with a &lt;code&gt;double*&lt;/code&gt; over interleaved real and imaginary parts, which is the part of &lt;em&gt;write it like C&lt;/em&gt; that's literal rather than analogical. &lt;a href="https://blog.stdlib.io/introducing-the-accessor-protocol-for-array-like-objects/" rel="noopener noreferrer"&gt;Athan Reines' accessor-protocol post&lt;/a&gt; unpacks that two-layer arrangement: logical complex-values on top, raw floats underneath.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The through-line: move anything invariant across iterations out of the loop at authoring time, rather than leaving it for the compiler to maybe hoist.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Plain arrays stay packed.&lt;/strong&gt; Where a plain &lt;code&gt;Array&lt;/code&gt; is unavoidable—the stdlib &lt;code&gt;generic&lt;/code&gt; dtype, argument lists—stdlib builds it with a literal &lt;code&gt;[ ]&lt;/code&gt; or with &lt;code&gt;push&lt;/code&gt; rather than &lt;code&gt;new Array(n)&lt;/code&gt; followed by index assignment, and removes indices with &lt;code&gt;splice&lt;/code&gt; rather than &lt;code&gt;delete&lt;/code&gt;. Once an array transitions to &lt;code&gt;HOLEY_*&lt;/code&gt;, every read pays for a possible prototype-chain walk in case &lt;code&gt;Array.prototype[i]&lt;/code&gt; was set. That cost is what &lt;em&gt;fix them early&lt;/em&gt; is pointing at: on the value-type axis the lattice is one-way, so there's no later cleanup pass that undoes it, and even on the recoverable holey axis the fix has to be deliberate—&lt;code&gt;fill&lt;/code&gt; is something you have to know to reach for, not something that happens on its own.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Prototypes don't change after instances exist.&lt;/strong&gt; Adding a method to a constructor's prototype after instances exist invalidates every IC that watched that prototype. Adding anything to &lt;code&gt;Object.prototype&lt;/code&gt; invalidates every prototype-walking IC in the running program. This is &lt;em&gt;mistakes are infectious&lt;/em&gt; with a runtime mechanism behind it—one upstream mutation is paid for by every downstream caller, none of whom did anything wrong.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Verify, don't theorize.&lt;/strong&gt; This was the part I found most reassuring, because it means none of the above has to be taken on faith. Run Node.js with &lt;code&gt;--allow-natives-syntax&lt;/code&gt; and use &lt;code&gt;%HaveSameMap(a, b)&lt;/code&gt; to confirm two objects share a hidden class, or &lt;code&gt;%DebugPrint(arr)&lt;/code&gt; to read off an array's elements kind. Run with &lt;code&gt;--trace-opt --trace-deopt&lt;/code&gt; to watch promotions and deopts scroll past, with the reason attached—&lt;code&gt;wrong map&lt;/code&gt; is the one you'll see most, and it's worth knowing that's the string V8 prints for a hidden-class mismatch. IC state transitions are a little more work: the old &lt;code&gt;--trace-ic&lt;/code&gt; flag no longer exists, and its replacement &lt;code&gt;--log-ic&lt;/code&gt; writes to a file rather than to your terminal—V8's own &lt;code&gt;tools/ic-processor&lt;/code&gt; then reads that file. For the generated machine code, &lt;code&gt;--print-opt-code&lt;/code&gt; dumps it. For the IR, &lt;code&gt;--trace-turbo&lt;/code&gt; plus &lt;a href="https://v8.github.io/tools/head/turbolizer/" rel="noopener noreferrer"&gt;Turbolizer&lt;/a&gt;.&lt;sup id="fnref8"&gt;8&lt;/sup&gt; The decisions that matter for a hot path are observable, which means "is this really monomorphic?" is a question you can answer rather than argue about.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Benchmark hygiene.&lt;/strong&gt; Microbenchmarks that look reasonable can measure nothing—Egorov's &lt;a href="http://mrale.ph/talks/goto2016/" rel="noopener noreferrer"&gt;&lt;em&gt;Performance Through the Spyglass&lt;/em&gt;&lt;/a&gt; is the canonical demonstration, and his one-line version of it is hard to improve on: optimizers eat microbenchmarks for dinner. The optimizer is allowed to constant-propagate, dead-code-eliminate, hoist invariants out of the timing loop, and unroll. stdlib's benchmark convention defends against this by including explicit lightweight assertions—e.g., a NaN self-inequality check (&lt;code&gt;if ( x !== x ) { b.fail(...) }&lt;/code&gt;) inside and after the timing loop. The trick exploits NaN's inequality with itself: the check is false for any ordinary value, so the fail path never runs. But the compiler still has to observe the read of &lt;code&gt;x&lt;/code&gt; (which defeats dead-code elimination) and it can't fold the check away.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where this seems to be heading
&lt;/h2&gt;

&lt;p&gt;The rules—monomorphic call sites, stable hidden classes, typed-array storage, lean loops—have held for a decade. The infrastructure underneath has been rebuilt three or four times and presumably will be again. I'm not going to pretend to know what V8 ships next, but three things look like they're already in motion:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Float16&lt;/strong&gt; and &lt;code&gt;Float16Array&lt;/code&gt; arrived in browsers recently. The numerical case for half precision is mostly machine-learning-shaped (model weights, intermediate activations) but that case will increasingly intersect numerical-JS code living next to ML pipelines. stdlib has already moved: &lt;code&gt;float16&lt;/code&gt; is a working dtype with a real &lt;code&gt;Float16Array&lt;/code&gt; behind it, and it participates in the promotion and safe-cast tables like any other. &lt;code&gt;complex32&lt;/code&gt; is the genuinely forward-looking one—it's named in &lt;code&gt;@stdlib/ndarray/dtypes&lt;/code&gt; and in the dtype-kind taxonomy, but no &lt;code&gt;Complex32Array&lt;/code&gt; exists yet to back it and its addition is slated as future work.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Maglev → Turboshaft path keeps compressing the warmup curve.&lt;/strong&gt; A hot loop reaches optimized machine code earlier on each successive V8 release, and where parts of a call graph used to sit in Ignition, never quite crossing the TurboFan threshold, they now land in Maglev instead. That makes occasional-path code matter slightly more than it used to: polymorphism in a moderately-called function is now something the optimizer has to account for, where before it wasn't looking.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mutable heap numbers and similar fine-grained representation tricks&lt;/strong&gt; are the genre of optimization V8 is shipping right now. These are mostly invisible from the source (good code keeps getting faster) but they reward the same disciplines that are already best practice: fewer abstraction layers between the source's intent and a single number in a single register.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Most of this is invisible if the kernels are well-behaved. That's by design. The optimizer's job is to take code that &lt;em&gt;describes&lt;/em&gt; a numerical loop dynamically and &lt;em&gt;execute&lt;/em&gt; it as if it were a statically-typed one. The shorter the gap between those two, the easier its job.&lt;/p&gt;

&lt;p&gt;Which is where I ended up on the aphorisms. &lt;em&gt;Write it like C&lt;/em&gt; isn't nostalgia for a language most of stdlib's JavaScript never touches, and &lt;em&gt;don't be clever&lt;/em&gt; isn't a general plea for humility. They both describe the shape a speculative optimizer is looking for, written in the imperative because that's the form a convention takes. Fifteen years of compiler engineering went into recognizing that shape at runtime. The list is short because the shape is simple—which is also, I think, what &lt;em&gt;simple is beautiful&lt;/em&gt; is doing at the end of the Zen of stdlib.&lt;/p&gt;

&lt;h2&gt;
  
  
  Further reading
&lt;/h2&gt;

&lt;h3&gt;
  
  
  V8 team primary sources
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://blog.chromium.org/2010/12/new-crankshaft-for-v8.html" rel="noopener noreferrer"&gt;A New Crankshaft for V8&lt;/a&gt; — Chromium Blog, December 2010&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://v8.dev/blog/ignition-interpreter" rel="noopener noreferrer"&gt;Firing up the Ignition interpreter&lt;/a&gt; — V8 blog, 2016&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://v8.dev/blog/launching-ignition-and-turbofan" rel="noopener noreferrer"&gt;Launching Ignition and TurboFan&lt;/a&gt; — V8 blog, May 2017&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://v8.dev/blog/v8-release-61" rel="noopener noreferrer"&gt;V8 release v6.1&lt;/a&gt; — V8 blog (Crankshaft removed)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://v8.dev/blog/v8-release-62" rel="noopener noreferrer"&gt;V8 release v6.2&lt;/a&gt; — V8 blog (baseline compiler removed)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://v8.dev/blog/turbofan-jit" rel="noopener noreferrer"&gt;Digging into the TurboFan JIT&lt;/a&gt; — V8 blog&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://v8.dev/docs/turbofan" rel="noopener noreferrer"&gt;TurboFan docs&lt;/a&gt; — V8 docs&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://v8.dev/docs/hidden-classes" rel="noopener noreferrer"&gt;Maps (Hidden Classes) in V8&lt;/a&gt; — V8 docs&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://chromium.googlesource.com/v8/v8/+/HEAD/docs/heap/objects-and-maps.md" rel="noopener noreferrer"&gt;Objects and Maps in V8&lt;/a&gt; — V8 in-repo docs&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://chromium.googlesource.com/v8/v8/+/HEAD/docs/runtime/hidden-classes-and-ics.md" rel="noopener noreferrer"&gt;Hidden Classes and Inline Caches in V8&lt;/a&gt; — V8 in-repo docs&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://v8.dev/blog/pointer-compression" rel="noopener noreferrer"&gt;Pointer Compression in V8&lt;/a&gt; — V8 blog (V8 8.0)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://v8.dev/blog/sparkplug" rel="noopener noreferrer"&gt;Sparkplug—a non-optimizing JavaScript compiler&lt;/a&gt; — V8 blog, 2021 (V8 9.1)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://v8.dev/blog/maglev" rel="noopener noreferrer"&gt;Maglev: V8's Fastest Optimizing JIT&lt;/a&gt; — V8 blog, 2023 (Chrome M117)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://v8.dev/blog/leaving-the-sea-of-nodes" rel="noopener noreferrer"&gt;Land ahoy: leaving the Sea of Nodes&lt;/a&gt; — V8 blog, March 2025 (Turboshaft)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://v8.dev/blog/mutable-heap-number" rel="noopener noreferrer"&gt;Turbocharging V8 with mutable heap numbers&lt;/a&gt; — V8 blog, February 2025&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://v8.dev/blog/fast-properties" rel="noopener noreferrer"&gt;Fast properties in V8&lt;/a&gt; — V8 blog&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://v8.dev/blog/elements-kinds" rel="noopener noreferrer"&gt;Elements kinds in V8&lt;/a&gt; — Mathias Bynens, V8 blog&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://v8.dev/blog/10-years" rel="noopener noreferrer"&gt;Celebrating 10 years of V8&lt;/a&gt; — V8 blog&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Talks and write-ups by V8 team members and adjacent practitioners
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Mathias Bynens — &lt;a href="https://mathiasbynens.be/notes/shapes-ics" rel="noopener noreferrer"&gt;&lt;em&gt;JavaScript engine fundamentals: Shapes and Inline Caches&lt;/em&gt;&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Mathias Bynens — &lt;a href="https://www.youtube.com/watch?v=m9cTaYI95Zc" rel="noopener noreferrer"&gt;&lt;em&gt;V8 internals for JavaScript developers&lt;/em&gt;&lt;/a&gt; (JSConf EU)&lt;/li&gt;
&lt;li&gt;Benedikt Meurer — &lt;a href="https://www.youtube.com/watch?v=IFWulQnM5E0" rel="noopener noreferrer"&gt;&lt;em&gt;JavaScript engines: a tale of types, classes, and maps&lt;/em&gt;&lt;/a&gt; (JSCamp Barcelona, 2018)&lt;/li&gt;
&lt;li&gt;Franziska Hinkelmann — &lt;a href="https://www.youtube.com/watch?v=p-iiEDtpy6I" rel="noopener noreferrer"&gt;&lt;em&gt;JavaScript engines—how do they even?&lt;/em&gt;&lt;/a&gt; (JSConf EU)&lt;/li&gt;
&lt;li&gt;Franziska Hinkelmann — &lt;a href="https://www.youtube.com/watch?v=j6LfSlg8Fig" rel="noopener noreferrer"&gt;&lt;em&gt;Performance Profiling for V8&lt;/em&gt;&lt;/a&gt; (Script'17)&lt;/li&gt;
&lt;li&gt;Vyacheslav Egorov — &lt;a href="https://www.youtube.com/watch?v=r76ZjdzFExg" rel="noopener noreferrer"&gt;&lt;em&gt;JavaScript Performance Through the Spyglass&lt;/em&gt;&lt;/a&gt; (&lt;a href="http://mrale.ph/talks/goto2016/" rel="noopener noreferrer"&gt;slides&lt;/a&gt;, GOTO Amsterdam, 2016)&lt;/li&gt;
&lt;li&gt;Vyacheslav Egorov — &lt;a href="https://mrale.ph/blog/2012/06/03/explaining-js-vms-in-js-inline-caches.html" rel="noopener noreferrer"&gt;&lt;em&gt;Explaining JavaScript VMs in JavaScript—Inline Caches&lt;/em&gt;&lt;/a&gt; — 2012; the explanation V8's own &lt;a href="https://v8.dev/blog/fast-properties" rel="noopener noreferrer"&gt;&lt;em&gt;Fast properties in V8&lt;/em&gt;&lt;/a&gt; sends readers to for inline caches&lt;/li&gt;
&lt;li&gt;Vyacheslav Egorov — &lt;a href="https://mrale.ph/blog/2015/01/11/whats-up-with-monomorphism.html" rel="noopener noreferrer"&gt;&lt;em&gt;What's up with monomorphism?&lt;/em&gt;&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Bartek Szopka — &lt;a href="https://www.youtube.com/watch?v=MqHDDtVYJRI" rel="noopener noreferrer"&gt;&lt;em&gt;Everything you never wanted to know about JavaScript numbers&lt;/em&gt;&lt;/a&gt; (JSConf EU 2013)&lt;/li&gt;
&lt;li&gt;Ishtmeet Singh — &lt;a href="https://www.thenodebook.com/node-arch/v8-engine-intro" rel="noopener noreferrer"&gt;&lt;em&gt;V8 JavaScript Engine in Node.js: Architecture, Tiers, Shapes, and Deoptimization&lt;/em&gt;&lt;/a&gt; — &lt;em&gt;The NodeBook&lt;/em&gt;, September 2025&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  stdlib and stdlib-adjacent
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/stdlib-js/stdlib" rel="noopener noreferrer"&gt;&lt;code&gt;stdlib-js/stdlib&lt;/code&gt;&lt;/a&gt; — the codebase&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/stdlib-js/stdlib/tree/develop/lib/node_modules/%40stdlib/array/dtypes" rel="noopener noreferrer"&gt;&lt;code&gt;@stdlib/array/dtypes&lt;/code&gt;&lt;/a&gt; — dtype registry; the "C type system at the API level"&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/stdlib-js/stdlib/tree/1743c65c572a188de7ccae946f542efce8755a0f/lib/node_modules/%40stdlib/blas/base/dasum" rel="noopener noreferrer"&gt;&lt;code&gt;@stdlib/blas/base/dasum&lt;/code&gt;&lt;/a&gt; and &lt;a href="https://github.com/stdlib-js/stdlib/tree/1743c65c572a188de7ccae946f542efce8755a0f/lib/node_modules/%40stdlib/blas/base/sasum" rel="noopener noreferrer"&gt;&lt;code&gt;@stdlib/blas/base/sasum&lt;/code&gt;&lt;/a&gt; — the canonical "one dtype, one implementation" pair&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/stdlib-js/stdlib/tree/1743c65c572a188de7ccae946f542efce8755a0f/lib/node_modules/%40stdlib/blas/base/zaxpy/lib/ndarray.js" rel="noopener noreferrer"&gt;&lt;code&gt;@stdlib/blas/base/zaxpy&lt;/code&gt; (&lt;code&gt;ndarray.js&lt;/code&gt;)&lt;/a&gt; — the reference implementation of the lean-inner-loop pattern with &lt;code&gt;reinterpret-complex128&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Athan Reines — &lt;a href="https://blog.stdlib.io/zen-of-stdlib/" rel="noopener noreferrer"&gt;&lt;em&gt;The Zen of stdlib&lt;/em&gt;&lt;/a&gt; — blog.stdlib.io; goes into considerably more detail than the &lt;a href="https://github.com/stdlib-js/stdlib/blob/develop/docs/contributing/zen_of_stdlib.md" rel="noopener noreferrer"&gt;contributing-docs version&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Athan Reines — &lt;a href="https://blog.stdlib.io/introducing-the-accessor-protocol-for-array-like-objects/" rel="noopener noreferrer"&gt;&lt;em&gt;Introducing the Accessor Protocol for Array-Like Objects&lt;/em&gt;&lt;/a&gt; — blog.stdlib.io&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;
    &lt;em&gt;Mara Averick is a developer advocate at &lt;a href="https://quansight.com/" rel="noopener noreferrer"&gt;Quansight&lt;/a&gt; and contributor experience lead for &lt;a href="https://github.com/stdlib-js/stdlib" rel="noopener noreferrer"&gt;stdlib&lt;/a&gt;.&lt;/em&gt;
&lt;/p&gt;




&lt;p&gt;&lt;a href="https://github.com/stdlib-js/stdlib" rel="noopener noreferrer"&gt;stdlib&lt;/a&gt; is an open source software project dedicated to providing a comprehensive suite of robust, high-performance libraries to accelerate your project's development and give you peace of mind knowing that you're depending on expertly crafted, high-quality software.&lt;/p&gt;

&lt;p&gt;If you've enjoyed this post, give us a star 🌟 on &lt;a href="https://github.com/stdlib-js/stdlib" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; and consider &lt;a href="https://opencollective.com/stdlib" rel="noopener noreferrer"&gt;supporting&lt;/a&gt; the project. Your contributions and continued support help ensure the project's long-term success and are greatly appreciated!&lt;/p&gt;

&lt;h2&gt;
  
  
  Acknowledgments
&lt;/h2&gt;

&lt;p&gt;This work was supported in part by the National Science Foundation under &lt;a href="https://www.nsf.gov/awardsearch/showAward?AWD_ID=2449410&amp;amp;HistoricalAwards=false" rel="noopener noreferrer"&gt;Award No. 2449410&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Disclaimer: Any opinions, findings and conclusions or recommendations expressed in this material are those of the author(s) and do not necessarily reflect the views of the National Science Foundation.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;ol&gt;

&lt;li id="fn1"&gt;
&lt;p&gt;V8's &lt;a href="https://v8.dev/blog/ignition-interpreter" rel="noopener noreferrer"&gt;Ignition announcement&lt;/a&gt; gives both figures: baseline-compiled machine code was occupying roughly a third of Chrome's JavaScript heap, and Ignition's bytecode is 25–50% the size of the equivalent baseline machine code.&amp;nbsp;↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn2"&gt;
&lt;p&gt;The &lt;a href="https://v8.dev/blog/launching-ignition-and-turbofan" rel="noopener noreferrer"&gt;launch post&lt;/a&gt; puts Crankshaft at "more than ten thousand lines of code per chip architecture," against a few thousand for TurboFan's architecture-specific layer.&amp;nbsp;↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn3"&gt;
&lt;p&gt;Both ratios are &lt;a href="https://v8.dev/blog/maglev" rel="noopener noreferrer"&gt;the V8 team's own&lt;/a&gt;: Maglev compiles roughly an order of magnitude faster than TurboFan and an order of magnitude slower than Sparkplug.&amp;nbsp;↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn4"&gt;
&lt;p&gt;The &lt;a href="https://v8.dev/blog/leaving-the-sea-of-nodes" rel="noopener noreferrer"&gt;Turboshaft write-up&lt;/a&gt; reports compile time "divided by 2" versus the sea-of-nodes pipeline.&amp;nbsp;↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn5"&gt;
&lt;p&gt;The ~40% heap-memory reduction is reported in V8's &lt;a href="https://v8.dev/blog/pointer-compression" rel="noopener noreferrer"&gt;pointer-compression write-up&lt;/a&gt;.&amp;nbsp;↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn6"&gt;
&lt;p&gt;The engine-by-engine mapping is Mathias Bynens and Benedikt Meurer's, from &lt;a href="https://mathiasbynens.be/notes/shapes-ics" rel="noopener noreferrer"&gt;&lt;em&gt;JavaScript engine fundamentals: Shapes and Inline Caches&lt;/em&gt;&lt;/a&gt; (14 June 2018). Their list adds the collision warnings the table compresses away: &lt;em&gt;Hidden Classes&lt;/em&gt; is confusing with respect to JavaScript classes, &lt;em&gt;Maps&lt;/em&gt; with respect to JavaScript &lt;code&gt;Map&lt;/code&gt;s, and &lt;em&gt;Types&lt;/em&gt; with respect to &lt;code&gt;typeof&lt;/code&gt;.&amp;nbsp;↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn7"&gt;
&lt;p&gt;V8's &lt;a href="https://v8.dev/blog/elements-kinds" rel="noopener noreferrer"&gt;elements-kinds post&lt;/a&gt; carries an inline amendment, "Update @ 2025-02-28: There is now an exception to this for &lt;code&gt;Array.prototype.fill&lt;/code&gt; specifically," and gives no further detail. Testing on Node v24.19.0 puts the exception slightly wider than the note implies: &lt;code&gt;fill&lt;/code&gt; restores packed status from &lt;code&gt;delete&lt;/code&gt;-induced holes as well.&amp;nbsp;↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn8"&gt;
&lt;p&gt;Singh's &lt;em&gt;NodeBook&lt;/em&gt; has flag tables—&lt;a href="https://www.thenodebook.com/node-arch/v8-engine-intro#informational-flags" rel="noopener noreferrer"&gt;informational&lt;/a&gt;, &lt;a href="https://www.thenodebook.com/node-arch/v8-engine-intro#behavioral-flags" rel="noopener noreferrer"&gt;behavioral&lt;/a&gt;, and &lt;a href="https://www.thenodebook.com/node-arch/v8-engine-intro#how-to-use-flags" rel="noopener noreferrer"&gt;how to pass them&lt;/a&gt;—which are a far better starting point than the &lt;a href="https://nodejs.org/docs/latest-v24.x/api/v8.html#v8setflagsfromstringflags" rel="noopener noreferrer"&gt;raw &lt;code&gt;--v8-options&lt;/code&gt; dump&lt;/a&gt;.&amp;nbsp;↩&lt;/p&gt;
&lt;/li&gt;

&lt;/ol&gt;

</description>
      <category>javascript</category>
      <category>devrel</category>
    </item>
    <item>
      <title>Your AI Policy Doesn't Have to Apply to You</title>
      <dc:creator>Mara Averick</dc:creator>
      <pubDate>Wed, 12 Aug 2026 14:06:03 +0000</pubDate>
      <link>https://dev.to/stdlib/your-ai-policy-doesnt-have-to-apply-to-you-m6o</link>
      <guid>https://dev.to/stdlib/your-ai-policy-doesnt-have-to-apply-to-you-m6o</guid>
      <description>&lt;p&gt;A confession: stdlib doesn't have an AI-use policy.&lt;/p&gt;

&lt;p&gt;It isn't for lack of trying. There's an open RFC proposing guidance on AI usage—citations to prior art, disclosure requirements, a carve-out for "good first issue"&lt;sup id="fnref1"&gt;1&lt;/sup&gt;—and a draft PR with the policy text, both of which are from December 2025.&lt;sup id="fnref2"&gt;2&lt;/sup&gt; Both are still open. Both are still drafts.&lt;/p&gt;

&lt;p&gt;It's not because AI usage is hypothetical here, either. We've had stdlib core contributors participating in &lt;a href="https://metr.org/blog/2025-07-10-early-2025-ai-experienced-os-dev-study/" rel="noopener noreferrer"&gt;METR's randomized trial&lt;/a&gt; on AI's effect on experienced open-source developers since 2025. Philipp Burckhardt, one of stdlib's co-maintainers, wrote up &lt;a href="https://blog.stdlib.io/reflection-on-the-metr-study-2025/" rel="noopener noreferrer"&gt;his experience&lt;/a&gt; on the project blog, including which tools he uses and how his workflow changed.&lt;sup id="fnref3"&gt;3&lt;/sup&gt; More recently, Karan Anand described &lt;a href="https://blog.stdlib.io/the-codebase-is-the-prompt/" rel="noopener noreferrer"&gt;using the codebase itself as the prompt&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;So: we use these tools, we've said so in public, and (beyond some check boxes in our PR template) we still haven't codified what we expect from anyone else. We've gotten stuck, and one of the sticking points has been an obvious one—discomfort with publishing a rule we're not going to hold ourselves to.&lt;/p&gt;

&lt;p&gt;This is a real objection. It's also, I've come to think, the wrong one, and we're not the only project stalling out on it. The question isn't &lt;em&gt;whether&lt;/em&gt; AI is being used on this project. It's whether there's anything written down that a contributor can read.&lt;/p&gt;

&lt;p&gt;Plenty of projects got past this. Melissa Weber Mendonça keeps a running catalog of what's been published;&lt;sup id="fnref4"&gt;4&lt;/sup&gt; Kate Holterhoff's RedMonk survey was up to eighty-six policies as of April 2026, dimensionalized by stance and primary concern;&lt;sup id="fnref5"&gt;5&lt;/sup&gt; CHAOSS's AI-alignment working group runs a companion list with an incident-and-discussion layer.&lt;sup id="fnref6"&gt;6&lt;/sup&gt;&lt;/p&gt;

&lt;p&gt;Given the scope of the discourse around AI usage in open source, this is a strikingly small corpus—and a young one.&lt;sup id="fnref7"&gt;7&lt;/sup&gt; Nobody is late to a settled consensus here. As James Fredley put it, after a tier-by-tier read from the primary documents: "there is no single industry-wide standard yet."&lt;sup id="fnref8"&gt;8&lt;/sup&gt;&lt;/p&gt;

&lt;p&gt;What the policies themselves agree on is narrower than it first looks. Stance splits three ways—permissive, ban, undecided—and the stated concerns split at least three ways again: code quality, licensing liability, and ethics. Where they &lt;em&gt;do&lt;/em&gt; line up is on something small and specific: &lt;strong&gt;contributors are responsible for understanding what they submit.&lt;/strong&gt; Sean McLellan's case for the &lt;code&gt;Assisted-by:&lt;/code&gt; Git trailer states the reasoning plainly—using &lt;code&gt;Co-authored-by:&lt;/code&gt; claims shared authorship, and with it &lt;em&gt;accountability&lt;/em&gt;. A tool can't hold up its end of that bargain.&lt;sup id="fnref9"&gt;9&lt;/sup&gt; The trailer itself is one lightweight implementation; the claim underneath it is what travels.&lt;/p&gt;

&lt;p&gt;Accountability is what the trailer is &lt;em&gt;for&lt;/em&gt;; &lt;em&gt;responsibility&lt;/em&gt; is what the policies actually ask of you. Part of that is legal clarity—the human is the sole &lt;em&gt;author&lt;/em&gt; of the commit, whatever helped them write it. But what responsibility looks like in practice depends on the author's relationship to the project.&lt;/p&gt;

&lt;p&gt;Two mechanisms tend to get bundled together here. &lt;strong&gt;Disclosure&lt;/strong&gt; is a record-keeping rule—the &lt;code&gt;Assisted-by:&lt;/code&gt; trailer, the &lt;a href="https://github.com/stdlib-js/stdlib/blob/fd17bfa9bc99c1abbd3d99e882cb18bddab42c08/.github/PULL_REQUEST_TEMPLATE.md?plain=1#L37-L55" rel="noopener noreferrer"&gt;AI-assistance section of our own PR template&lt;/a&gt;—and what it produces is provenance: &lt;em&gt;what helped write this&lt;/em&gt;. Everyone takes part in that one. &lt;strong&gt;Gating&lt;/strong&gt; is a permission rule: who may use these tools, on what, with how much latitude. What follows is an argument about the gate, not the disclosure.&lt;/p&gt;

&lt;p&gt;A maintainer who merges a bad change is still there in three months when it breaks. They wrote the module it broke, or reviewed it, and they will be the one bisecting at midnight to find out why the build went red. Responsibility, for them, isn't a claim. It's a structural fact about where they're standing.&lt;/p&gt;

&lt;p&gt;An infrequent contributor who submits a bad change usually isn't there. Not out of bad faith—that's simply what infrequent means. Whatever &lt;em&gt;I take responsibility for this&lt;/em&gt; means when they sign off on their PR, it can't mean quite the same thing as it does for a maintainer. The position it's said from is different.&lt;/p&gt;

&lt;p&gt;So a policy that says &lt;em&gt;you're responsible for what you submit&lt;/em&gt; is asking two structurally different things of two structurally different people, while looking like it asks one thing of everyone. That's roughly where our hypocrisy objection came from. It's also, it turns out, where the objection comes apart.&lt;/p&gt;

&lt;h2&gt;
  
  
  The word for it
&lt;/h2&gt;

&lt;p&gt;There's a word for a rule that behaves this way, and I'm going to use it even though it's unfamiliar, because the precise word does work the familiar ones don't.&lt;/p&gt;

&lt;p&gt;A rule can be &lt;strong&gt;defeasible&lt;/strong&gt;: correct as a default, and &lt;em&gt;properly overridden&lt;/em&gt; when the situations it applies to genuinely differ. The everyday version is &lt;em&gt;the speed limit is 45, except for the ambulance.&lt;/em&gt; The rule holds. The exception is principled. The exception doesn't retroactively invalidate the rule.&lt;sup id="fnref10"&gt;10&lt;/sup&gt;&lt;/p&gt;

&lt;p&gt;The open-source policy version goes something like this:&lt;/p&gt;

&lt;p&gt;There's a naïve consistency heuristic: &lt;em&gt;don't require of others what you wouldn't do yourself&lt;/em&gt;. It's correct for most governance choices. Don't require signed commits if you don't sign yours. Don't require test coverage your codebase doesn't have.&lt;/p&gt;

&lt;p&gt;But it's &lt;em&gt;defeasible&lt;/em&gt; where the situations genuinely differ, and AI-use policy is the clean case. A maintainer using AI owns the fallout, holds the project's mental model, and can recognize when the output is wrong. A first-time contributor using AI may be offloading understanding to the tool—producing work they can't fully vouch for—and has no accountability chain yet. Those are different situations. Different rules are warranted.&lt;/p&gt;

&lt;p&gt;The distinction comes down to what's underneath the rule. &lt;em&gt;Because I own the fallout in a way you don't&lt;/em&gt; is principled. &lt;em&gt;Because I said so&lt;/em&gt; isn't. The trust ledger is what makes the difference legible, and it's why "just be consistent" is worse advice than it sounds.&lt;/p&gt;

&lt;p&gt;The naïve-consistency rule is itself defeasible. That's the whole thing. It's a good default, it does honest work most of the time, and this is one of the situations where the exception is warranted rather than merely convenient.&lt;/p&gt;

&lt;p&gt;Ghostty's &lt;code&gt;AI_POLICY.md&lt;/code&gt; lays out the usual disclosure-and-human-in-the-loop rules for contributions, and then closes with this:&lt;sup id="fnref11"&gt;11&lt;/sup&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;These rules apply only to outside contributions to Ghostty. &lt;br&gt;
Maintainers are exempt from these rules and may use AI tools at their discretion; they've proven themselves trustworthy to apply good judgment.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Two sentences. The first says &lt;em&gt;this policy is for you, the contributor, not for me.&lt;/em&gt; The second says &lt;em&gt;why.&lt;/em&gt; And the why isn't brash authority—it's a claim about accumulated context. &lt;em&gt;They've proven themselves.&lt;/em&gt; You can agree with it, or push back on it, or ask what the proving consisted of.&lt;/p&gt;

&lt;p&gt;So: you (potential contributor) can weigh and evaluate the claim. Naming the asymmetry on the page—rather than writing a symmetric-sounding rule and then quietly not applying it to yourself—is the move. It's two sentences. It's been shipped, in public. Just like &lt;a href="https://blog.stdlib.io/do-you-want-contributors/" rel="noopener noreferrer"&gt;a good &lt;code&gt;CONTRIBUTING.md&lt;/code&gt;&lt;/a&gt;, the honest accounting is a kindness.&lt;/p&gt;

&lt;h2&gt;
  
  
  "You're just formalizing hierarchy"
&lt;/h2&gt;

&lt;p&gt;There's a critique here I too feel the pull of, and it might be part of what keeps projects like ours from making the move. &lt;em&gt;You're building a hierarchy where maintainers get one rule and contributors get another.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The hierarchy is already there, and has been the whole time—and it's not just maintainers vs. contributors, there are often tiers of trust (maintainers is just a useful shorthand). A maintainer with three years of context on a codebase reads a stranger's PR differently from how they read their own or one of a long-time contributor. The maintainer/reviewer knows what's load-bearing, what's under active refactor, which review objections are terminal versus fixable. None of that is malicious; most of it isn't even conscious. It's what accumulated context does. Call it a "trust ledger" (a term I'll return to in a subsequent post).&lt;/p&gt;

&lt;p&gt;What a written policy does is convert an existing asymmetry from &lt;strong&gt;invisible-and-unchallengeable&lt;/strong&gt; into &lt;strong&gt;visible-and-contestable&lt;/strong&gt;. That's an accountability gain, not a hierarchy invention. Writing it down is what makes it something you can be held to.&lt;/p&gt;

&lt;p&gt;If you've been sitting on a policy because the &lt;em&gt;but-I-don't-hold-myself-to-this&lt;/em&gt; objection kept stopping you: note it, acknowledge that it does ethical work worth respecting in most contexts, &lt;em&gt;and&lt;/em&gt; it doesn't apply here in the way it feels like it does. The situations genuinely differ. Different rules are principled when the difference has a reason you can be held to.&lt;/p&gt;

&lt;p&gt;I'm not saying every project should ship one; whether it's right for a given project depends on more than fits in a piece this size. What I &lt;em&gt;am&lt;/em&gt; saying is that this particular reason for stalling—the hypocrisy-avoidance reason, the one that stopped us—is dissolvable.&lt;/p&gt;

&lt;p&gt;Take it, if it's useful. Ours has been open since December; I'm hoping this is a step towards closing it out.&lt;/p&gt;




&lt;p&gt;
    &lt;em&gt;Mara Averick is a developer advocate at &lt;a href="https://quansight.com/" rel="noopener noreferrer"&gt;Quansight&lt;/a&gt; and contributor experience lead for &lt;a href="https://github.com/stdlib-js/stdlib" rel="noopener noreferrer"&gt;stdlib&lt;/a&gt;.&lt;/em&gt;
&lt;/p&gt;




&lt;p&gt;&lt;a href="https://github.com/stdlib-js/stdlib" rel="noopener noreferrer"&gt;stdlib&lt;/a&gt; is an open source software project dedicated to providing a comprehensive suite of robust, high-performance libraries to accelerate your project's development and give you peace of mind knowing that you're depending on expertly crafted, high-quality software.&lt;/p&gt;

&lt;p&gt;If you've enjoyed this post, give us a star 🌟 on &lt;a href="https://github.com/stdlib-js/stdlib" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; and consider &lt;a href="https://opencollective.com/stdlib" rel="noopener noreferrer"&gt;supporting&lt;/a&gt; the project. Your contributions and continued support help ensure the project's long-term success and are greatly appreciated!&lt;/p&gt;

&lt;h2&gt;
  
  
  Acknowledgments
&lt;/h2&gt;

&lt;p&gt;This work was supported in part by the National Science Foundation under &lt;a href="https://www.nsf.gov/awardsearch/showAward?AWD_ID=2449410&amp;amp;HistoricalAwards=false" rel="noopener noreferrer"&gt;Award No. 2449410&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Disclaimer: Any opinions, findings and conclusions or recommendations expressed in this material are those of the author(s) and do not necessarily reflect the views of the National Science Foundation.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;ol&gt;

&lt;li id="fn1"&gt;
&lt;p&gt;stdlib-js/stdlib issue &lt;a href="https://github.com/stdlib-js/stdlib/issues/9347" rel="noopener noreferrer"&gt;#9347&lt;/a&gt;, "[RFC]: Add guidance concerning AI usage"—proposing disclosure of AI assistance in pull requests and issues, excluding &lt;code&gt;good first issue&lt;/code&gt; work from AI-assisted resolution (per the draft PR's rule 5: "these issues are intended to help newcomers learn about the project and gain experience with the mechanics of contributing"), and ruling out model output posted as comments on GitHub or Zulip. Opened December 24, 2025.&amp;nbsp;↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn2"&gt;
&lt;p&gt;stdlib-js/stdlib pull request &lt;a href="https://github.com/stdlib-js/stdlib/pull/9459" rel="noopener noreferrer"&gt;#9459&lt;/a&gt;—the companion draft PR adding the policy text, opened December 31, 2025; still a draft as of this writing.&amp;nbsp;↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn3"&gt;
&lt;p&gt;Philipp Burckhardt, &lt;a href="https://blog.stdlib.io/reflection-on-the-metr-study-2025/" rel="noopener noreferrer"&gt;"Using AI in the development of stdlib,"&lt;/a&gt; Numerical Bits, July 17, 2025—a reflection on stdlib's participation in METR's &lt;a href="https://metr.org/blog/2025-07-10-early-2025-ai-experienced-os-dev-study/" rel="noopener noreferrer"&gt;"Impact of Early-2025 AI on Experienced Open-Source Developer Productivity"&lt;/a&gt; study, in which two stdlib core contributors worked randomized project issues under AI-allowed and AI-disallowed conditions—a snapshot from early 2025; both the models and the surrounding tooling have changed considerably since.&amp;nbsp;↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn4"&gt;
&lt;p&gt;Melissa Weber Mendonça, &lt;a href="https://github.com/melissawm/open-source-ai-contribution-policies" rel="noopener noreferrer"&gt;&lt;code&gt;open-source-ai-contribution-policies&lt;/code&gt;&lt;/a&gt;—a community-sourced catalog framed simply as "a list of policies by different open source projects about how to engage with AI-generated contributions." Roughly 100 project rows as of August 2026, plus separate sections for ongoing discussions and adjacent references.&amp;nbsp;↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn5"&gt;
&lt;p&gt;Kate Holterhoff, &lt;a href="https://redmonk.com/kholterhoff/2026/02/26/generative-ai-policy-landscape-in-open-source/" rel="noopener noreferrer"&gt;"The Generative AI Policy Landscape in Open Source,"&lt;/a&gt; RedMonk, February 26, 2026 (with rolling edits through April 12, 2026). The dimensionalized cross-section—stance, primary concern, disclosure requirement, adoption date—of the same underlying corpus Melissa's catalog and CHAOSS's list track from different registers. Companion &lt;a href="https://oss-ai-policies.netlify.app/" rel="noopener noreferrer"&gt;visualization site&lt;/a&gt;.&amp;nbsp;↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn6"&gt;
&lt;p&gt;CHAOSS AI Alignment Working Group, &lt;a href="https://github.com/chaoss/wg-ai-alignment/blob/main/moderation/README.md" rel="noopener noreferrer"&gt;"Awesome LLM Policy,"&lt;/a&gt; live PR-editable catalog with a nine-part taxonomy—including &lt;em&gt;discussion&lt;/em&gt; threads and &lt;em&gt;incident evidence&lt;/em&gt; as first-class categories alongside finished policy artifacts.&amp;nbsp;↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn7"&gt;
&lt;p&gt;The adjacent body of work is about as short as the list of trackers. Sviatoslav Sydorenko's &lt;a href="https://gist.github.com/webknjaz/1819dd466cc908ff09e2ed4d934455eb" rel="noopener noreferrer"&gt;EuroPython 2026 reference gist&lt;/a&gt; collects the maintainer-side tooling and files both Holterhoff's RedMonk piece and the CHAOSS list under "Surveys of the policy landscape"; the three trackers each reference the other two.&amp;nbsp;↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn8"&gt;
&lt;p&gt;James Fredley, &lt;a href="https://allthingsopen.org/articles/open-source-ai-contributions-assisted-by-git-trailer-standard" rel="noopener noreferrer"&gt;"Assisted-by: How open source projects are drawing the line on AI contributions,"&lt;/a&gt; All Things Open, May 11, 2026. A tier-by-tier read from the primary documents—QEMU, Gentoo, and NetBSD at the total-ban end; LLVM, Fedora, the Linux Kernel, Apache, OpenInfra, OpenTelemetry, and Rocky Linux across the permissive-with-disclosure middle—arguing that &lt;code&gt;Assisted-by:&lt;/code&gt; is settling in as the de facto convention over &lt;code&gt;Co-authored-by:&lt;/code&gt;, which "implies legal personhood." Fredley is chair of the Apache Grails PMC.&amp;nbsp;↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn9"&gt;
&lt;p&gt;Sean McLellan, &lt;a href="https://www.baristalabs.io/blog/ai-assisted-commits-need-provenance-trailer" rel="noopener noreferrer"&gt;"Assisted-by git trailer: provenance for AI-assisted commits,"&lt;/a&gt; Barista Labs, June 20, 2026. Key line: "Co-authorship says this entity shares authorship and accountability." Pulled into stdlib's own trailer discussion by Athan Reines' (&lt;a href="https://github.com/stdlib-js/stdlib/issues/9347#issuecomment-5052552276" rel="noopener noreferrer"&gt;comment&lt;/a&gt; on issue &lt;a href="https://github.com/stdlib-js/stdlib/issues/9347" rel="noopener noreferrer"&gt;#9347&lt;/a&gt;)—not necessarily the line's original point of entry into OSS AI-policy discourse, just where this piece's thread of it traces back to.&amp;nbsp;↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn10"&gt;
&lt;p&gt;The term has a longer life outside software. H. L. A. Hart, &lt;a href="http://www.jstor.org/stable/4544455" rel="noopener noreferrer"&gt;"The Ascription of Responsibility and Rights,"&lt;/a&gt; &lt;em&gt;Proceedings of the Aristotelian Society, New Series&lt;/em&gt; 49 (1948–1949): 171–194, is the canonical statement of the legal version—rules that hold unless a specific exception overrides them. John L. Pollock, &lt;a href="https://doi.org/10.1207/s15516709cog1104_4" rel="noopener noreferrer"&gt;"Defeasible Reasoning,"&lt;/a&gt; &lt;em&gt;Cognitive Science&lt;/em&gt; 11, no. 4 (1987): 481–518, formalizes the everyday version: how people reason with rough-and-ready rules that new information can &lt;em&gt;defeat&lt;/em&gt;, and what it takes for an override to count.&amp;nbsp;↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn11"&gt;
&lt;p&gt;Ghostty, &lt;a href="https://github.com/ghostty-org/ghostty/blob/main/AI_POLICY.md" rel="noopener noreferrer"&gt;&lt;code&gt;AI_POLICY.md&lt;/code&gt;&lt;/a&gt; (fetched 2026-08-03). Iterated in public via PR &lt;a href="https://github.com/ghostty-org/ghostty/pull/8289" rel="noopener noreferrer"&gt;#8289&lt;/a&gt; (initial disclosure rules, August 2025) and PR &lt;a href="https://github.com/ghostty-org/ghostty/pull/10412" rel="noopener noreferrer"&gt;#10412&lt;/a&gt; (hardened into the standalone policy, January 2026).&amp;nbsp;↩&lt;/p&gt;
&lt;/li&gt;

&lt;/ol&gt;

</description>
      <category>devrel</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Do you WANT contributors?</title>
      <dc:creator>Mara Averick</dc:creator>
      <pubDate>Thu, 23 Jul 2026 16:12:00 +0000</pubDate>
      <link>https://dev.to/stdlib/do-you-want-contributors-37lo</link>
      <guid>https://dev.to/stdlib/do-you-want-contributors-37lo</guid>
      <description>&lt;p&gt;My open-source origin story goes something like this: &lt;em&gt;I fixed a typo, tweeted some stuff, tripped, fell, and found myself in the inner circle of a large-scale OSS ecosystem with an active, supportive community!&lt;/em&gt; It's trite but true—neither universal nor unique. But "inner circle of a large-scale OSS ecosystem" is where I landed, and it's colored a lot of what I've written since—including an implicit assumption that you (person who is reading this) are trying to build an ecosystem-scale project too.&lt;/p&gt;

&lt;p&gt;POSE (Pathways to Enable Open-Source Ecosystems) has it right there in the name. It expressly describes this shape as the first item in the list of activities it's meant to support:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Building and maintaining a distributed community of external contributors who will actively participate in the ongoing development of the open-source product.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;However, this isn't the only (or even the most common) experience for open-source maintainers or projects. Most projects run on one to three active maintainers.&lt;sup id="fnref1"&gt;1&lt;/sup&gt; Solo isn't a failure mode; it's the default.&lt;/p&gt;

&lt;p&gt;There's a wide spectrum of how folks define open source: from "it's just a license" to "radically open and participatory." For the rest of this post to land, I need you to accept the license-end as at least legitimate—not right, necessarily, just legitimate. There's no need to re-litigate the decades-long disagreement in order to say that you, as a maintainer, get to decide the bounds of participation in your project. &lt;/p&gt;

&lt;p&gt;An open-source project's first few artifacts are click-through: license (checkbox), &lt;code&gt;README&lt;/code&gt; (template), &lt;code&gt;.gitignore&lt;/code&gt; (whatever your framework spits out). Then you hit &lt;code&gt;CONTRIBUTING.md&lt;/code&gt;—the first artifact that actually asks you what you want. Sure, there are best practices and helpful guides for you to follow, but it's where your project starts having opinions instead of defaults. So it's worth pausing and really unpacking what you want yours to look like. It's more than the mechanics of filing issues and submitting pull requests: &lt;code&gt;CONTRIBUTING.md&lt;/code&gt; is the foundational framework for the relationship between your project and the unnamed masses who may or may not encounter it. &lt;/p&gt;

&lt;p&gt;In fairness, the bare-bones version of contributor guidelines may work just fine for you. Or you and your project may have a version that &lt;em&gt;was&lt;/em&gt; working in the past, but no longer feels sufficient.&lt;/p&gt;

&lt;p&gt;For years, I had a Venn diagram I used in talks to illustrate what I called the "FOSS happy place":&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9l1ldkifeva125pw0s5b.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9l1ldkifeva125pw0s5b.png" alt="Two overlapping circles labeled " width="799" height="527"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It wasn't a lie—it reflected my early experience as a contributor to open source (even if my contributions weren't code-shaped). It's still not "wrong," per se. But it &lt;em&gt;is&lt;/em&gt; guilty of radical oversimplification—not &lt;em&gt;quite&lt;/em&gt; discretizing a continuous variable, but &lt;em&gt;definitely&lt;/em&gt; improper dimensionality reduction. Critical features, like time, remuneration, and changing conditions/priorities are nowhere to be seen. &lt;/p&gt;

&lt;p&gt;I'm not the only one whose journey was typo fix → full-time job/way of life. Kent C. Dodds (among the best in the game of open-source developer experience, in my opinion) describes a similar origin story in &lt;a href="https://kentcdodds.com/blog/how-getting-into-open-source-has-been-awesome-for-me" rel="noopener noreferrer"&gt;How getting into Open Source has been awesome for me&lt;/a&gt; (2020). I mention him for two reasons, and neither is throwing him under the bus: one, it's not just me; two, it's not dev-rel delusion—because (unlike me) he's a lines-of-code, PR-reviews, engineer-shaped maintainer of a widely used open-source project (&lt;a href="https://testing-library.com/" rel="noopener noreferrer"&gt;Testing Library&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;A career in open source isn't always a heart-eyes-emoji experience. Evan Czaplicki gave a great (aptly named) talk at &lt;em&gt;Strange Loop&lt;/em&gt; 2018:  &lt;a href="https://youtu.be/o_4EX4dPppA?si=5PuZgCgQrGFxlOYn" rel="noopener noreferrer"&gt;The Hard Parts of Open Source&lt;/a&gt;. The challenges he describes seem somewhat inherent to the craft: you can't please everyone, conflicts will arise, things that once motivated you will be deeply draining at times. Anyone who's struggled through a group project in school will recognize the shape. None of that friction is new—LLMs didn't create maintainer burnout. In a 2019 piece by Nadia Eghbal, &lt;a href="https://increment.com/open-source/the-rise-of-few-maintainer-projects/" rel="noopener noreferrer"&gt;"The rise of few-maintainer projects"&lt;/a&gt;, she wrote:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The salient issue for maintainers today is less about growing contributor numbers and more about navigating the flow of developers who are clamoring for their time.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Sounds familiar, right? AI has ratcheted things up. Maintainers are seriously rethinking what their relationship to their projects and users looks like. And here's where I return to &lt;code&gt;CONTRIBUTING.md&lt;/code&gt; because &lt;em&gt;this&lt;/em&gt; is a place where you get to be explicit about the choices you're making. Even if nobody reads every word (and most people don't read &lt;code&gt;CONTRIBUTING&lt;/code&gt; files at all), writing one is how you set your boundaries in the first place. A &lt;code&gt;CONTRIBUTING.md&lt;/code&gt; that reads:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;No thanks! I'm gonna go it alone.
But feel free to fork and do whatever you like!
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;may not be what you're used to seeing (in fairness, that sentiment is likely carried through the &lt;em&gt;absence&lt;/em&gt; of a CONTRIBUTING file), but it's legitimate. Or, maybe you think it isn't. Maybe it's anathema to open source, &lt;strong&gt;BUT&lt;/strong&gt; it &lt;em&gt;is&lt;/em&gt; a move that &lt;em&gt;someone&lt;/em&gt; could make.&lt;/p&gt;

&lt;p&gt;Am I recommending it? No. But I'm not you. I'm not everyone. And, importantly, it forces you to acknowledge that you get to set your boundaries and expectations. Again, this is an oversimplification. If you're maintaining a mature project with a set governance pattern and change-management process, waking up and burning your contributing process to the ground is likely to have serious fallout. But, that doesn't mean that you can't experiment (especially if the status quo feels like you're headed off a cliff.)&lt;/p&gt;

&lt;p&gt;Certain projects have, by design, made the bar for contribution high for legitimate reasons. Debian's already the perfect example of a working gatekeeping mechanism that's high-demand. You need a sponsor, you find a mentor, you meet in person with someone who can vouch for you—as Nadia Eghbal put it, &lt;em&gt;"Debian's process is built on the need for trust."&lt;/em&gt;&lt;sup id="fnref2"&gt;2&lt;/sup&gt;&lt;/p&gt;

&lt;p&gt;But Debian is one shape among several. Athan Reines pitched a taxonomy for this: not &lt;em&gt;"how open is your project"&lt;/em&gt; on a slider, but &lt;em&gt;"what shape of project are you actually running?"&lt;/em&gt; He described four archetypes: the walled castle, the clique, the university, and the hippie commune.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The walled castle (or black box).&lt;/strong&gt; You get the code, but the drawbridge stays up. SQLite is the cleanest live example. Their copyright page has a section literally titled "Open-Source, not Open-Contribution," which says:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;SQLite is open-source, meaning that you can make as many copies of it as you want and do whatever you want with those copies, without limitation. But SQLite is not open-contribution...the project does not accept patches from random people on the internet.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You can't misread that. It's on their homepage. This archetype fits when the maintainer has a firm boundary they don't want to soften—a totally legitimate choice. It also fits when there's an author who's just done. The question: Do you want contributors? The answer: No.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The clique.&lt;/strong&gt; You need to know the right people to get the secret password. OpenBSD is the best not-pejoratively-clique example I could find. Their &lt;a href="https://www.openbsd.org/hackathons.html" rel="noopener noreferrer"&gt;hackathons page&lt;/a&gt; says, plainly: "Hackathon attendees come by invitation only. Some new people in the community who show promise are sometimes invited to see if they have what it takes." Linux kernel subsystems run on the same social logic at scale: the kernel docs describe a "chain of trust" where pull requests from unknown developers are received warily by subsystem maintainers until they've earned their way in. This archetype fits when the cost of misplaced trust is high and the mechanism for establishing trust is "does someone I already trust vouch for you."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The university.&lt;/strong&gt; If you do your homework and score high enough on your exams, we'll admit you. Debian's process, above, is the archetypal case: the sponsor and mentor are part of a longer sequence that includes an application manager, a Philosophy &amp;amp; Procedures exam, a Tasks &amp;amp; Skills exam, and then DAM approval (or not). Their own docs are unusually honest about the deal: &lt;em&gt;"the whole NM process is very strict and thorough. This is not meant to discourage people... but it does explain why the New Member process takes so much time."&lt;/em&gt; Kubernetes runs the most numerically explicit ladder I've seen: Member (sponsored by two reviewers from different companies), Reviewer (primary on 5+ PRs, reviewed 20+ substantial ones), Approver (30+ merged, nominated by a subproject owner). If you want the stdlib-adjacent case, &lt;a href="https://www.pyopensci.org/" rel="noopener noreferrer"&gt;pyOpenSci&lt;/a&gt; has documented onboarding flows for reviewers and editors: guest-editor probation, editor-in-chief nominations, a peer-review process modeled on academic journals. This archetype fits infrastructure that's load-bearing enough that mistakes are expensive to unwind, and it works if the syllabus is real. (It fails, badly, if the syllabus is a rebrand of "vibes-based homework we won't tell you the rules of.")&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The hippie commune.&lt;/strong&gt; Do you have a pulse? Yes? You're in. freeCodeCamp basically checks for a pulse and hands you a &lt;code&gt;first timers only&lt;/code&gt; label. Their whole cultural stance is "let us help you land your first PR." I've been part of projects like this, and it's actually onerous in its own way. But, they're where a lot of folks get started. Without these, the rest of the spectrum loses its future contributors and maintainers. MDN Web Docs runs the same posture on the docs side: their contributing guide is basically "there's a place for you here, even if your grammar isn't perfect." (Wikipedia is the mother of this archetype but isn't code-shaped, so treat that as a cultural reference, not a technical one.) This archetype fits projects where the marginal contribution has low blast radius, and the real bottleneck is reach or discoverability, not integration cost. It also fits projects whose &lt;em&gt;goal&lt;/em&gt; is teaching people to contribute, where the code is almost a byproduct of the mentorship.&lt;/p&gt;

&lt;p&gt;In reality, there is, of course, a spectrum—and shades of gray as well. A project doesn't necessarily have to operate in a single mode, either. SQLite runs some clique-shaped review practices under its walled-castle surface. Debian's university process has real clique-shaped social realities inside it. Any given project can occupy more than one archetype at once—a codebase that's walled-castle at the core and hippie commune at the docs layer, say. And most projects drift between shapes as they age.&lt;/p&gt;

&lt;p&gt;I also want to be clear that you're ALLOWED TO MOVE where your project is at. Is hippie-commune mode failing you in the age of AI slop? Maybe you run an experiment with a more rigorous onboarding (or have folks who have already contributed to the project mentor newcomers through their first PR so that the onus doesn't always fall on the maintainer.)&lt;/p&gt;

&lt;p&gt;There is no more a platonic form for a perfect &lt;code&gt;CONTRIBUTING.md&lt;/code&gt; than there is for a universally "perfect" job. The fit is between your project and its contributors. What works for one can be exactly wrong for another.&lt;/p&gt;

&lt;p&gt;But here's why the taxonomy belongs in a piece about what you actually want: it forces you to say the thing out loud. &lt;em&gt;"I want a hippie commune"&lt;/em&gt; is a different maintainer future than &lt;em&gt;"I want a university,"&lt;/em&gt; and both are different from &lt;em&gt;"I want to keep the code out here but the process in here."&lt;/em&gt; You can't dodge the question by writing a &lt;code&gt;CONTRIBUTING.md&lt;/code&gt; that just describes issue templates. The people who read it (the ~7 who will) can tell which archetype you're aiming for even when you haven't named it. Might as well spell it out.&lt;/p&gt;

&lt;p&gt;There are pros and cons to the types of friction you introduce—selectively inviting who can interact, permanently banning a suspected peddler of AI slop, toggling off the "Issues" section of a repository. But, if the relationship (that between a maintainer and contributors) is evolving in such a way that it feels like it cannot hold, that's its own type of failure. Neither the maintainer nor "the community" are to &lt;em&gt;blame&lt;/em&gt; when things end in a silent resignation or a public crashout—but maybe they can be avoided by experimenting with things you hadn't previously considered as being "worth it." &lt;/p&gt;

&lt;p&gt;Like my old FOSS-happy-place Venn diagram, the chart below doesn't capture the n-dimensional reality of working in open source. But I'd like to think it's an improvement. On the x-axis you have maintainer/project (dis)comfort. And the y-axis is something akin to ROI (return on investment)—demand from contributors/community/ecosystem.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F3h9r178elo1niefmyirc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F3h9r178elo1niefmyirc.png" alt="A hand-drawn chart on graph paper. The vertical axis runs from " width="800" height="680"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The catch is that you don't really know where an experiment lands on this chart until you try it—not how much it'll cost you (the x-axis), and not what you'll get back (the y-axis). That doesn't mean you have to try everything. But it's worth paying attention to where things actually fall, rather than where you assumed they would.&lt;/p&gt;

&lt;p&gt;Here are some things that we're trying (or considering taking for a spin):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Code review as the alignment step.&lt;/strong&gt; Ed Yang makes the case that &lt;a href="https://blog.ezyang.com/2025/12/code-review-as-human-alignment-in-the-era-of-llms/" rel="noopener noreferrer"&gt;code review is where humans do the alignment work&lt;/a&gt; now. As AI takes on more of the implementation, the reviewer's job shifts away from line-by-line mechanics and toward the big-picture questions—does this fit, should it exist, is this the shape we want?&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Using AI as a market-research companion, not a PR-generation machine.&lt;/strong&gt; Instead of &lt;em&gt;AI writes the pull request&lt;/em&gt;, it's &lt;em&gt;AI helps you see the landscape&lt;/em&gt;. Feed it the parallel projects—for us that's NumPy, SciPy, Math.js, and friends—let it aggregate the patterns everyone else has converged on, and then &lt;em&gt;you&lt;/em&gt; decide what fits. Same tool, pointed at &lt;em&gt;what should we build&lt;/em&gt; instead of &lt;em&gt;let's crank out code&lt;/em&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Livestreamed PR review.&lt;/strong&gt; Twitch, Discord, YouTube—pick your poison. Someone watches a maintainer work through a review in real time. The discomfort scales hard depending on who you are (some folks would genuinely rather chew glass than do their job on camera), but it puts other humans in the room at the moment you're deciding something. And it teaches in both directions: people watching learn what makes a good contribution &lt;em&gt;and&lt;/em&gt; what being a maintainer actually takes. It's knowledge capture without the curation—which means it might surface insights you never knew people wanted.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of this is guaranteed to work. Most of what gets written about open-source communities and contributor experience gets written after the fact—after something worked. That means there's a bottom drawer somewhere full of the things people tried that didn't (those posts mostly don't get written.) And even the experiments that did work worked in the &lt;em&gt;specific conditions&lt;/em&gt; of that project—its size, its maintainers, its users, its moment. Different projects, different fits. This isn't Wills and Trusts. You can iterate. Your &lt;code&gt;CONTRIBUTING.md&lt;/code&gt; can change. Your governance can change. Your mind can change. Whatever you want to do is okay—just own it. And if you're trying something (the thing that's working, or the thing that isn't) come tell us about it. We're figuring it out too.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sources
&lt;/h2&gt;




&lt;p&gt;
    &lt;em&gt;Mara Averick is a developer advocate at &lt;a href="https://quansight.com/" rel="noopener noreferrer"&gt;Quansight&lt;/a&gt; and contributor experience lead for &lt;a href="https://github.com/stdlib-js/stdlib" rel="noopener noreferrer"&gt;stdlib&lt;/a&gt;.&lt;/em&gt;
&lt;/p&gt;




&lt;p&gt;&lt;a href="https://github.com/stdlib-js/stdlib" rel="noopener noreferrer"&gt;stdlib&lt;/a&gt; is an open source software project dedicated to providing a comprehensive suite of robust, high-performance libraries to accelerate your project's development and give you peace of mind knowing that you're depending on expertly crafted, high-quality software.&lt;/p&gt;

&lt;p&gt;If you've enjoyed this post, give us a star 🌟 on &lt;a href="https://github.com/stdlib-js/stdlib" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; and consider &lt;a href="https://opencollective.com/stdlib" rel="noopener noreferrer"&gt;supporting&lt;/a&gt; the project. Your contributions and continued support help ensure the project's long-term success and are greatly appreciated!&lt;/p&gt;

&lt;h2&gt;
  
  
  Acknowledgments
&lt;/h2&gt;

&lt;p&gt;This work was supported in part by the National Science Foundation under &lt;a href="https://www.nsf.gov/awardsearch/showAward?AWD_ID=2449410&amp;amp;HistoricalAwards=false" rel="noopener noreferrer"&gt;Award No. 2449410&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Disclaimer: Any opinions, findings and conclusions or recommendations expressed in this material are those of the author(s) and do not necessarily reflect the views of the National Science Foundation.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;ol&gt;

&lt;li id="fn1"&gt;
&lt;p&gt;Linux Foundation, OpenSSF, and Harvard LISH. &lt;em&gt;Census III of Free and Open Source Software.&lt;/em&gt; December 2024. &lt;a href="https://www.linuxfoundation.org/hubfs/LF%20Research/lfr_censusiii_120424a.pdf?hsLang=en" rel="noopener noreferrer"&gt;https://www.linuxfoundation.org/hubfs/LF%20Research/lfr_censusiii_120424a.pdf?hsLang=en&lt;/a&gt;; GitHub. &lt;em&gt;Octoverse 2025.&lt;/em&gt; &lt;a href="https://github.blog/news-insights/octoverse/octoverse-a-new-developer-joins-github-every-second-as-ai-leads-typescript-to-1/" rel="noopener noreferrer"&gt;https://github.blog/news-insights/octoverse/octoverse-a-new-developer-joins-github-every-second-as-ai-leads-typescript-to-1/&lt;/a&gt;.&amp;nbsp;↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn2"&gt;
&lt;p&gt;Nadia Eghbal. "The Rise of Few-Maintainer Projects." &lt;em&gt;Increment&lt;/em&gt;, 2019. &lt;a href="https://increment.com/open-source/the-rise-of-few-maintainer-projects/" rel="noopener noreferrer"&gt;https://increment.com/open-source/the-rise-of-few-maintainer-projects/&lt;/a&gt;.&amp;nbsp;↩&lt;/p&gt;
&lt;/li&gt;

&lt;/ol&gt;

</description>
      <category>devrel</category>
      <category>opensource</category>
    </item>
  </channel>
</rss>
