<?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: puffball1567</title>
    <description>The latest articles on DEV Community by puffball1567 (@puffball1567).</description>
    <link>https://dev.to/puffball1567</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%2F4028036%2F8a0f794e-59fe-4876-b5ef-a1b7e1d8ed01.JPG</url>
      <title>DEV Community: puffball1567</title>
      <link>https://dev.to/puffball1567</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/puffball1567"/>
    <language>en</language>
    <item>
      <title>Object-Oriented Programming for Beginners: Build a Character Health System</title>
      <dc:creator>puffball1567</dc:creator>
      <pubDate>Sat, 03 Oct 2026 14:00:29 +0000</pubDate>
      <link>https://dev.to/puffball1567/object-oriented-programming-for-beginners-build-a-character-health-system-114i</link>
      <guid>https://dev.to/puffball1567/object-oriented-programming-for-beginners-build-a-character-health-system-114i</guid>
      <description>&lt;p&gt;A game character takes 30 damage, reducing their health from 100 to 70. A variable and a subtraction can handle that. But what happens when there are two characters? Where should we handle damage that exceeds the remaining health, or a negative value passed by mistake?&lt;/p&gt;

&lt;p&gt;This article introduces object-oriented programming through a small character health program. In this first installment, we will group data with operations, create separate characters from the same definition, and put the rules for changing health in one place.&lt;/p&gt;

&lt;p&gt;The examples use &lt;a href="https://puffball1567.github.io/kinmokusei/" rel="noopener noreferrer"&gt;Kinmokusei&lt;/a&gt;, a programming language I am developing. It uses TypeScript-inspired syntax and compiles to Go source code. We will explain the class and method syntax as we use it. You do not need prior Kinmokusei experience, but you should be familiar with variables, functions, and &lt;code&gt;if&lt;/code&gt; statements.&lt;/p&gt;

&lt;p&gt;We will start by managing HP with variables and a function, then gradually rewrite the program using a class. At the end of this article, you will find the complete program and instructions for running it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Start with variables and a function
&lt;/h2&gt;

&lt;p&gt;Begin with a character's name and current health:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;heroName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Aki&lt;/span&gt;&lt;span class="dl"&gt;"&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;heroHp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;int&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;heroName&lt;/code&gt; holds the name; &lt;code&gt;heroHp&lt;/code&gt; holds the current hit points, or HP. &lt;code&gt;int&lt;/code&gt; means an integer. We use &lt;code&gt;let&lt;/code&gt; for HP because we will assign a new value, and &lt;code&gt;const&lt;/code&gt; for a name we do not reassign in this example.&lt;/p&gt;

&lt;p&gt;We can put the damage calculation in a function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;damage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;hp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;int&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;int&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;amount&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;hp&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;amount&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="nx"&gt;hp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;hp&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;amount&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;&lt;code&gt;hp&lt;/code&gt; is the current health, and &lt;code&gt;amount&lt;/code&gt; is the damage. The final &lt;code&gt;: int&lt;/code&gt; says the function returns an integer. Zero or negative damage leaves health unchanged; damage at least as large as the remaining HP returns zero. Otherwise, we subtract.&lt;/p&gt;

&lt;p&gt;The function returns a new number. To update the caller's variable, assign that result:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;heroHp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;damage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;heroHp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// heroHp is now 70.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With two characters, the code inside &lt;code&gt;main&lt;/code&gt; might look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;heroName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Aki&lt;/span&gt;&lt;span class="dl"&gt;"&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;heroHp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;int&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;partnerName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Haru&lt;/span&gt;&lt;span class="dl"&gt;"&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;partnerHp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;int&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;heroHp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;damage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;heroHp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;heroName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;heroHp&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;partnerName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;partnerHp&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;fmt.Println&lt;/code&gt; prints values so we can inspect them. The complete program later includes the import from Go's standard library. This example prints:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Aki 70
Haru 100
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A function already lets us share the damage calculation without introducing a class. However, names and health values are separate variables. The caller keeps track of which values belong together. Writing &lt;code&gt;partnerHp = damage(heroHp, 30)&lt;/code&gt; would assign a result based on the hero's health to the partner. Both values are integers, so their types alone cannot catch this mix-up.&lt;/p&gt;

&lt;p&gt;Let's try grouping a character's name and health together, then asking that character to take damage.&lt;/p&gt;

&lt;h2&gt;
  
  
  Define a character with a class
&lt;/h2&gt;

&lt;p&gt;An &lt;strong&gt;object&lt;/strong&gt;, in this example, holds state such as a name and HP and offers operations using that state. A &lt;strong&gt;class&lt;/strong&gt; defines what those objects hold and which operations they provide.&lt;/p&gt;

&lt;p&gt;Start with a class named &lt;code&gt;Character&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Character&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;hp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;int&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;100&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;Variables belonging to an object, such as &lt;code&gt;name&lt;/code&gt; and &lt;code&gt;hp&lt;/code&gt;, are called &lt;strong&gt;fields&lt;/strong&gt;. &lt;code&gt;string&lt;/code&gt; represents text, and &lt;code&gt;int&lt;/code&gt; represents an integer. &lt;code&gt;public&lt;/code&gt; permits access from outside the class; &lt;code&gt;private&lt;/code&gt; restricts access to the class's implementation. We expose the name while keeping health under the class's control. The public &lt;code&gt;name&lt;/code&gt; field can also be changed from outside in this version.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;constructor&lt;/code&gt; contains the initialization code that runs when an object is created. Here, it stores the supplied name in a field and sets HP to 100. This initialization mechanism is called a &lt;strong&gt;constructor&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;this&lt;/code&gt; refers to the object being initialized or operated on. In &lt;code&gt;this.name = name&lt;/code&gt;, the left side is the object's field; the right side is the constructor argument. Their names match, but they serve different roles.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;new Character("Aki")
        |
        +-- constructor argument name receives "Aki"
                this.name = name  -&amp;gt; store the name "Aki"
                this.hp = 100     -&amp;gt; start at 100 HP
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Create two separate instances
&lt;/h2&gt;

&lt;p&gt;Writing &lt;code&gt;class Character&lt;/code&gt; defines the type; it does not create the hero or partner. Use &lt;code&gt;new&lt;/code&gt; to create actual objects:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;hero&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Character&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Aki&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;partner&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Character&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Haru&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;An individual object created from a class is an &lt;strong&gt;instance&lt;/strong&gt; of that class. &lt;code&gt;hero&lt;/code&gt; and &lt;code&gt;partner&lt;/code&gt; each refer to a &lt;code&gt;Character&lt;/code&gt; instance. Calling &lt;code&gt;new&lt;/code&gt; twice creates two characters, each with their own name and HP.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Character class
  Defines the name and HP fields
          |
          +-- new Character("Aki")  -&amp;gt; hero    -&amp;gt; Aki  / HP 100
          +-- new Character("Haru") -&amp;gt; partner -&amp;gt; Haru / HP 100
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;const&lt;/code&gt; means we cannot reassign the variable to a different object. It does not freeze the character's HP. Also, &lt;code&gt;const other = hero&lt;/code&gt; would not create a new character: it would add another reference to the same one. The separate &lt;code&gt;new&lt;/code&gt; calls are what create separate state.&lt;/p&gt;

&lt;h2&gt;
  
  
  Add methods that work on the character
&lt;/h2&gt;

&lt;p&gt;Next, add the damage operation inside &lt;code&gt;Character&lt;/code&gt;. A function defined on a class and called on an object is a &lt;strong&gt;method&lt;/strong&gt;. Insert these two methods after the constructor, before the class's closing brace:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;takeDamage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;int&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;amount&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;amount&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hp&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="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hp&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getHp&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nx"&gt;int&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hp&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;&lt;code&gt;takeDamage&lt;/code&gt; receives only the damage amount. The target object already holds its current health in &lt;code&gt;this.hp&lt;/code&gt;. Its return type, &lt;code&gt;void&lt;/code&gt;, means it does not return a value to the caller. Instead, it updates the object's state.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;getHp&lt;/code&gt; returns the current HP as an integer. It lets callers inspect health without changing it.&lt;/p&gt;

&lt;p&gt;To damage the hero, call:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;hero&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;takeDamage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The expression before the dot, &lt;code&gt;hero&lt;/code&gt;, identifies the target. During this method call, &lt;code&gt;this&lt;/code&gt; refers to the character referenced by &lt;code&gt;hero&lt;/code&gt;. Only that character's health changes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Before                             After hero.takeDamage(30)
hero    -&amp;gt; Aki  / HP 100            hero    -&amp;gt; Aki  / HP 70
partner -&amp;gt; Haru / HP 100            partner -&amp;gt; Haru / HP 100
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The function version used &lt;code&gt;heroHp = damage(heroHp, 30)&lt;/code&gt;: pass in the current value and assign the result. The method version uses &lt;code&gt;hero.takeDamage(30)&lt;/code&gt;: identify an object and ask it to perform an operation. We can still call a method on the wrong character, but the relationship between a name, its HP, and the health-changing operation is now part of the program's structure.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keep health changes within the rules
&lt;/h2&gt;

&lt;p&gt;Why make &lt;code&gt;hp&lt;/code&gt; private? Code outside the class cannot directly write:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// This is a compile error. Do not add it to the finished program.&lt;/span&gt;
&lt;span class="nx"&gt;hero&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;999&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Callers use &lt;code&gt;hero.takeDamage(...)&lt;/code&gt; instead. That entry point applies these rules:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Damage amount&lt;/th&gt;
&lt;th&gt;Action&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Zero or negative&lt;/td&gt;
&lt;td&gt;Leave HP unchanged&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;At least the remaining HP&lt;/td&gt;
&lt;td&gt;Set HP to zero&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Any other positive amount&lt;/td&gt;
&lt;td&gt;Subtract it from HP&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;If a character has 70 HP and takes 200 damage, we compare before subtracting and set HP to zero. Subtracting a negative number would otherwise increase health, so the first condition returns without making a change. Starting at 100 in the constructor and updating only through this method keeps HP between 0 and 100.&lt;/p&gt;

&lt;p&gt;Grouping state with the operations that manage it, and defining what callers can access, is &lt;strong&gt;encapsulation&lt;/strong&gt;. Merely writing &lt;code&gt;private&lt;/code&gt; does not automatically enforce a rule. The class must implement valid state changes and provide the operations through which callers use them.&lt;/p&gt;

&lt;p&gt;This small program could also be designed with functions and separate data structures. The class expresses a particular grouping: whose state we are working with, and how that state may change. We can explore this part of object-oriented programming without introducing inheritance.&lt;/p&gt;

&lt;h2&gt;
  
  
  Save and run the complete program
&lt;/h2&gt;

&lt;p&gt;Create a directory named &lt;code&gt;character-health&lt;/code&gt; and save the following code as &lt;code&gt;main.km&lt;/code&gt;. This assembles the pieces above and adds a &lt;code&gt;main&lt;/code&gt; function to exercise them.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;character-health/
└── main.km
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;import go fmt from "fmt"&lt;/code&gt; imports Go's standard formatting library. We use &lt;code&gt;fmt.Println&lt;/code&gt; to print the results. Execution starts in &lt;code&gt;main&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;go&lt;/span&gt; &lt;span class="nx"&gt;fmt&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;fmt&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Character&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;hp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;int&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;takeDamage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;int&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;amount&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;amount&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hp&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="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hp&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getHp&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nx"&gt;int&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hp&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;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;hero&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Character&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Aki&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;partner&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Character&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Haru&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;hero&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;hero&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getHp&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
  &lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;partner&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;partner&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getHp&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

  &lt;span class="nx"&gt;hero&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;takeDamage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;After 30 damage:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;hero&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;hero&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getHp&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
  &lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Partner:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;partner&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;partner&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getHp&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

  &lt;span class="nx"&gt;hero&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;takeDamage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;After 200 damage:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;hero&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;hero&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getHp&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

  &lt;span class="nx"&gt;hero&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;takeDamage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;After negative damage:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;hero&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;hero&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getHp&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;Follow the &lt;a href="https://puffball1567.github.io/kinmokusei/learn/installation" rel="noopener noreferrer"&gt;official installation guide&lt;/a&gt; to install Kinmokusei's CLI and its supported Go toolchain. The CLI is named &lt;code&gt;keika&lt;/code&gt;. From &lt;code&gt;character-health&lt;/code&gt;, run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;keika version
keika check main.km
keika run main.km
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;check&lt;/code&gt; validates the source; &lt;code&gt;run&lt;/code&gt; compiles and executes it. I ran this sample with &lt;code&gt;keika v0.4.3&lt;/code&gt; and &lt;code&gt;go1.23.5 linux/amd64&lt;/code&gt;, producing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Aki 100
Haru 100
After 30 damage: Aki 70
Partner: Haru 100
After 200 damage: Aki 0
After negative damage: Aki 0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Both characters start at 100 HP. After the hero takes 30 damage, their HP becomes 70 while the partner stays at 100. Another 200 damage reduces the hero's HP to zero, and the negative damage leaves it at zero. The output makes both independent instance state and the update rules visible.&lt;/p&gt;

&lt;p&gt;Try changing &lt;code&gt;hero.takeDamage(30)&lt;/code&gt; to &lt;code&gt;partner.takeDamage(30)&lt;/code&gt;. Which character's HP changes now? Then test damage amounts of 0, 100, and 101 on a newly created character with 100 HP to exercise the different branches.&lt;/p&gt;

&lt;h2&gt;
  
  
  Connect the vocabulary to the code
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Term&lt;/th&gt;
&lt;th&gt;Meaning in this program&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Class&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;Character&lt;/code&gt;, the definition of state and operations&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Object or instance&lt;/td&gt;
&lt;td&gt;An individual character created with &lt;code&gt;new Character(...)&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Field&lt;/td&gt;
&lt;td&gt;State held by each character, such as &lt;code&gt;name&lt;/code&gt; or &lt;code&gt;hp&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Constructor&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;constructor&lt;/code&gt;, which sets the name and initial HP&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Method&lt;/td&gt;
&lt;td&gt;An operation called on a character, such as &lt;code&gt;takeDamage&lt;/code&gt; or &lt;code&gt;getHp&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Encapsulation&lt;/td&gt;
&lt;td&gt;Restricting direct HP access and updating it through defined operations&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Adding healing gives us a next step. If healing must not exceed maximum HP, what state should the character hold, and where should that check live? Before scattering conditions across callers, try expressing healing as another operation the character provides.&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>oop</category>
      <category>programming</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Build a Colorful Wave of Cubes on the GPU with bgfx</title>
      <dc:creator>puffball1567</dc:creator>
      <pubDate>Fri, 02 Oct 2026 16:06:25 +0000</pubDate>
      <link>https://dev.to/puffball1567/build-a-colorful-wave-of-cubes-on-the-gpu-with-bgfx-213m</link>
      <guid>https://dev.to/puffball1567/build-a-colorful-wave-of-cubes-on-the-gpu-with-bgfx-213m</guid>
      <description>&lt;p&gt;&lt;strong&gt;GPU Implementation with bgfx&lt;/strong&gt; is a series about building a variety of GPU effects and techniques with bgfx. We'll start with small, working demos and explore animated shapes, drawing many objects efficiently, image effects, and GPU computation. Each article will connect what you see on screen to the code that makes it happen, explaining how the CPU and GPU share the work along the way.&lt;/p&gt;

&lt;p&gt;For this first installment, let's make something move.&lt;/p&gt;

&lt;p&gt;The scene below contains a 25 × 25 grid of cubes. Waves travel through the grid, lifting the cubes and shifting their colors. The CPU places the cubes on a flat grid; a vertex shader calculates their vertical movement on the GPU.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fub93hh4087xc61cfqfxd.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fub93hh4087xc61cfqfxd.gif" alt="625 colored cubes rising and falling in a wave, rendered by the accompanying bgfx application" width="560" height="350"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is a capture of the working demo, not a mockup. Press &lt;strong&gt;Space&lt;/strong&gt; to pause, &lt;strong&gt;Up/Down&lt;/strong&gt; to change the amplitude, and &lt;strong&gt;R&lt;/strong&gt; to reset it.&lt;/p&gt;

&lt;p&gt;We'll build this effect step by step using bgfx's public API. You need some C++ familiarity, but no previous shader experience. The full &lt;a href="//demo-wave-cubes/README.md"&gt;demo source and build instructions&lt;/a&gt; accompany the article; the snippets below come from that implementation, with surrounding setup omitted where stated.&lt;/p&gt;

&lt;h2&gt;
  
  
  Representing 3D shapes with polygons
&lt;/h2&gt;

&lt;p&gt;The scene shows cubes, but our program does not simply ask the GPU to “draw a cube here.” We describe the cube's surface as smaller faces and supply their positions and connections. Let's look at how that representation works.&lt;/p&gt;

&lt;p&gt;A &lt;strong&gt;polygon&lt;/strong&gt; is a shape with straight sides. Triangles and quadrilaterals are both polygons; in this demo, we divide every surface into triangles. Three points that do not lie on a straight line define one flat face. With four or more points, the points might not all lie in the same plane. This makes triangles a convenient building block for 3D surfaces, and they are the units used by our drawing setup.&lt;/p&gt;

&lt;p&gt;To describe one square face of a cube, split it along a diagonal. Here is that face viewed straight on. The numbers &lt;code&gt;0&lt;/code&gt; through &lt;code&gt;3&lt;/code&gt; identify its corner points:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;3 -------- 2
|        / |
|      /   |
|    /     |
|  /       |
0 -------- 1

Triangle A: 0 → 1 → 2
Triangle B: 0 → 2 → 3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Together, the triangles cover the whole square without a gap. The diagonal shows where we split the surface; it does not need to appear as a visible line. A cube has six square faces, so &lt;strong&gt;6 faces × 2 triangles = 12 triangles&lt;/strong&gt; describe its entire surface. We do not need to fill its interior with smaller cubes. Drawing the surface and using depth to determine which face is in front gives it a solid appearance.&lt;/p&gt;

&lt;p&gt;Each corner point is a &lt;strong&gt;vertex&lt;/strong&gt;. In 3D, its position is described by three numbers: &lt;code&gt;(x, y, z)&lt;/code&gt;. Their reference point is the &lt;strong&gt;origin&lt;/strong&gt;, at &lt;code&gt;(0, 0, 0)&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Let's establish the axis directions used in this demo. Imagine looking horizontally toward the origin from the negative-Z side. From that reference viewpoint, the directions are:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Axis&lt;/th&gt;
&lt;th&gt;Positive direction (increasing values)&lt;/th&gt;
&lt;th&gt;Negative direction (decreasing values)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;X&lt;/td&gt;
&lt;td&gt;Right&lt;/td&gt;
&lt;td&gt;Left&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Y&lt;/td&gt;
&lt;td&gt;Up&lt;/td&gt;
&lt;td&gt;Down&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Z&lt;/td&gt;
&lt;td&gt;Away from you, beyond the origin&lt;/td&gt;
&lt;td&gt;Toward you&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;For example, &lt;code&gt;(1, 0, 0)&lt;/code&gt; is one unit from the origin along positive X, &lt;code&gt;(0, 1, 0)&lt;/code&gt; is one unit up, and &lt;code&gt;(0, 0, 1)&lt;/code&gt; is one unit along positive Z. X and Z describe positions along the ground plane; Y describes height. These are this demo's conventions, not rules shared by every 3D application.&lt;/p&gt;

&lt;p&gt;The actual demo camera looks at this space from above and at an angle, so positive X does not necessarily point directly right on screen. “Right, up, and away” in the table describe our reference viewpoint. Moving the camera does not change the axes in the scene. The cube's vertex coordinates will use its own center as the origin, while positions used to arrange the cubes will use the center of the entire grid. We do not rotate the cubes in this demo, so the axis directions in these two coordinate systems stay aligned.&lt;/p&gt;

&lt;p&gt;Positions alone do not tell us which points form a face, so we also supply connections: “use 0, 1, and 2 for one triangle; use 0, 2, and 3 for another.” A shape represented by vertices and their face connections is called a &lt;strong&gt;mesh&lt;/strong&gt;. We will prepare one cube mesh.&lt;/p&gt;

&lt;p&gt;Curved surfaces, such as spheres and characters, can also be approximated with many triangles. Our cube's faces are flat, so just two triangles per face describe their shape. This small mesh will be our starting point for drawing and animation on the GPU.&lt;/p&gt;

&lt;h2&gt;
  
  
  What runs on the CPU, and what runs on the GPU?
&lt;/h2&gt;

&lt;p&gt;Our program has three pieces:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CPU / C++
  Upload one cube mesh during initialization
  Each frame: supply time, amplitude, and a position for each cube
       |
       v
GPU / vertex shader
  Calculate the wave height at that cube's center
  Move each corner upward or downward by that height
  Transform the result into clip space
       |
       v
Rasterization → GPU / fragment shader
  Color the covered fragments and shade the cube faces
       |
       v
Depth testing and color output → window
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A &lt;strong&gt;vertex shader&lt;/strong&gt; calculates where each mesh vertex goes for drawing. The GPU assembles triangles from the resulting vertices and the supplied connections. &lt;strong&gt;Rasterization&lt;/strong&gt; determines which screen pixels those triangles cover: it connects the faces described by coordinates to the grid of pixels that makes up the image. A &lt;strong&gt;fragment shader&lt;/strong&gt; then calculates color for the covered samples. Depth testing also determines visibility, so a hidden face's color does not simply appear on screen.&lt;/p&gt;

&lt;p&gt;The shaders in this project use bgfx's GLSL-like shader language and are compiled with its &lt;code&gt;shaderc&lt;/code&gt; tool. &lt;a href="https://bkaradzic.github.io/bgfx/tools.html#shader-compiler-shaderc" rel="noopener noreferrer"&gt;bgfx shader tooling&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We will reuse one mesh, but submit a separate draw for each cube. This makes the connection between a cube's position and its shader inputs easy to follow. It does &lt;strong&gt;not&lt;/strong&gt; make 625 cubes a single draw call. Instancing is a useful next step once this version works.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Project directory structure
&lt;/h2&gt;

&lt;p&gt;We will organize the program in a directory named &lt;code&gt;demo-wave-cubes&lt;/code&gt;, separating the C++ code from the shaders that run on the GPU:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;demo-wave-cubes/
├── main.cpp              window, mesh, input, drawing loop
├── demo_support.h        vertex types and shader-loading helpers
├── build.sh              shader and C++ build commands
├── make_overlay.py       generate the transparent title/help image
└── shaders/
    ├── vs_wave.sc        vertex shader for wave displacement
    ├── fs_wave.sc        color, lighting, edge accents
    ├── varying.def.sc    vertex inputs and shader-stage connections
    ├── vs_overlay.sc    vertex shader for positioning the title image
    └── fs_overlay.sc    fragment shader for drawing the title image
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;main.cpp&lt;/code&gt; will prepare the geometry and draw inputs; the shaders in &lt;code&gt;shaders/&lt;/code&gt; will calculate the cubes' movement and color. The title and keyboard help will use a transparent image generated by &lt;code&gt;make_overlay.py&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Building the project will generate the executable, compiled shaders, and overlay image in &lt;code&gt;build/&lt;/code&gt;. We will cover the build and run commands after the implementation. First, let's prepare the geometry for one cube.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Describe one cube
&lt;/h2&gt;

&lt;p&gt;First, define what one vertex contains. This is the actual type in &lt;code&gt;demo_support.h&lt;/code&gt;, included by &lt;code&gt;main.cpp&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="nc"&gt;Vertex&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;z&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;nx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ny&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;nz&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;&lt;code&gt;x&lt;/code&gt;, &lt;code&gt;y&lt;/code&gt;, and &lt;code&gt;z&lt;/code&gt; are the local position. &lt;code&gt;nx&lt;/code&gt;, &lt;code&gt;ny&lt;/code&gt;, and &lt;code&gt;nz&lt;/code&gt; describe a &lt;strong&gt;normal&lt;/strong&gt;: a direction pointing out of the face, used later for lighting. The top face has the normal &lt;code&gt;(0, 1, 0)&lt;/code&gt;; the face pointing along positive X has &lt;code&gt;(1, 0, 0)&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The demo creates four vertices for each of six faces: 24 vertices in total. A geometric cube has only eight corners, but one corner belongs to three faces with different normals. Keeping separate vertex records gives each face a flat appearance.&lt;/p&gt;

&lt;p&gt;We encode the connections from the diagram as &lt;strong&gt;indices&lt;/strong&gt;: numbers that refer to entries in the vertex array, starting at zero. In our drawing setup, each group of three indices specifies one triangle. For the first face:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Four face vertices: 0, 1, 2, 3
First triangle:    0, 1, 2
Second triangle:   0, 2, 3

6 faces × 2 triangles × 3 indices = 36 indices
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first face's index array is &lt;code&gt;{0, 1, 2, 0, 2, 3}&lt;/code&gt;. Both triangles refer to vertices 0 and 2, so they reuse those vertex records within the face. The next face has four separate vertices and uses &lt;code&gt;{4, 5, 6, 4, 6, 7}&lt;/code&gt;. Repeating this for all six faces gives the demo's 36 indices. &lt;strong&gt;24 counts vertex records, 12 counts triangles, and 36 counts references to vertices&lt;/strong&gt;; these numbers describe different things.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;main.cpp&lt;/code&gt; fills &lt;code&gt;std::vector&amp;lt;Vertex&amp;gt; vertices&lt;/code&gt; and &lt;code&gt;std::vector&amp;lt;uint16_t&amp;gt; indices&lt;/code&gt; with that data. Then it describes the vertex layout and creates the buffers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="n"&gt;bgfx&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;VertexLayout&lt;/span&gt; &lt;span class="n"&gt;layout&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;layout&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;begin&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bgfx&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Attrib&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Position&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bgfx&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;AttribType&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Float&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bgfx&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Attrib&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Normal&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bgfx&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;AttribType&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Float&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;end&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// Copy the CPU arrays into memory managed by bgfx.&lt;/span&gt;
&lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;vb&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;bgfx&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;createVertexBuffer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;bgfx&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;copy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;vertices&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="kt"&gt;uint32_t&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;vertices&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nf"&gt;sizeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Vertex&lt;/span&gt;&lt;span class="p"&gt;))),&lt;/span&gt;
    &lt;span class="n"&gt;layout&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;ib&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;bgfx&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;createIndexBuffer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;bgfx&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;copy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;indices&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="kt"&gt;uint32_t&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;indices&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nf"&gt;sizeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;uint16_t&lt;/span&gt;&lt;span class="p"&gt;))));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;Vertex&lt;/code&gt; stores six floats in this order: &lt;code&gt;x, y, z, nx, ny, nz&lt;/code&gt;. The vertex buffer receives their bytes, without the C++ member names. We therefore need to tell bgfx which part represents a position and which part represents a normal. &lt;code&gt;layout&lt;/code&gt; describes how to read that data.&lt;/p&gt;

&lt;p&gt;The first &lt;code&gt;.add(bgfx::Attrib::Position, 3, bgfx::AttribType::Float)&lt;/code&gt; says, “read the first three floats as a position.” The next &lt;code&gt;.add(bgfx::Attrib::Normal, 3, bgfx::AttribType::Float)&lt;/code&gt; says, “read the next three floats as a normal.” Here, &lt;code&gt;3&lt;/code&gt; counts the components of one position or direction, not the number of vertices. &lt;code&gt;begin()&lt;/code&gt; starts the layout definition, and &lt;code&gt;end()&lt;/code&gt; finalizes it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;One vertex:    [ x, y, z   | nx, ny, nz ]
Layout meaning:[ position | normal     ]
Shader input:  [ a_position| a_normal   ]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For example, a vertex on the top face might contain &lt;code&gt;{0.46f, 0.46f, 0.46f, 0.0f, 1.0f, 0.0f}&lt;/code&gt;. The first three values become the position &lt;code&gt;(0.46, 0.46, 0.46)&lt;/code&gt;, and the last three become the upward normal &lt;code&gt;(0, 1, 0)&lt;/code&gt;. Our shader receives them through inputs named &lt;code&gt;a_position&lt;/code&gt; and &lt;code&gt;a_normal&lt;/code&gt;. This also connects to the &lt;code&gt;POSITION&lt;/code&gt; and &lt;code&gt;NORMAL&lt;/code&gt; definitions in &lt;code&gt;varying.def.sc&lt;/code&gt;, which we will examine later.&lt;/p&gt;

&lt;p&gt;In this build, a float occupies four bytes: 12 bytes for the position and 12 for the normal, making 24 bytes per vertex. The layout also records this distance from one vertex to the next, called the &lt;strong&gt;stride&lt;/strong&gt;. With multiple vertices in the buffer, the same reading pattern repeats every 24 bytes.&lt;/p&gt;

&lt;p&gt;That is why &lt;code&gt;createVertexBuffer&lt;/code&gt; receives both the data and &lt;code&gt;layout&lt;/code&gt;: the data supplies the numbers, and the layout describes their arrangement and meaning. The layout does not rearrange the numbers, so the order and types in the &lt;code&gt;.add()&lt;/code&gt; calls must match the actual &lt;code&gt;Vertex&lt;/code&gt; records.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;vb&lt;/code&gt; is a &lt;code&gt;bgfx::VertexBufferHandle&lt;/code&gt;; &lt;code&gt;ib&lt;/code&gt; is a &lt;code&gt;bgfx::IndexBufferHandle&lt;/code&gt;. They identify resources managed by bgfx. They are not pointers into the vectors. &lt;code&gt;bgfx::copy&lt;/code&gt; copies the supplied bytes, so their later use does not depend on keeping the vectors' backing storage alive. &lt;a href="https://bkaradzic.github.io/bgfx/bgfx.html" rel="noopener noreferrer"&gt;Buffer and memory API&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;These buffers are created once, before the frame loop. Every cube uses them.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Give each cube a position
&lt;/h2&gt;

&lt;p&gt;The previous section prepared one cube centered at &lt;code&gt;(0, 0, 0)&lt;/code&gt;. We will reuse that shape to make a grid of 25 by 25 cubes: 625 in total. Recall our reference directions: X is left/right, Z is depth, and Y is height.&lt;/p&gt;

&lt;h3&gt;
  
  
  Start with grid indices
&lt;/h3&gt;

&lt;p&gt;Number the positions from &lt;code&gt;0&lt;/code&gt; to &lt;code&gt;24&lt;/code&gt; along both X and Z. The code calls these integer grid indices &lt;code&gt;x&lt;/code&gt; and &lt;code&gt;z&lt;/code&gt;. They are not yet coordinates measuring distance in the scene.&lt;/p&gt;

&lt;p&gt;For example, &lt;code&gt;(x, z) = (0, 0)&lt;/code&gt; selects the first position along both directions. &lt;code&gt;(12, 12)&lt;/code&gt; selects the middle. Counting from zero makes index &lt;code&gt;12&lt;/code&gt; the thirteenth position, the middle of 25 positions.&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%2Fspaarqkyomvbjk9dys79.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%2Fspaarqkyomvbjk9dys79.png" alt="A top view mapping the 25-by-25 grid indices to cube centers, with a second diagram translating a cube by 1.12 along X" width="800" height="629"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Each dot in the upper diagram is a cube center. Viewed from above, X runs across the page and Z runs up the page; Y is perpendicular to it. This is a different viewpoint from the demo's angled camera.&lt;/p&gt;

&lt;h3&gt;
  
  
  Turn each index into a center position
&lt;/h3&gt;

&lt;p&gt;Using the indices directly as coordinates would place all cubes on the positive side of the origin. Subtract &lt;code&gt;12&lt;/code&gt; to center the grid around zero:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Index:            0    1   ...   12   ...   23   24
Subtract 12:    -12  -11   ...    0   ...   11   12
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then multiply by the center spacing, &lt;code&gt;1.12&lt;/code&gt;. Each cube extends from &lt;code&gt;-0.46&lt;/code&gt; to &lt;code&gt;+0.46&lt;/code&gt; on each axis, making its side length &lt;code&gt;0.92&lt;/code&gt;. Spacing the centers by &lt;code&gt;1.12&lt;/code&gt; leaves a gap of &lt;code&gt;1.12 - 0.92 = 0.20&lt;/code&gt; between adjacent faces before displacement. This spacing is our design choice, not a bgfx constant.&lt;/p&gt;

&lt;p&gt;Here is the calculation along X. The same calculation applies to Z.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Grid index &lt;code&gt;x&lt;/code&gt;
&lt;/th&gt;
&lt;th&gt;Center around zero: &lt;code&gt;x - 12&lt;/code&gt;
&lt;/th&gt;
&lt;th&gt;Center X: &lt;code&gt;(x - 12) * 1.12&lt;/code&gt;
&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;-12&lt;/td&gt;
&lt;td&gt;-13.44&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;11&lt;/td&gt;
&lt;td&gt;-1&lt;/td&gt;
&lt;td&gt;-1.12&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;12&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;0.00&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;13&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;1.12&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;24&lt;/td&gt;
&lt;td&gt;12&lt;/td&gt;
&lt;td&gt;13.44&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Grid indices &lt;code&gt;(13, 12)&lt;/code&gt; therefore give the cube center &lt;code&gt;(1.12, 0, 0)&lt;/code&gt;. Indices &lt;code&gt;(12, 13)&lt;/code&gt; give &lt;code&gt;(0, 0, 1.12)&lt;/code&gt;. For now, every center's Y coordinate stays zero.&lt;/p&gt;

&lt;h3&gt;
  
  
  Move the whole cube to that position
&lt;/h3&gt;

&lt;p&gt;Moving every vertex by the same amount is called &lt;strong&gt;translation&lt;/strong&gt;. To place the center at &lt;code&gt;(1.12, 0, 0)&lt;/code&gt;, add &lt;code&gt;1.12&lt;/code&gt; to every vertex's X coordinate:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Vertex within cube       Translation       Vertex in scene
(0.46, 0.46, 0.46)   +  (1.12, 0, 0)  =  (1.58, 0.46, 0.46)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The lower diagram shows the center and the rightmost vertex moving by the same amount. Applying the same translation to every vertex preserves the cube's shape and size.&lt;/p&gt;

&lt;p&gt;We describe this operation to the shader with a &lt;strong&gt;model matrix&lt;/strong&gt;. A matrix is a rectangular arrangement of numbers; in 3D graphics, matrices represent operations such as translation, rotation, and scaling. Here we only translate. Coordinates relative to the cube's own center are called &lt;strong&gt;local coordinates&lt;/strong&gt;; coordinates in the scene containing the entire grid are &lt;strong&gt;world coordinates&lt;/strong&gt;. Our model matrix describes the conversion from local to world coordinates.&lt;/p&gt;

&lt;p&gt;Inside the per-cube drawing loop, the implementation uses:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// x and z are grid indices, each ranging from 0 to 24.&lt;/span&gt;
&lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="n"&gt;bx&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;mtxTranslate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;1.12&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;z&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;1.12&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;bgfx&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;setTransform&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;model&lt;/code&gt; is an array of 16 floats holding a 4 × 4 matrix. &lt;code&gt;bx::mtxTranslate&lt;/code&gt; writes the matrix into its first argument, &lt;code&gt;model&lt;/code&gt;. The next three arguments specify the X, Y, and Z translation. We do not need to calculate the individual matrix entries ourselves.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;bgfx::setTransform(model)&lt;/code&gt; sets the matrix for the draw we will submit next. This call alone does not draw a cube. We will pair it with &lt;code&gt;bgfx::submit&lt;/code&gt; later, and the vertex shader will use the matrix to calculate each vertex's position. The original vertex buffer stays unchanged.&lt;/p&gt;

&lt;p&gt;Repeating this setup and drawing the shared cube inside the nested &lt;code&gt;x&lt;/code&gt; and &lt;code&gt;z&lt;/code&gt; loops fills the grid. So far, the CPU has only arranged the cubes on a flat plane. Next, we will pass time to the GPU and add the vertical wave motion.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Pass time into the shader
&lt;/h2&gt;

&lt;p&gt;A &lt;strong&gt;uniform&lt;/strong&gt; supplies a value that stays the same across a draw. We need a clock and an amplitude, so one four-component float value is enough.&lt;/p&gt;

&lt;p&gt;Create its handle once, beside the buffers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;uWave&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;bgfx&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;createUniform&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"u_wave"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bgfx&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;UniformType&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Vec4&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then fill its values during each frame:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// time is elapsed seconds; amplitude starts at 1.35.&lt;/span&gt;
&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;wave&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;amplitude&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="c1"&gt;// Set this before each cube's submit; the final two components are unused.&lt;/span&gt;
&lt;span class="n"&gt;bgfx&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;setUniform&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;uWave&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;wave&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The corresponding shader declaration is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight glsl"&gt;&lt;code&gt;&lt;span class="k"&gt;uniform&lt;/span&gt; &lt;span class="kt"&gt;vec4&lt;/span&gt; &lt;span class="n"&gt;u_wave&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The name &lt;code&gt;u_wave&lt;/code&gt; connects the C++ uniform with the shader declaration. Its &lt;code&gt;.x&lt;/code&gt; component carries time and &lt;code&gt;.y&lt;/code&gt; carries amplitude. Pausing freezes the C++ clock; the shader continues rendering with the same input values. &lt;a href="https://bkaradzic.github.io/bgfx/bgfx.html" rel="noopener noreferrer"&gt;Uniform API&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Calculate the wave at the cube's center
&lt;/h2&gt;

&lt;p&gt;Here is the movement calculation from &lt;code&gt;vs_wave.sc&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight glsl"&gt;&lt;code&gt;&lt;span class="c1"&gt;// A local origin transformed by the model matrix is the cube's center.&lt;/span&gt;
&lt;span class="kt"&gt;vec3&lt;/span&gt; &lt;span class="n"&gt;center&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mul&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;u_model&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="kt"&gt;vec4&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="n"&gt;xyz&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;radius&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;length&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;center&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;xz&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;height&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;u_wave&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;sin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;radius&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="mi"&gt;65&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;u_wave&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;)&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="mi"&gt;35&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nf"&gt;cos&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;center&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;x&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="mi"&gt;5&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;center&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;z&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="mi"&gt;35&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;u_wave&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;u_model[0]&lt;/code&gt; is bgfx's predefined model matrix, populated through &lt;code&gt;setTransform&lt;/code&gt;. &lt;code&gt;.xz&lt;/code&gt; selects the two horizontal coordinates, and &lt;code&gt;length&lt;/code&gt; measures their distance from the grid's center. &lt;a href="https://bkaradzic.github.io/bgfx/tools.html#predefined-uniforms" rel="noopener noreferrer"&gt;Predefined uniforms&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Read the first wave as three controls:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;radius * 0.65&lt;/code&gt; changes the phase as we move outward, producing rings.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-time * 1.8&lt;/code&gt; shifts those rings over time.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;amplitude&lt;/code&gt; controls the vertical distance traveled.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The smaller cosine wave adds variation across X and Z, so the result is not perfectly circular. These coefficients are artistic choices in this demo, not physical constants.&lt;/p&gt;

&lt;p&gt;For a concrete check, put the center at &lt;code&gt;(0, 0, 0)&lt;/code&gt; and time at zero. The sine term is zero and the cosine term is one, so the height is &lt;code&gt;1.35 × 0.35 = 0.4725&lt;/code&gt;. Setting amplitude to zero makes the height zero everywhere.&lt;/p&gt;

&lt;p&gt;Why use the &lt;strong&gt;center&lt;/strong&gt;, rather than the current vertex position? All corners of a cube need the same displacement. Sampling the wave separately at each corner would deform the cube. Here, every vertex invocation for a given cube independently calculates the same height, keeping it rigid.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Move each vertex and project it
&lt;/h2&gt;

&lt;p&gt;Next, the shader transforms the actual corner and adds that height:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight glsl"&gt;&lt;code&gt;&lt;span class="kt"&gt;vec3&lt;/span&gt; &lt;span class="n"&gt;world&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mul&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;u_model&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="kt"&gt;vec4&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a_position&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="n"&gt;xyz&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;world&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;height&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nb"&gt;gl_Position&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mul&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;u_viewProj&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;vec4&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;world&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;a_position&lt;/code&gt; comes from the vertex buffer. &lt;code&gt;world&lt;/code&gt; is the position after placing that cube in the grid. &lt;code&gt;u_viewProj&lt;/code&gt; combines the camera view and projection; &lt;code&gt;gl_Position&lt;/code&gt; is the clip-space output consumed by the graphics pipeline.&lt;/p&gt;

&lt;p&gt;The CPU creates the camera matrices once because this demo has a fixed camera:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;view&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;projection&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="n"&gt;bx&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;mtxLookAt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;view&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mf"&gt;27.0&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;25.0&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;32.0&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="n"&gt;bx&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;mtxOrtho&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;projection&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;25.0&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;25.0&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;11.82&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;11.82&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="mf"&gt;0.1&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;120.0&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bgfx&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;getCaps&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;homogeneousDepth&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;bgfx&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;setViewTransform&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;view&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;projection&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;An orthographic projection keeps the grid's distant cubes from shrinking with perspective, which suits this graphic, diagram-like composition. View &lt;code&gt;0&lt;/code&gt; is the scene's drawing group. The title overlay uses a separate view.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. Give the wave color and depth
&lt;/h2&gt;

&lt;p&gt;The vertex shader passes three values onward: the world position, the face normal, and the original local position. &lt;code&gt;varying.def.sc&lt;/code&gt; declares the connection between the shader stages:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight glsl"&gt;&lt;code&gt;&lt;span class="kt"&gt;vec3&lt;/span&gt; &lt;span class="n"&gt;a_position&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;POSITION&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kt"&gt;vec3&lt;/span&gt; &lt;span class="n"&gt;a_normal&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;NORMAL&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kt"&gt;vec3&lt;/span&gt; &lt;span class="n"&gt;v_normal&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;TEXCOORD1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kt"&gt;vec3&lt;/span&gt; &lt;span class="n"&gt;v_world&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;TEXCOORD2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kt"&gt;vec3&lt;/span&gt; &lt;span class="n"&gt;v_local&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;TEXCOORD3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This excerpt shows the wave inputs and outputs; the complete file also contains the overlay's UV coordinates. Here &lt;code&gt;TEXCOORD1&lt;/code&gt;, for example, identifies an interpolated data channel. It does not require a texture. The vertex shader assigns &lt;code&gt;v_normal = a_normal&lt;/code&gt;, &lt;code&gt;v_world = world&lt;/code&gt;, and &lt;code&gt;v_local = a_position&lt;/code&gt;; the fragment shader receives interpolated values across each triangle.&lt;/p&gt;

&lt;p&gt;In &lt;code&gt;fs_wave.sc&lt;/code&gt;, three offset cosine curves produce the red, green, and blue components:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight glsl"&gt;&lt;code&gt;&lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;phase&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;v_world&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;x&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="mo"&gt;022&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;v_world&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;z&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="mo"&gt;01&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;v_world&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;y&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="mo"&gt;055&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kt"&gt;vec3&lt;/span&gt; &lt;span class="n"&gt;color&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="mi"&gt;55&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="mi"&gt;45&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nf"&gt;cos&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;2831853&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;vec3&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;phase&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;phase&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;phase&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="kt"&gt;vec3&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mo"&gt;02&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;35&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;65&lt;/span&gt;&lt;span class="p"&gt;)));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The phase depends on height as well as horizontal position. That makes a cube change color as it rises and falls, without uploading a new per-cube color from C++.&lt;/p&gt;

&lt;p&gt;For lighting, compare the surface direction with one fixed light direction:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight glsl"&gt;&lt;code&gt;&lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;light&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="mi"&gt;32&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="mi"&gt;68&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;normalize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;v_normal&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;normalize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;vec3&lt;/span&gt;&lt;span class="p"&gt;(&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="mi"&gt;45&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;85&lt;/span&gt;&lt;span class="p"&gt;,&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="mi"&gt;55&lt;/span&gt;&lt;span class="p"&gt;))),&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The dot product grows as the two normalized directions align. &lt;code&gt;max(..., 0.0)&lt;/code&gt; prevents negative lighting, while &lt;code&gt;0.32&lt;/code&gt; keeps the unlit faces visible. This is a simple directional-light effect, with no cast shadows. Since our model matrices only translate, the normals remain valid as supplied. Adding rotation or nonuniform scaling would require transforming the normals too.&lt;/p&gt;

&lt;p&gt;The full shader also adds a narrow bright accent near each face edge. It measures how close the local position is to two cube boundaries. This makes the cubes easier to separate visually; it does not add bevel geometry, bloom, or a physical material simulation.&lt;/p&gt;

&lt;h2&gt;
  
  
  8. Submit the same mesh with different inputs
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;program&lt;/code&gt; handle is created before the frame loop by our own &lt;code&gt;loadProgram&lt;/code&gt; helper in &lt;code&gt;demo_support.h&lt;/code&gt;. It reads &lt;code&gt;vs_wave.bin&lt;/code&gt; and &lt;code&gt;fs_wave.bin&lt;/code&gt;, creates a shader from each with &lt;code&gt;bgfx::createShader&lt;/code&gt;, and combines them using &lt;code&gt;bgfx::createProgram(v, f, true)&lt;/code&gt;. The full helper checks failures; the final &lt;code&gt;true&lt;/code&gt; transfers responsibility for releasing those shader references to the program.&lt;/p&gt;

&lt;p&gt;With the resources ready, the inner draw loop is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// model and wave are the values constructed in the preceding steps.&lt;/span&gt;
&lt;span class="n"&gt;bgfx&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;setTransform&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;bgfx&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;setVertexBuffer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;vb&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;bgfx&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;setIndexBuffer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ib&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;bgfx&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;setUniform&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;uWave&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;wave&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;bgfx&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;setState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BGFX_STATE_WRITE_RGB&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;BGFX_STATE_WRITE_A&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;
               &lt;span class="n"&gt;BGFX_STATE_WRITE_Z&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;BGFX_STATE_DEPTH_TEST_LESS&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;bgfx&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;submit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;program&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first &lt;code&gt;0&lt;/code&gt; in &lt;code&gt;setVertexBuffer&lt;/code&gt; selects vertex stream zero; the &lt;code&gt;0&lt;/code&gt; in &lt;code&gt;submit&lt;/code&gt; selects view zero. They name different things. Depth writing and testing keep a farther cube from painting over a nearer one. This introductory mesh disables face culling; adding consistent winding and culling is a separate improvement.&lt;/p&gt;

&lt;p&gt;After all cube draws and the overlay draw, call:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="n"&gt;bgfx&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;frame&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// Advance the frame after recording this frame's drawing work.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The next frame supplies a new time. We never rebuild or upload the cube mesh to animate the wave. The CPU still builds matrices and records separate draws; moving an equation into a shader does not remove submission overhead. &lt;a href="https://bkaradzic.github.io/bgfx/bgfx.html" rel="noopener noreferrer"&gt;Drawing API&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  9. Build and run the source
&lt;/h2&gt;

&lt;p&gt;With the implementation assembled, we can build the source and see it in motion.&lt;/p&gt;

&lt;p&gt;The accompanying build targets &lt;strong&gt;Linux, SDL2/X11, and bgfx's OpenGL backend&lt;/strong&gt;. XWayland also works in the environment used here. It uses a bgfx API 159 SDK with matching bx/bimg libraries and &lt;code&gt;shaderc&lt;/code&gt;, plus a C++20 compiler. Other backends need matching shader binaries and platform setup; this particular build does not select them automatically.&lt;/p&gt;

&lt;p&gt;Prepare the SDK and other dependencies using the &lt;a href="//demo-wave-cubes/README.md"&gt;README's build instructions&lt;/a&gt;, then run these commands from &lt;code&gt;demo-wave-cubes&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# The directory must contain bgfx/, bx/, and bimg/.&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;BGFX_SDK&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/path/to/bgfx-sdk
bash build.sh
./build/wave-cubes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;build.sh&lt;/code&gt; compiles the shaders and generates the overlay image and C++ executable. Running &lt;code&gt;./build/wave-cubes&lt;/code&gt; opens the animated field of 625 cubes. Press Space to pause, Up/Down to change the amplitude, R to reset, and Esc to exit.&lt;/p&gt;

&lt;h2&gt;
  
  
  Check the effect instead of trusting the picture
&lt;/h2&gt;

&lt;p&gt;The supplied validation script renders screenshots through bgfx, then compares their pixels. It checks that a zero-amplitude field stays unchanged when time changes, and that a nonzero-amplitude field does change. The capture path reads this application's framebuffer, not the desktop.&lt;/p&gt;

&lt;p&gt;The demo was built and exercised on Linux with SDL 2.0.20 and a bgfx API 159 SDK. bgfx reported OpenGL 4.3; the display's driver reported an accelerated AMD RENOIR device with Mesa 23.2.1. The six-second capture contains 180 frames sampled at a fixed 1/30-second simulation step. That is an animation export setting, not a measured performance claim.&lt;/p&gt;

&lt;p&gt;Try changing just one term:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Set amplitude to zero to recover the flat grid.&lt;/li&gt;
&lt;li&gt;Remove the cosine term to see only circular waves.&lt;/li&gt;
&lt;li&gt;Increase &lt;code&gt;0.65&lt;/code&gt; to fit more rings into the same space.&lt;/li&gt;
&lt;li&gt;Remove the Y term from the color phase to keep the color field stationary.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Rebuild after changing a shader. Its &lt;code&gt;.sc&lt;/code&gt; source is compiled into a &lt;code&gt;.bin&lt;/code&gt; asset; editing the source alone does not replace the binary loaded by the application.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where to take it next
&lt;/h2&gt;

&lt;p&gt;We now have a small effect with a clear division of work: C++ supplies shared geometry and draw inputs, the vertex shader supplies movement, and the fragment shader supplies appearance.&lt;/p&gt;

&lt;p&gt;The next practical question is how to draw this grid with fewer CPU submissions. A follow-up can replace the per-cube draw loop with instance data while preserving the same wave equation. After that, render targets and compute shaders open up effects that need intermediate images or persistent simulation state.&lt;/p&gt;

&lt;h2&gt;
  
  
  Clay Board Style System
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/puffball1567/clay-board-style-system" rel="noopener noreferrer"&gt;Clay Board Style System&lt;/a&gt; is my CSS-inspired foundation for native GUI toolkits. It includes an optional GPU host and a bgfx adapter through bgfxim, connecting custom GPU content with native layout and input. If you want to take shader experiments into an application UI, see its &lt;a href="https://github.com/puffball1567/clay-board-style-system/blob/devel/docs/gpu-host.md" rel="noopener noreferrer"&gt;GPU host documentation&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>cpp</category>
      <category>graphics</category>
      <category>gamedev</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Inside TypeScript 7: Parallel Type Checking Without Giving Up Determinism</title>
      <dc:creator>puffball1567</dc:creator>
      <pubDate>Mon, 21 Sep 2026 19:32:09 +0000</pubDate>
      <link>https://dev.to/puffball1567/inside-typescript-7-parallel-type-checking-without-giving-up-determinism-2l0m</link>
      <guid>https://dev.to/puffball1567/inside-typescript-7-parallel-type-checking-without-giving-up-determinism-2l0m</guid>
      <description>&lt;p&gt;&lt;em&gt;A source-guided look at checker pools, import-graph partitioning, and the trade-off between build speed and duplicated type state&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;TypeScript 7 is often introduced as the Go-based, much faster successor to the JavaScript compiler. That description is true, but it misses one of the more interesting engineering decisions in the new compiler: type checking is not simply dispatched to an arbitrary worker pool. The compiler builds a fixed set of checkers, assigns source files to them deliberately, and keeps that assignment stable for a given program and checker count.&lt;/p&gt;

&lt;p&gt;This matters because a type checker is not a stateless function that can examine any file in isolation. A file may rely on declarations from its imports, ambient declarations, generic instantiations, and the global scope. A design that maximized CPU use without considering those relationships would recreate the same semantic work repeatedly, make memory use unpredictable, and risk output whose ordering changes from build to build.&lt;/p&gt;

&lt;p&gt;The TypeScript team describes TypeScript 7 as a native Go port which performs parsing, type checking, and emitting in parallel. Its public controls include &lt;code&gt;--checkers&lt;/code&gt;, &lt;code&gt;--builders&lt;/code&gt;, and &lt;code&gt;--singleThreaded&lt;/code&gt;. This article focuses on the less obvious one: the type-checker pool used within a project.&lt;/p&gt;

&lt;p&gt;I am not part of the TypeScript team. This is an independent technical reading of the publicly available source code and documentation, not an official explanation of the implementation or its design decisions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Source and license note
&lt;/h2&gt;

&lt;p&gt;This article discusses the publicly available &lt;a href="https://github.com/microsoft/TypeScript" rel="noopener noreferrer"&gt;&lt;code&gt;microsoft/TypeScript&lt;/code&gt;&lt;/a&gt; repository. TypeScript 7’s Go compiler lives under &lt;a href="https://github.com/microsoft/TypeScript/tree/main/tsc" rel="noopener noreferrer"&gt;&lt;code&gt;tsc/&lt;/code&gt;&lt;/a&gt;; the earlier &lt;code&gt;typescript-go&lt;/code&gt; staging repository was archived after the release. The source is licensed under the &lt;a href="https://github.com/microsoft/TypeScript/blob/main/LICENSE.txt" rel="noopener noreferrer"&gt;Apache License, Version 2.0&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The short blocks labelled as excerpts are from that repository. Diagrams, explanations, and the simplified code are original. The full sources discussed here are &lt;a href="https://github.com/microsoft/TypeScript/blob/main/tsc/internal/compiler/checkerpool.go" rel="noopener noreferrer"&gt;&lt;code&gt;checkerpool.go&lt;/code&gt;&lt;/a&gt; and &lt;a href="https://github.com/microsoft/TypeScript/blob/main/tsc/internal/compiler/program.go" rel="noopener noreferrer"&gt;&lt;code&gt;program.go&lt;/code&gt;&lt;/a&gt;. The repository’s &lt;a href="https://github.com/microsoft/TypeScript/blob/main/NOTICE.txt" rel="noopener noreferrer"&gt;NOTICE file&lt;/a&gt; remains available with its attribution notices.&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem: type checking has shared semantic work
&lt;/h2&gt;

&lt;p&gt;Parsing is comparatively easy to parallelize. Given a file’s text and compiler options, a parser can normally construct that file’s syntax tree without waiting for the rest of the program. Emitting JavaScript can often follow the same pattern.&lt;/p&gt;

&lt;p&gt;Type checking is different. Consider a small project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;src/api.ts ───────┐
                   ├── src/service.ts ─── src/app.ts
src/models.ts ────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Consider the following code. &lt;code&gt;ApiResponse&amp;lt;T&amp;gt;&lt;/code&gt; is generic: when the compiler checks &lt;code&gt;ApiResponse&amp;lt;User&amp;gt;&lt;/code&gt;, it has to work with a concrete form in which &lt;code&gt;T&lt;/code&gt; is replaced by &lt;code&gt;User&lt;/code&gt;. This is called generic instantiation.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/models.ts&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="c1"&gt;// src/api.ts&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;ApiResponse&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;T&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;request&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ApiResponse&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// The real HTTP work is omitted to keep this example focused.&lt;/span&gt;
  &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`not implemented: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// src/service.ts&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./api&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./models&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;loadDisplayName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`/users/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toUpperCase&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// src/app.ts&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;loadDisplayName&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./service&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;loadDisplayName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;123&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To check &lt;code&gt;service.ts&lt;/code&gt;, a checker first resolves the names &lt;code&gt;request&lt;/code&gt; and &lt;code&gt;User&lt;/code&gt; to the modules that export them. To check &lt;code&gt;request&amp;lt;User&amp;gt;(...)&lt;/code&gt;, it handles the return type &lt;code&gt;Promise&amp;lt;ApiResponse&amp;lt;User&amp;gt;&amp;gt;&lt;/code&gt;, in which &lt;code&gt;T&lt;/code&gt; has been replaced with &lt;code&gt;User&lt;/code&gt;. To confirm that &lt;code&gt;response.data.name&lt;/code&gt; is valid, it must also follow &lt;code&gt;User&lt;/code&gt; far enough to find &lt;code&gt;name: string&lt;/code&gt;. &lt;code&gt;app.ts&lt;/code&gt; imports &lt;code&gt;loadDisplayName&lt;/code&gt;, so it also needs the exported function type.&lt;/p&gt;

&lt;p&gt;In this article, &lt;em&gt;semantic state&lt;/em&gt; means the checker’s internal working information: resolved names, compared types, and concrete types created from generics. It is neither emitted JavaScript nor a user-visible value. It is work the compiler can retain so that it does not have to answer the same type question repeatedly.&lt;/p&gt;

&lt;p&gt;That gives file assignment a concrete trade-off.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;One new checker per file

checker A: api.ts       → builds state for ApiResponse&amp;lt;T&amp;gt; and request&amp;lt;T&amp;gt;
checker B: models.ts    → builds state for User
checker C: service.ts   → may build its own state while following the imports
checker D: app.ts       → may separately follow the exported loadDisplayName type

Benefit: file-level work is easy to isolate
Cost: semantic state about shared imports can be duplicated
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;One checker for every file

checker 0: api.ts, models.ts, service.ts, app.ts

Benefit: symbols and types from imports are easier to reuse within one checker
Cost: one checker cannot safely serve multiple file checks at once, so file-oriented checking becomes effectively serial
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The useful design space lies between those extremes: keep related files near the same checker where possible, while ensuring that no one checker receives most of the work.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Several checkers

checker 0: api.ts, models.ts, service.ts
checker 1: app.ts

service.ts stays close to the api.ts and models.ts files it imports, which aims to reuse checker 0's state.
app.ts is related to service.ts too, but the whole project must also avoid concentrating too much work in one checker.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The real compiler is not deciding only among these four files. It uses the complete import graph and an estimated weight for each file. It therefore does not follow a simplistic “always place an import beside its importer” rule; it looks for an assignment that preserves both useful locality and practical parallelism.&lt;/p&gt;

&lt;h2&gt;
  
  
  A checker is a stateful worker, not just a goroutine
&lt;/h2&gt;

&lt;p&gt;In TypeScript 7, a checker pool owns a fixed number of &lt;code&gt;*checker.Checker&lt;/code&gt; values, one mutex per checker, and a mapping from each source file to its assigned checker.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// Excerpt. Fields unrelated to this article are omitted; inline comments are explanatory.&lt;/span&gt;
&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;checkerPool&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;program&lt;/span&gt;          &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Program&lt;/span&gt;                              &lt;span class="c"&gt;// The Program for the project currently being compiled.&lt;/span&gt;
    &lt;span class="n"&gt;checkers&lt;/span&gt;         &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;checker&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Checker&lt;/span&gt;                    &lt;span class="c"&gt;// An ordered list of created checkers.&lt;/span&gt;
    &lt;span class="n"&gt;locks&lt;/span&gt;            &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;sync&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Mutex&lt;/span&gt;                         &lt;span class="c"&gt;// A mutex for the checker at the same index.&lt;/span&gt;
    &lt;span class="n"&gt;fileAssociations&lt;/span&gt; &lt;span class="k"&gt;map&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;ast&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SourceFile&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;checker&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Checker&lt;/span&gt;  &lt;span class="c"&gt;// A lookup from file to its assigned checker.&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;First, a quick guide to the Go notation: &lt;code&gt;*T&lt;/code&gt; is a pointer to &lt;code&gt;T&lt;/code&gt;. Here it refers to one existing &lt;code&gt;Program&lt;/code&gt; or &lt;code&gt;Checker&lt;/code&gt; instance without copying it. &lt;code&gt;[]T&lt;/code&gt; is an ordered list, and &lt;code&gt;map[K]V&lt;/code&gt; is a lookup table from keys of type &lt;code&gt;K&lt;/code&gt; to values of type &lt;code&gt;V&lt;/code&gt;. Therefore, &lt;code&gt;[]*checker.Checker&lt;/code&gt; is an ordered list of pointers to checkers, while &lt;code&gt;map[*ast.SourceFile]*checker.Checker&lt;/code&gt; looks up a pointer to a checker from a pointer to a source file.&lt;/p&gt;

&lt;p&gt;Each field has a distinct role.&lt;/p&gt;

&lt;p&gt;Here, a &lt;code&gt;mutex&lt;/code&gt; is short for &lt;em&gt;mutual exclusion&lt;/em&gt;. It is a lock that prevents one shared resource from being changed simultaneously, or read while another operation is midway through changing it. When a goroutine calls &lt;code&gt;mutex.Lock()&lt;/code&gt;, only one goroutine can hold that mutex. Another goroutine that calls &lt;code&gt;Lock()&lt;/code&gt; on the same mutex waits until the first calls &lt;code&gt;Unlock()&lt;/code&gt;. A mutex does not copy or store data; it decides who is currently allowed to use one checker.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;program&lt;/code&gt; represents the one project currently being compiled. &lt;code&gt;Program&lt;/code&gt; holds project-wide information such as source files, compiler options, and import relationships. The checker pool is not a global pool shared by every compilation; it is created to type-check this &lt;code&gt;Program&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;checkers&lt;/code&gt; is the list of objects that actually perform type checking. With two checkers, &lt;code&gt;checkers[0]&lt;/code&gt; and &lt;code&gt;checkers[1]&lt;/code&gt; hold different &lt;code&gt;Checker&lt;/code&gt; instances. Each instance has its own caches for symbols, types, and generic instantiations.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;locks&lt;/code&gt; is the list of mutexes that prevents two goroutines from using the same checker at once. The index relationship matters: &lt;code&gt;locks[0]&lt;/code&gt; protects only &lt;code&gt;checkers[0]&lt;/code&gt;, and &lt;code&gt;locks[1]&lt;/code&gt; protects only &lt;code&gt;checkers[1]&lt;/code&gt;. This is not one global lock, so work using a different checker does not have to wait.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;fileAssociations&lt;/code&gt; records the owner of every source file. Its key is &lt;code&gt;*ast.SourceFile&lt;/code&gt; and its value is &lt;code&gt;*checker.Checker&lt;/code&gt;. After &lt;code&gt;service.ts&lt;/code&gt; is assigned to checker 0, &lt;code&gt;fileAssociations[serviceFile]&lt;/code&gt; returns the same checker as &lt;code&gt;checkers[0]&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For the earlier project with two checkers, the relationship looks like this in principle:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;checkers[0] = checker 0                  locks[0] = mutex for checker 0
checkers[1] = checker 1                  locks[1] = mutex for checker 1

fileAssociations[service.ts] ──────────&amp;gt; checker 0
fileAssociations[api.ts]     ──────────&amp;gt; checker 0
fileAssociations[models.ts]  ──────────&amp;gt; checker 0
fileAssociations[app.ts]     ──────────&amp;gt; checker 1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The important point is that locks belong to &lt;strong&gt;checkers&lt;/strong&gt;, not to &lt;strong&gt;files&lt;/strong&gt;. &lt;code&gt;service.ts&lt;/code&gt;, &lt;code&gt;api.ts&lt;/code&gt;, and &lt;code&gt;models.ts&lt;/code&gt; are different files, but all are associated with checker 0. Any work using checker 0's state needs the same &lt;code&gt;locks[0]&lt;/code&gt;. &lt;code&gt;app.ts&lt;/code&gt;, associated with checker 1, uses &lt;code&gt;locks[1]&lt;/code&gt; instead.&lt;/p&gt;

&lt;p&gt;Within one &lt;code&gt;Program&lt;/code&gt;, one file cannot use both &lt;code&gt;locks[0]&lt;/code&gt; and &lt;code&gt;locks[1]&lt;/code&gt;. &lt;code&gt;fileAssociations[serviceFile]&lt;/code&gt; returns one checker, and that checker has one position in &lt;code&gt;checkers&lt;/code&gt; and therefore one corresponding mutex. The mutex protects the checker's caches and in-progress state, which several files can share; it does not protect a file itself.&lt;/p&gt;

&lt;p&gt;The following simplified Go code shows only how the mutex works. Treat &lt;code&gt;work&lt;/code&gt; as type-checking one file with one checker. This is explanatory code, not the TypeScript implementation.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// withExclusiveChecker makes one checker exclusive for the duration of work.&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;withExclusiveChecker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lock&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;sync&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Mutex&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;work&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;lock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Lock&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;         &lt;span class="c"&gt;// Another goroutine using this lock waits until Unlock.&lt;/span&gt;
    &lt;span class="k"&gt;defer&lt;/span&gt; &lt;span class="n"&gt;lock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Unlock&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c"&gt;// Release the checker even if work returns early.&lt;/span&gt;

    &lt;span class="n"&gt;work&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c"&gt;// Safely use the checker's caches and semantic state.&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;defer&lt;/code&gt; is Go syntax for running a call when the function exits. Here, it makes sure that &lt;code&gt;Unlock()&lt;/code&gt; is not forgotten whether &lt;code&gt;work&lt;/code&gt; completes normally or returns early. In the following examples, the &lt;code&gt;go&lt;/code&gt; prefix starts a function call in a new goroutine.&lt;/p&gt;

&lt;p&gt;This is permitted parallel work. The two jobs use different checkers and therefore different mutexes, so neither waits for the other.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// Explanatory pseudocode: service.ts uses checker 0; app.ts uses checker 1.&lt;/span&gt;
&lt;span class="k"&gt;go&lt;/span&gt; &lt;span class="n"&gt;withExclusiveChecker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;locks&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;typeCheck&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;checkers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;serviceFile&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c"&gt;// Uses only checker 0's state.&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="k"&gt;go&lt;/span&gt; &lt;span class="n"&gt;withExclusiveChecker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;locks&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;typeCheck&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;checkers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;appFile&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c"&gt;// Uses only checker 1's state.&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;goroutine A: Lock locks[0] → check service.ts with checker 0 ───→ Unlock
goroutine B: Lock locks[1] → check app.ts with checker 1 ───────→ Unlock

The locks differ, so A and B can make progress at the same time.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is work that waits. &lt;code&gt;service.ts&lt;/code&gt; and &lt;code&gt;api.ts&lt;/code&gt; are different files, but both are assigned to checker 0 and therefore both need &lt;code&gt;locks[0]&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// Explanatory pseudocode: both jobs need the same checker 0.&lt;/span&gt;
&lt;span class="k"&gt;go&lt;/span&gt; &lt;span class="n"&gt;withExclusiveChecker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;locks&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;typeCheck&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;checkers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;serviceFile&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="k"&gt;go&lt;/span&gt; &lt;span class="n"&gt;withExclusiveChecker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;locks&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;typeCheck&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;checkers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;apiFile&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c"&gt;// Cannot progress until the earlier work unlocks checker 0.&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;goroutine A: Lock locks[0] → check service.ts with checker 0 ───→ Unlock
goroutine B: tries Lock locks[0] → waits ───────────────────────→ check api.ts → Unlock

B waits so that both jobs never touch one checker's state at once.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If two goroutines tried to check the same &lt;code&gt;service.ts&lt;/code&gt; file, the second would also wait on &lt;code&gt;locks[0]&lt;/code&gt;. However, the central purpose is not to protect one file; it is to prevent multiple jobs that share one checker from corrupting its caches and state at the same time.&lt;/p&gt;

&lt;p&gt;The earlier examples simplified mutex use by passing a lock directly. The real &lt;code&gt;getCheckerForFileExclusive&lt;/code&gt; instead receives a file and finds the required mutex itself. It has four steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Look up the file’s assigned checker with &lt;code&gt;fileAssociations[file]&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Find that checker’s position in &lt;code&gt;checkers&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Lock the mutex at the same position, &lt;code&gt;locks[idx]&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Return the checker and a release function for the caller to invoke when its work is done.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The following excerpt implements exactly that correspondence.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// Excerpt: the pool selects the file's checker, then protects that checker.&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;checkerPool&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;getCheckerForFileExclusive&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;ast&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SourceFile&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;checker&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Checker&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;createCheckers&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fileAssociations&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="c"&gt;// The association is created once for this program.&lt;/span&gt;
    &lt;span class="n"&gt;idx&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;slices&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Index&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;checkers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;locks&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;idx&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Lock&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c"&gt;// Do not let two callers mutate or read this checker unsafely.&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sync&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OnceFunc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;locks&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;idx&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Unlock&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c"&gt;// The caller releases the checker when its operation ends.&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;&lt;code&gt;c&lt;/code&gt; is the checker from step 1, and &lt;code&gt;idx&lt;/code&gt; is the checker’s position found in step 2. &lt;code&gt;p.locks[idx]&lt;/code&gt; is the mutex from step 3. The second returned value is the release function, which the caller uses when it is done with the checker. &lt;code&gt;sync.OnceFunc&lt;/code&gt; is a Go helper that makes the inner &lt;code&gt;Unlock()&lt;/code&gt; run only once, even if the release function is accidentally called twice.&lt;/p&gt;

&lt;p&gt;The reused unit here is a real checker with caches and mutable semantic state, not merely an execution slot. The source comment describes each partition as a checker with its own symbol, type, and instantiation caches. Multiple checkers may work at the same time, but one checker is never treated as a thread-safe shared bag of type state.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why file assignment is a graph-partitioning problem
&lt;/h2&gt;

&lt;p&gt;The implementation models the program’s in-project resolved imports as an undirected graph.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;source file  = a vertex
resolved import between in-project files = an edge
checker      = a partition of vertices
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;An import is directional in source code: &lt;code&gt;app.ts&lt;/code&gt; imports &lt;code&gt;service.ts&lt;/code&gt;. For cache locality, however, the relationship is useful in both directions. If two files share a checker, either one may benefit from type information that the other caused that checker to construct. The implementation therefore uses an undirected adjacency graph for this decision.&lt;/p&gt;

&lt;p&gt;The source comments describe the objective in a compact form:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;affinity(partition) - alpha × incrementalLoadPenalty(partition)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;“Affinity” means that a file gets a better score for joining a checker that already owns adjacent imported files. “Load penalty” means that a checker becomes less attractive as its estimated workload grows. Neither term should win unconditionally: putting every connected file together harms parallelism; spreading every file evenly can duplicate semantic cache construction.&lt;/p&gt;

&lt;p&gt;The production code uses a weighted adaptation of the FENNEL streaming graph-partitioning objective. You do not need to know FENNEL to understand the practical rule: score every checker by nearby import relationships, subtract a penalty for additional assigned work, and choose the best stable result.&lt;/p&gt;

&lt;p&gt;Here is a deliberately simplified version of that decision. It is explanatory code, not a copy of the TypeScript implementation.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// chooseChecker assigns one file to the most suitable checker.&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;chooseChecker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;file&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;neighbors&lt;/span&gt; &lt;span class="p"&gt;[][]&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;assignment&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;loads&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;weight&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;bestChecker&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;
    &lt;span class="n"&gt;bestScore&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;math&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Inf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;checker&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="k"&gt;range&lt;/span&gt; &lt;span class="n"&gt;loads&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;sharedNeighbors&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;adjacentFile&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="k"&gt;range&lt;/span&gt; &lt;span class="n"&gt;neighbors&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;assignment&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;adjacentFile&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;checker&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;sharedNeighbors&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt; &lt;span class="c"&gt;// Related files on one checker can reuse its semantic caches.&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="n"&gt;nextLoad&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;loads&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;checker&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;weight&lt;/span&gt;
        &lt;span class="n"&gt;loadPenalty&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="kt"&gt;float64&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nextLoad&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c"&gt;// The real compiler uses a calibrated convex penalty.&lt;/span&gt;
        &lt;span class="n"&gt;score&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="kt"&gt;float64&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sharedNeighbors&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;loadPenalty&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;score&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;bestScore&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;bestChecker&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bestScore&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;checker&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;score&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;bestChecker&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The real function is more careful. It estimates each file’s weight from syntax-node count, source-text length, and import count; it establishes a preferred maximum load; and it uses deterministic tie-breaking. Those details prevent a highly connected or expensive file from causing one checker to become the bottleneck.&lt;/p&gt;

&lt;h2&gt;
  
  
  Determinism is part of the design, not a by-product
&lt;/h2&gt;

&lt;p&gt;Parallel systems often become non-deterministic by accident. If a scheduler assigns the next completed task to the next available worker, two runs can choose different work sequences. That is acceptable for many batch jobs, but it is a poor default for a compiler: developers expect the same input and configuration to produce the same diagnostics and output.&lt;/p&gt;

&lt;p&gt;TypeScript 7 therefore computes associations before it begins file-oriented type-checking. The association code has explicit stable rules: it can preserve program order, or sort source files before declaration files and use their estimated weight; ties fall back to stable file indexes. Once constructed, &lt;code&gt;fileAssociations&lt;/code&gt; maps every source file to its selected checker.&lt;/p&gt;

&lt;p&gt;The subsequent diagnostics collection preserves the source-file index as well. The following excerpt shows the essential idea.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// Excerpt, shortened: each file's diagnostics go to its own stable slot.&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Program&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;collectCheckerDiagnosticsFromFiles&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sourceFiles&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;ast&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SourceFile&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;collect&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;checker&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Checker&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;ast&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SourceFile&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;ast&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Diagnostic&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;[][]&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;ast&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Diagnostic&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;diagnostics&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nb"&gt;make&lt;/span&gt;&lt;span class="p"&gt;([][]&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;ast&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Diagnostic&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sourceFiles&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

    &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;compilerCheckerPool&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;forEachCheckerGroupDo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sourceFiles&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SingleThreaded&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;checker&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Checker&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fileIndex&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;ast&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SourceFile&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;diagnostics&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;fileIndex&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;collect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;diagnostics&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Workers may complete at different times, but completion order does not determine where results are stored. Later processing can concatenate, sort, and deduplicate diagnostics. This is a broadly useful pattern whenever parallel work must retain a caller-visible order: assign stable input indexes before concurrency begins, store each result by index, and make ordering explicit at the merge boundary.&lt;/p&gt;

&lt;p&gt;There is an important limit. The TypeScript 7 release notes say that changing the number of checkers can expose rare order-dependent results. A fixed &lt;code&gt;--checkers&lt;/code&gt; value gives a stable partition for a stable input, but changing that value deliberately changes the partitioning configuration. Teams that need identical behavior across environments can therefore pin the checker count.&lt;/p&gt;

&lt;h2&gt;
  
  
  The worker count is a performance and memory dial
&lt;/h2&gt;

&lt;p&gt;The pool defaults to four checkers. &lt;code&gt;--checkers&lt;/code&gt; can choose another count, while &lt;code&gt;--singleThreaded&lt;/code&gt; sets the count to one and also disables parsing and emitting parallelism. The implementation caps the count to the number of program files and to 256, so a configuration cannot create more checkers than there are files to assign.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// Excerpt: the default and the two configuration paths.&lt;/span&gt;
&lt;span class="n"&gt;checkerCount&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="m"&gt;4&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;program&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SingleThreaded&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;checkerCount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;program&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Options&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Checkers&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;checkerCount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;checkerCount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;checkerCount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;program&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;files&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="m"&gt;256&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;More checkers can reduce wall-clock time when a project has enough independent work and CPU capacity. They also increase the chance that separate checker caches recreate similar type information. The release notes make this trade-off explicit: increasing &lt;code&gt;--checkers&lt;/code&gt; can speed up larger builds, but usually costs more memory. &lt;code&gt;--checkers 1&lt;/code&gt; removes inter-checker duplication at the cost of type-checking parallelism.&lt;/p&gt;

&lt;p&gt;This is why “use every CPU core” is not automatically the right compiler setting. The appropriate value depends on project shape, available memory, CI runner capacity, and whether type-checking or another phase is actually the bottleneck. A monorepo has a second control, &lt;code&gt;--builders&lt;/code&gt;, for project-reference builds; its concurrency combines multiplicatively with checker concurrency, so &lt;code&gt;--checkers 4 --builders 4&lt;/code&gt; may allow up to sixteen checkers at once.&lt;/p&gt;

&lt;h2&gt;
  
  
  What application developers can borrow from this design
&lt;/h2&gt;

&lt;p&gt;Most applications do not need graph partitioning. The transferable lessons are smaller and more practical.&lt;/p&gt;

&lt;p&gt;If a worker has expensive reusable state, treat locality as a first-class concern. A database connection with prepared statements, a compiler cache, a GPU context, or a model session may benefit from assigning related work consistently rather than sending every task to whichever worker happens to be idle.&lt;/p&gt;

&lt;p&gt;If the work has a visible order, preserve that order separately from execution. Parallelism should change how long a job takes, not randomly change the sequence of diagnostics, records, or user-facing messages.&lt;/p&gt;

&lt;p&gt;Finally, expose concurrency as a control rather than an opaque promise. TypeScript 7 gives users a default, a way to tune it for larger machines, and a fully single-threaded mode for constrained environments or debugging. That makes the performance trade-off observable and reversible.&lt;/p&gt;

&lt;h2&gt;
  
  
  Closing thought
&lt;/h2&gt;

&lt;p&gt;TypeScript 7’s speedup is not only a consequence of moving from JavaScript to Go. The native implementation also makes it practical to use shared-memory parallelism, and the checker pool shows the care required to use that parallelism well.&lt;/p&gt;

&lt;p&gt;The key idea is not “run type checking on four workers.” It is “partition a program so that related work can reuse state, unbalanced work does not dominate the build, and the same configuration produces the same observable result.” That is a useful design lens for compilers, build systems, and any service that needs both concurrency and trustworthy output.&lt;/p&gt;

&lt;h2&gt;
  
  
  Related project
&lt;/h2&gt;

&lt;p&gt;I develop &lt;a href="https://github.com/puffball1567/kinmokusei" rel="noopener noreferrer"&gt;Kinmokusei&lt;/a&gt;, a programming language with TypeScript-inspired syntax that compiles to readable Go. The project is relevant here because it works with the normal Go toolchain and ecosystem while exploring a different source-language design.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://devblogs.microsoft.com/typescript/announcing-typescript-7-0/" rel="noopener noreferrer"&gt;Announcing TypeScript 7.0&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/microsoft/TypeScript/blob/main/tsc/internal/compiler/checkerpool.go" rel="noopener noreferrer"&gt;TypeScript 7 checker-pool implementation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/microsoft/TypeScript/blob/main/tsc/internal/compiler/program.go" rel="noopener noreferrer"&gt;TypeScript 7 diagnostic collection&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/microsoft/TypeScript/blob/main/LICENSE.txt" rel="noopener noreferrer"&gt;Apache License 2.0 in the referenced repository&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>typescript</category>
      <category>go</category>
      <category>compiler</category>
      <category>performance</category>
    </item>
    <item>
      <title>Inside TypeScript 7: Arena Allocation and AST Memory Layout</title>
      <dc:creator>puffball1567</dc:creator>
      <pubDate>Sun, 20 Sep 2026 07:11:52 +0000</pubDate>
      <link>https://dev.to/puffball1567/inside-typescript-7-arena-allocation-and-ast-memory-layout-4l1o</link>
      <guid>https://dev.to/puffball1567/inside-typescript-7-arena-allocation-and-ast-memory-layout-4l1o</guid>
      <description>&lt;p&gt;When a compiler reads source code, it first turns the text into a tree called an &lt;strong&gt;AST&lt;/strong&gt; (abstract syntax tree). Each part of the source—a function call, identifier, or expression—becomes a small object called a &lt;strong&gt;node&lt;/strong&gt; in that tree. A large project can therefore create millions of nodes. TypeScript 7's Go compiler uses a small reusable allocator, called a typed arena, for selected node types such as &lt;code&gt;CallExpression&lt;/code&gt;. This article follows the path from creating a call-expression node to returning the common &lt;code&gt;*Node&lt;/code&gt; value, then separates what this allocator guarantees from what it does not.&lt;/p&gt;

&lt;p&gt;The intended reader works with Go or TypeScript but does not need compiler-internals experience. The topic is a practical memory-layout question: what a typed arena changes about AST allocation, and what it does not.&lt;/p&gt;

&lt;p&gt;I am not part of the TypeScript team. This is an independent technical reading of the publicly available source code and documentation, not an official explanation of the implementation or its design decisions.&lt;/p&gt;

&lt;h2&gt;
  
  
  What problem is this arena solving?
&lt;/h2&gt;

&lt;p&gt;A compiler creates many nodes while it reads and analyzes source files. Here, an &lt;strong&gt;allocation&lt;/strong&gt; means asking Go to reserve memory for one new object. If every node is created separately, Go's memory manager must handle many individual allocations, and objects of the same kind can be placed far apart in memory. TypeScript 7 gives some specific node structs—such as &lt;code&gt;CallExpression&lt;/code&gt;—their own arenas. This appears intended to create repeated nodes in small batches instead.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why care about individual heap allocations?
&lt;/h2&gt;

&lt;p&gt;Go can allocate objects efficiently, but each allocation still requires work. Objects that must outlive the current function are typically placed in the &lt;strong&gt;heap&lt;/strong&gt;, the area of memory that Go's garbage collector manages. Creating many separate heap objects increases allocator work and gives the garbage collector more objects to keep track of. An arena does not remove the pointers stored inside an AST node, so it does not eliminate all garbage-collection work. Instead, it can replace many small allocations with a smaller number of slice allocations, where one slice holds many values of the same node type.&lt;/p&gt;

&lt;p&gt;The difference is easiest to see as a shape of allocations, not as a special kind of memory.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Direct allocation: one runtime-managed object per node

&amp;amp;CallExpression{} ──► [CallExpression]
&amp;amp;CallExpression{} ──► [CallExpression]
&amp;amp;CallExpression{} ──► [CallExpression]
                         ...
runtime sees many separately allocated objects

Typed arena: one backing slice holds several values of the same type

NodeFactory
  └─ callExpressionArena ──► [CallExpression | CallExpression | CallExpression | ...]
                                  ^ each New() returns a pointer to one element
runtime sees a much smaller number of backing-slice allocations
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This allocation pattern may also fit an AST's lifetime. A parsed tree is commonly kept as a whole while later compiler stages use it; a compiler usually does not discard one call-expression node at a time. Keeping same-type values in batches can therefore be a reasonable trade-off. In one batch, values sit next to one another in the slice's underlying array. When that batch fills, the arena creates a new array instead of moving the old one, so pointers already returned to callers remain valid.&lt;/p&gt;

&lt;p&gt;The repository does not state a universal rationale for every arena choice, so these are the properties and typical benefits of the pattern—not a claim about an unstated TypeScript 7 performance result. It also does not mean that the whole AST becomes one contiguous block or that every tree walk becomes fast. The rest of this article shows the narrower mechanism for &lt;code&gt;CallExpression&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This is a companion to &lt;a href="https://dev.to/puffball1567/inside-typescript-go-how-its-ast-nodes-are-built-and-traversed"&gt;Inside TypeScript 7: How Its AST Nodes Are Built and Traversed&lt;/a&gt;. The two articles can also be read independently.&lt;/p&gt;

&lt;h2&gt;
  
  
  Source and license note
&lt;/h2&gt;

&lt;p&gt;This article discusses the publicly available &lt;a href="https://github.com/microsoft/TypeScript" rel="noopener noreferrer"&gt;&lt;code&gt;microsoft/TypeScript&lt;/code&gt;&lt;/a&gt; repository. TypeScript 7's Go compiler is under &lt;a href="https://github.com/microsoft/TypeScript/tree/main/tsc" rel="noopener noreferrer"&gt;&lt;code&gt;tsc/&lt;/code&gt;&lt;/a&gt; and is licensed under the &lt;a href="https://github.com/microsoft/TypeScript/blob/main/LICENSE.txt" rel="noopener noreferrer"&gt;Apache License 2.0&lt;/a&gt;. Code blocks marked as excerpts are short portions of the upstream source; diagrams and explanatory text are original. File-and-line citations in this article are pinned to source snapshot &lt;a href="https://github.com/microsoft/TypeScript/tree/f29aeb9f825d96feea27841f3f7342dbf0df68a8" rel="noopener noreferrer"&gt;&lt;code&gt;f29aeb9&lt;/code&gt;&lt;/a&gt; so that later changes to &lt;code&gt;main&lt;/code&gt; do not move the cited code.&lt;/p&gt;

&lt;p&gt;Source excerpts are from Microsoft’s TypeScript project:&lt;br&gt;
&lt;a href="https://github.com/microsoft/TypeScript" rel="noopener noreferrer"&gt;https://github.com/microsoft/TypeScript&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Licensed under the Apache License, Version 2.0.&lt;br&gt;
A copy of the License is available at:&lt;br&gt;
&lt;a href="https://github.com/microsoft/TypeScript/blob/main/LICENSE.txt" rel="noopener noreferrer"&gt;https://github.com/microsoft/TypeScript/blob/main/LICENSE.txt&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  First, what an arena allocator usually means
&lt;/h2&gt;

&lt;p&gt;An &lt;strong&gt;arena allocator&lt;/strong&gt; is a way to manage the lifetime of a group of objects together. Instead of requesting memory separately for every object and releasing each one separately, a program creates an arena, asks it for objects as needed, and later discards or resets the entire arena at once. This is useful when a group of objects is expected to become unnecessary at roughly the same time—for example, temporary data used while processing one request or one compiler phase.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Typical arena lifetime

create arena
    │
    ├─ allocate object A
    ├─ allocate object B
    └─ allocate object C
    │
all three objects become unnecessary together
    │
reset or discard the arena
    └─ release the group as one unit
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The important idea is not that all memory is reserved up front. An arena can obtain more memory in batches as it fills. What makes it an arena is that allocation is grouped and its lifetime is commonly controlled at the arena level rather than one object at a time.&lt;/p&gt;

&lt;h2&gt;
  
  
  How TypeScript 7's &lt;code&gt;Arena&lt;/code&gt; differs from a classic arena
&lt;/h2&gt;

&lt;p&gt;TypeScript 7 uses the name &lt;code&gt;Arena&lt;/code&gt;, but its implementation is deliberately smaller and works within ordinary Go memory management. It is an ordinary library data structure, not a special feature of the Go runtime. It stores a growable list (a slice) of one node type and returns pointers to entries in that list. It has &lt;code&gt;New()&lt;/code&gt; and slice-allocation methods, but no public &lt;code&gt;Free()&lt;/code&gt; or &lt;code&gt;Reset()&lt;/code&gt; method that immediately releases all entries.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Classic arena                                 TypeScript 7 core.Arena[T]

program controls reset / discard              no explicit Free or Reset API
objects can be released as one region         Go's GC reclaims memory when nothing references it
often owns a general-purpose byte region      owns typed slices such as []CallExpression
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That distinction matters. A &lt;code&gt;NodeFactory&lt;/code&gt; owns an &lt;code&gt;Arena[CallExpression]&lt;/code&gt;, but Go does not free its memory merely because one call-expression node is no longer needed. The backing arrays become collectible only after the factory, the AST nodes, and any other references keeping those arrays alive are no longer reachable. This design groups allocation, but it does not introduce manual memory management or bypass Go's garbage collector.&lt;/p&gt;

&lt;p&gt;The relevant TypeScript 7 implementation is short. Source: &lt;a href="https://github.com/microsoft/TypeScript/blob/f29aeb9f825d96feea27841f3f7342dbf0df68a8/tsc/internal/core/arena.go#L7-L21" rel="noopener noreferrer"&gt;&lt;code&gt;tsc/internal/core/arena.go&lt;/code&gt;, lines 7–21&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// Annotated excerpt: internal/core/arena.go&lt;/span&gt;
&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;Arena&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt; &lt;span class="n"&gt;any&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt; &lt;span class="c"&gt;// One backing slice for entries of the same concrete type.&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Arena&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="n"&gt;New&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nb"&gt;cap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;nextSize&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;nextArenaSize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="c"&gt;// Decide the capacity of the next batch.&lt;/span&gt;
        &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;slices&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Grow&lt;/span&gt;&lt;span class="p"&gt;[[]&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;nextSize&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c"&gt;// Start a new backing slice; old pointers stay valid.&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c"&gt;// The next unused element becomes the new entry.&lt;/span&gt;
    &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="c"&gt;// Extend the slice length so that entry belongs to the arena.&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="c"&gt;// Return a pointer to that concrete value.&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;Arena[CallExpression]&lt;/code&gt; therefore means “an arena whose list contains &lt;code&gt;CallExpression&lt;/code&gt; values.” Its &lt;code&gt;New()&lt;/code&gt; method returns a pointer, &lt;code&gt;*CallExpression&lt;/code&gt;, to one newly reserved entry. The &lt;a href="https://pkg.go.dev/slices#Grow" rel="noopener noreferrer"&gt;standard-library documentation for &lt;code&gt;slices.Grow&lt;/code&gt;&lt;/a&gt; explains the capacity-growth operation used here.&lt;/p&gt;

&lt;h2&gt;
  
  
  TypeScript 7 keeps an arena per selected node type
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;NodeFactory&lt;/code&gt; is the object responsible for constructing AST nodes. Think of it as the place that both knows how to fill in a node's fields and owns the typed storage used while making that kind of node. For example, after the parser recognizes &lt;code&gt;client?.request&amp;lt;User&amp;gt;(id)&lt;/code&gt;, it can ask the factory to make one call-expression node from the pieces it already recognized.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;parser has recognized: client?.request&amp;lt;User&amp;gt;(id)
        │
        │ passes the four meaningful pieces to NodeFactory
        ▼
NewCallExpression(expression, ?., &amp;lt;User&amp;gt;, (id))
        │
        ├─ reserve an empty CallExpression entry from its arena
        ├─ put those four pieces into that entry
        └─ return the entry's shared *Node portion
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;NodeFactory&lt;/code&gt; has many fields. The two fields relevant to this article are its typed arena and its &lt;code&gt;hooks&lt;/code&gt; field. &lt;code&gt;hooks&lt;/code&gt; is simply a group of optional functions that a caller can provide while creating a factory; the next section shows exactly when &lt;code&gt;OnCreate&lt;/code&gt; is called. Sources: &lt;a href="https://github.com/microsoft/TypeScript/blob/f29aeb9f825d96feea27841f3f7342dbf0df68a8/tsc/internal/ast/ast.go#L61-L65" rel="noopener noreferrer"&gt;&lt;code&gt;tsc/internal/ast/ast.go&lt;/code&gt;, lines 61–65&lt;/a&gt; for the hooks and &lt;a href="https://github.com/microsoft/TypeScript/blob/f29aeb9f825d96feea27841f3f7342dbf0df68a8/tsc/internal/ast/ast_generated.go#L20-L25" rel="noopener noreferrer"&gt;&lt;code&gt;tsc/internal/ast/ast_generated.go&lt;/code&gt;, lines 20–25&lt;/a&gt; for the typed arena field.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// Annotated excerpts, shortened to the fields relevant here.&lt;/span&gt;
&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;NodeFactoryHooks&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;OnCreate&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Node&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;                 &lt;span class="c"&gt;// Optional function called after creating a node.&lt;/span&gt;
    &lt;span class="n"&gt;OnUpdate&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Node&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;original&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Node&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c"&gt;// Optional function called after updating a node.&lt;/span&gt;
    &lt;span class="n"&gt;OnClone&lt;/span&gt;  &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Node&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;original&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Node&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c"&gt;// Optional function called after cloning a node.&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;NodeFactory&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;hooks&lt;/span&gt;                &lt;span class="n"&gt;NodeFactoryHooks&lt;/span&gt;          &lt;span class="c"&gt;// The optional functions supplied when this factory was created.&lt;/span&gt;
    &lt;span class="n"&gt;callExpressionArena&lt;/span&gt; &lt;span class="n"&gt;core&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Arena&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;CallExpression&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="c"&gt;// Storage used when constructing CallExpression values.&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The full &lt;code&gt;CallExpression&lt;/code&gt; object stores information that only a call expression needs: what is being called, whether &lt;code&gt;?.&lt;/code&gt; is present, and its type and ordinary arguments. It also contains a shared &lt;code&gt;Node&lt;/code&gt; portion used by every kind of AST node. The generated type and factory then look like this. Source: &lt;a href="https://github.com/microsoft/TypeScript/blob/f29aeb9f825d96feea27841f3f7342dbf0df68a8/tsc/internal/ast/ast_generated.go#L4256-L4274" rel="noopener noreferrer"&gt;&lt;code&gt;tsc/internal/ast/ast_generated.go&lt;/code&gt;, lines 4256–4274&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// Annotated excerpt: internal/ast/ast_generated.go&lt;/span&gt;
&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;CallExpression&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;LeftHandSideExpressionBase&lt;/span&gt; &lt;span class="c"&gt;// Embeds common behavior for an expression that can appear on the left-hand side.&lt;/span&gt;
    &lt;span class="n"&gt;DeclarationBase&lt;/span&gt;            &lt;span class="c"&gt;// Embeds declaration-related shared state.&lt;/span&gt;
    &lt;span class="n"&gt;CompositeBase&lt;/span&gt;              &lt;span class="c"&gt;// Reaches the shared Node header through the embedded base types.&lt;/span&gt;
    &lt;span class="n"&gt;Expression&lt;/span&gt;       &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Expression&lt;/span&gt;        &lt;span class="c"&gt;// The expression being called, such as client.request.&lt;/span&gt;
    &lt;span class="n"&gt;QuestionDotToken&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;QuestionDotToken&lt;/span&gt;  &lt;span class="c"&gt;// The ?. token, when this is an optional call.&lt;/span&gt;
    &lt;span class="n"&gt;TypeArguments&lt;/span&gt;    &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;TypeList&lt;/span&gt;          &lt;span class="c"&gt;// Type arguments such as &amp;lt;User&amp;gt;, when present.&lt;/span&gt;
    &lt;span class="n"&gt;Arguments&lt;/span&gt;        &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;ElementList&lt;/span&gt;       &lt;span class="c"&gt;// Ordinary call arguments such as (id).&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;NodeFactory&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;NewCallExpression&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;expression&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Expression&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;questionDotToken&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;QuestionDotToken&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;typeArguments&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;TypeList&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;arguments&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;ElementList&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;flags&lt;/span&gt; &lt;span class="n"&gt;NodeFlags&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Node&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;callExpressionArena&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;New&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c"&gt;// Reserve a *CallExpression entry in this factory's arena.&lt;/span&gt;
    &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Expression&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;expression&lt;/span&gt;         &lt;span class="c"&gt;// Store the callee.&lt;/span&gt;
    &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;QuestionDotToken&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;questionDotToken&lt;/span&gt; &lt;span class="c"&gt;// Store ?. or nil.&lt;/span&gt;
    &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TypeArguments&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;typeArguments&lt;/span&gt;   &lt;span class="c"&gt;// Store &amp;lt;T&amp;gt; arguments or nil.&lt;/span&gt;
    &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Arguments&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;arguments&lt;/span&gt;           &lt;span class="c"&gt;// Store the ordinary argument list.&lt;/span&gt;
    &lt;span class="n"&gt;node&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;newNode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;KindCallExpression&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c"&gt;// Initialize and return the embedded common Node.&lt;/span&gt;
    &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Flags&lt;/span&gt; &lt;span class="o"&gt;|=&lt;/span&gt; &lt;span class="n"&gt;flags&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;NodeFlagsOptionalChain&lt;/span&gt; &lt;span class="c"&gt;// Preserve the optional-chain bit from the caller.&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt; &lt;span class="c"&gt;// Callers use the common *Node API rather than *CallExpression directly.&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For &lt;code&gt;client?.request&amp;lt;User&amp;gt;(id)&lt;/code&gt;, those fields represent the called expression, the optional-chain token, type arguments, and ordinary arguments. Read the code one line at a time:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;f.callExpressionArena.New()&lt;/code&gt; returns the address of one empty &lt;code&gt;CallExpression&lt;/code&gt; already stored in the arena. The local variable named &lt;code&gt;data&lt;/code&gt; receives that address.&lt;/li&gt;
&lt;li&gt;Lines such as &lt;code&gt;data.Expression = expression&lt;/code&gt; write values into that one CallExpression.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;f.newNode(KindCallExpression, data)&lt;/code&gt; receives the address of that same CallExpression. It initializes the shared &lt;code&gt;Node&lt;/code&gt; portion contained inside the object.&lt;/li&gt;
&lt;li&gt;The returned &lt;code&gt;*Node&lt;/code&gt; is not a new, separate object. It is the address of the common Node portion inside the same CallExpression.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In short, the factory reserves one CallExpression in the arena, fills its call-specific and shared fields in order, then returns it in a form that later code can treat as a general AST node. &lt;a href="https://github.com/microsoft/TypeScript/blob/f29aeb9f825d96feea27841f3f7342dbf0df68a8/tsc/internal/ast/ast_generated.go#L4256-L4289" rel="noopener noreferrer"&gt;The full generated implementation, lines 4256–4289&lt;/a&gt; also shows its child-traversal method.&lt;/p&gt;

&lt;p&gt;The allocation and initialization path is therefore as follows. There is one object throughout this flow—not one &lt;code&gt;CallExpression&lt;/code&gt; object plus another &lt;code&gt;Node&lt;/code&gt; object.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;NewCallExpression(...)
        │
        ├─ callExpressionArena.New()
        │       └─ returns one *CallExpression inside a batch
        │
        ├─ write Expression / TypeArguments / Arguments into that value
        │
        └─ newNode(KindCallExpression, data)
                └─ initializes the shared Node portion inside that same object, then returns *Node
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  What &lt;code&gt;NodeFactory.newNode&lt;/code&gt; does
&lt;/h3&gt;

&lt;p&gt;There are two functions named &lt;code&gt;newNode&lt;/code&gt;. The call in &lt;code&gt;NewCallExpression&lt;/code&gt; is the &lt;strong&gt;method&lt;/strong&gt; on &lt;code&gt;NodeFactory&lt;/code&gt;, written &lt;code&gt;f.newNode(...)&lt;/code&gt;. Its job is deliberately small: count that the factory made one more node, then pass the work and the factory's hooks to the package-level &lt;code&gt;newNode(...)&lt;/code&gt; function.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// Excerpt: tsc/internal/ast/ast.go, lines 75–88.&lt;/span&gt;
&lt;span class="c"&gt;// nodeData and Node are defined in the next section.&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;NodeFactory&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;newNode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;kind&lt;/span&gt; &lt;span class="n"&gt;Kind&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="n"&gt;nodeData&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Node&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;nodeCount&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;                 &lt;span class="c"&gt;// Record that this factory created one more AST node.&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;newNode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;hooks&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c"&gt;// Delegate the shared-field initialization below.&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;// This package-level helper is the second function with the same name.&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;newNode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;kind&lt;/span&gt; &lt;span class="n"&gt;Kind&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="n"&gt;nodeData&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;hooks&lt;/span&gt; &lt;span class="n"&gt;NodeFactoryHooks&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Node&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AsNode&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c"&gt;// Find the common Node portion inside the full object.&lt;/span&gt;
    &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Loc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;core&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UndefinedTextRange&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c"&gt;// Give its source range an initial value.&lt;/span&gt;
    &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Kind&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;kind&lt;/span&gt; &lt;span class="c"&gt;// Record the supplied kind; here, it is KindCallExpression.&lt;/span&gt;
    &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="c"&gt;// Keep a way back to the full CallExpression object.&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;hooks&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OnCreate&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;hooks&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OnCreate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c"&gt;// Run an optional callback after initialization.&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="c"&gt;// Return the common Node portion inside that same object.&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  When does &lt;code&gt;OnCreate&lt;/code&gt; run?
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;OnCreate&lt;/code&gt; is not an event that fires automatically at some later time. It is an optional function value supplied when a factory is created. It runs synchronously during node creation only when both of these conditions are true:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The code that created this &lt;code&gt;NodeFactory&lt;/code&gt; provided a non-&lt;code&gt;nil&lt;/code&gt; &lt;code&gt;OnCreate&lt;/code&gt; callback.&lt;/li&gt;
&lt;li&gt;A factory method reaches the package-level &lt;code&gt;newNode&lt;/code&gt;, which checks &lt;code&gt;hooks.OnCreate != nil&lt;/code&gt; and calls it directly.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The printer is one concrete setup site:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// Shortened excerpts: tsc/internal/printer/factory.go, lines 18–24,&lt;/span&gt;
&lt;span class="c"&gt;// and tsc/internal/printer/emitcontext.go, lines 71–73.&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;NewNodeFactory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;EmitContext&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;NodeFactory&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;NodeFactory&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;NodeFactory&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;ast&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewNodeFactory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ast&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NodeFactoryHooks&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;OnCreate&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;onCreate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c"&gt;// Register this function on the AST factory.&lt;/span&gt;
            &lt;span class="c"&gt;// OnUpdate and OnClone are also registered in the real code.&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;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;EmitContext&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;onCreate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;ast&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Node&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Flags&lt;/span&gt; &lt;span class="o"&gt;|=&lt;/span&gt; &lt;span class="n"&gt;ast&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NodeFlagsSynthesized&lt;/span&gt; &lt;span class="c"&gt;// Mark a printer-created node as synthesized.&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;create factory with OnCreate callback
        │
        ▼
factory.NewCallExpression(...)
        │
        ▼
newNode(...)
        │
        ├─ OnCreate is nil  ───► do nothing
        └─ OnCreate is set  ───► call it immediately with the new *Node
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For example, the printer creates its factory with &lt;code&gt;context.onCreate&lt;/code&gt; as the callback. That callback marks each node created through this printer factory as synthesized. A factory created with &lt;code&gt;ast.NodeFactoryHooks{}&lt;/code&gt; instead has no callback, so this step is skipped. Sources: &lt;a href="https://github.com/microsoft/TypeScript/blob/f29aeb9f825d96feea27841f3f7342dbf0df68a8/tsc/internal/printer/factory.go#L18-L24" rel="noopener noreferrer"&gt;&lt;code&gt;tsc/internal/printer/factory.go&lt;/code&gt;, lines 18–24&lt;/a&gt;, &lt;a href="https://github.com/microsoft/TypeScript/blob/f29aeb9f825d96feea27841f3f7342dbf0df68a8/tsc/internal/printer/emitcontext.go#L71-L73" rel="noopener noreferrer"&gt;&lt;code&gt;tsc/internal/printer/emitcontext.go&lt;/code&gt;, lines 71–73&lt;/a&gt;, and &lt;a href="https://github.com/microsoft/TypeScript/blob/f29aeb9f825d96feea27841f3f7342dbf0df68a8/tsc/internal/ast/ast.go#L75-L83" rel="noopener noreferrer"&gt;&lt;code&gt;tsc/internal/ast/ast.go&lt;/code&gt;, lines 75–83&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;So the two names represent two levels of responsibility:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;f.newNode(...)                  newNode(...)
NodeFactory method              package-level helper

increments this factory's       fills the common Node fields
node count                      and runs an optional creation hook
        │                                  │
        └────────────── calls ────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The factory links the common node header back to the concrete object
&lt;/h2&gt;

&lt;p&gt;Every AST node needs some information that is common to all nodes: its kind, flags, source range, and parent. TypeScript 7 puts this shared information in &lt;code&gt;Node&lt;/code&gt;, which works as a small common header inside the full object. The private &lt;code&gt;nodeData&lt;/code&gt; interface describes what a full node object must provide. The package-level &lt;code&gt;newNode&lt;/code&gt; shown above uses its &lt;code&gt;AsNode&lt;/code&gt; method to find that embedded common header. Sources: &lt;a href="https://github.com/microsoft/TypeScript/blob/f29aeb9f825d96feea27841f3f7342dbf0df68a8/tsc/internal/ast/ast.go#L75-L88" rel="noopener noreferrer"&gt;&lt;code&gt;tsc/internal/ast/ast.go&lt;/code&gt;, lines 75–88&lt;/a&gt;, &lt;a href="https://github.com/microsoft/TypeScript/blob/f29aeb9f825d96feea27841f3f7342dbf0df68a8/tsc/internal/ast/ast.go#L180-L187" rel="noopener noreferrer"&gt;&lt;code&gt;lines 180–187&lt;/code&gt;&lt;/a&gt;, and &lt;a href="https://github.com/microsoft/TypeScript/blob/f29aeb9f825d96feea27841f3f7342dbf0df68a8/tsc/internal/ast/ast.go#L1184-L1205" rel="noopener noreferrer"&gt;&lt;code&gt;lines 1184–1205&lt;/code&gt;&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// Annotated excerpt, with nodeData shortened to the method used below.&lt;/span&gt;
&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;Node&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Kind&lt;/span&gt;   &lt;span class="n"&gt;Kind&lt;/span&gt;            &lt;span class="c"&gt;// Identifies which concrete AST node this is.&lt;/span&gt;
    &lt;span class="n"&gt;Flags&lt;/span&gt;  &lt;span class="n"&gt;NodeFlags&lt;/span&gt;       &lt;span class="c"&gt;// Holds shared syntactic and analysis flags.&lt;/span&gt;
    &lt;span class="n"&gt;Loc&lt;/span&gt;    &lt;span class="n"&gt;core&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TextRange&lt;/span&gt;  &lt;span class="c"&gt;// Records the node's source range.&lt;/span&gt;
    &lt;span class="n"&gt;Parent&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Node&lt;/span&gt;           &lt;span class="c"&gt;// Points to the enclosing node when a parent has been assigned.&lt;/span&gt;
    &lt;span class="n"&gt;data&lt;/span&gt;   &lt;span class="n"&gt;nodeData&lt;/span&gt;        &lt;span class="c"&gt;// Points back to the full concrete object, such as *CallExpression.&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;nodeData&lt;/span&gt; &lt;span class="k"&gt;interface&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;AsNode&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Node&lt;/span&gt; &lt;span class="c"&gt;// Returns the common Node embedded in the concrete object.&lt;/span&gt;
    &lt;span class="c"&gt;// The real interface also defines traversal, cloning, and analysis methods.&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;CallExpression&lt;/code&gt; reaches &lt;code&gt;Node&lt;/code&gt; through its embedded base types. In other words, the full call-expression object contains the shared &lt;code&gt;Node&lt;/code&gt; portion inside it. &lt;code&gt;newNode&lt;/code&gt; obtains that inner &lt;code&gt;Node&lt;/code&gt; as &lt;code&gt;n&lt;/code&gt;, then stores a reference to the full &lt;code&gt;*CallExpression&lt;/code&gt; back in &lt;code&gt;n.data&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;*CallExpression allocated by callExpressionArena
├─ embedded base types
│  └─ shared Node  ← returned *Node
│     └─ data ────────────────┐
├─ Expression                 │
├─ TypeArguments              │
└─ Arguments                  │
                              │
data holds *CallExpression ───┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is why later code can start from a generic &lt;code&gt;*Node&lt;/code&gt;, inspect &lt;code&gt;Kind&lt;/code&gt;, and recover its concrete representation when necessary. The cited lines above show the production definitions, including the full &lt;code&gt;nodeData&lt;/code&gt; interface.&lt;/p&gt;

&lt;h2&gt;
  
  
  What this memory layout guarantees
&lt;/h2&gt;

&lt;p&gt;Within one active arena slice, entries of the same type are adjacent array elements. If a factory creates several &lt;code&gt;CallExpression&lt;/code&gt; values while the slice still has capacity, their &lt;code&gt;CallExpression&lt;/code&gt; structs are stored together. That can improve locality when code repeatedly works on the same node type.&lt;/p&gt;

&lt;p&gt;When the slice is full, this arena does not grow the old backing array in place. It creates a new backing slice with &lt;code&gt;slices.Grow(nil, nextSize)&lt;/code&gt; and begins placing subsequent values there. Existing pointers still point to elements in the old backing array; the allocator never moves or overwrites those entries. As long as the AST retains those pointers, Go retains the old allocation too.&lt;/p&gt;

&lt;p&gt;The result is better described as &lt;strong&gt;type-local batches&lt;/strong&gt; than as “one contiguous AST.”&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CallExpression arena
├─ batch A: CallExpression, CallExpression, CallExpression, ...
└─ batch B: CallExpression, CallExpression, ...

Identifier arena
└─ its own batches of Identifier values

Other node types
└─ their own allocation strategies and locations
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The pointers inside a call expression can lead to identifiers, argument lists, tokens, or other node types stored elsewhere. Tree order, source order, and physical memory order are therefore different concepts.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why not put every node type in one arena?
&lt;/h2&gt;

&lt;p&gt;The repository does not state one universal policy for which node types receive an arena. The generated factory includes arenas for many types, while some constructors allocate with a direct &lt;code&gt;&amp;amp;Type{}&lt;/code&gt; expression. It would be speculation to treat the presence of an arena as proof that one node type is always more important than another.&lt;/p&gt;

&lt;p&gt;What can be stated from the code is narrower: the design permits the factory to choose an allocation strategy per concrete type while preserving the same returned &lt;code&gt;*Node&lt;/code&gt; API. Callers of &lt;code&gt;NewCallExpression&lt;/code&gt; do not need to know whether the concrete object came from an arena or a direct allocation.&lt;/p&gt;

&lt;h2&gt;
  
  
  The trade-off: locality and allocation patterns, not automatic speed
&lt;/h2&gt;

&lt;p&gt;An arena can reduce per-object allocation work and group same-type objects, but it adds a custom lifetime and growth model. It also does not make arbitrary AST traversals cache-friendly: a traversal frequently follows pointers between different types and different batches.&lt;/p&gt;

&lt;p&gt;TypeScript 7's overall performance comes from more than this allocator. The TypeScript team attributes TypeScript 7's gains to native code, shared-memory parallelism, and other optimizations; an AST arena alone does not explain the result. &lt;a href="https://devblogs.microsoft.com/typescript/announcing-typescript-7-0/" rel="noopener noreferrer"&gt;The TypeScript 7 announcement&lt;/a&gt; provides the broader performance context.&lt;/p&gt;

&lt;h3&gt;
  
  
  Reference measurement: does the expected allocation effect appear?
&lt;/h3&gt;

&lt;p&gt;The mechanism above suggests that replacing one allocation per node with type-local batches should greatly reduce the number of objects allocated and tracked by the runtime. I tested that expectation with a small Go benchmark that constructs one live 100,000-node binary tree. The direct version allocates each node with &lt;code&gt;&amp;amp;node{}&lt;/code&gt;; the arena version matches TypeScript 7's slice-backed growth policy, doubling a batch up to 256 elements and then allocating additional 256-element batches. This is an explanatory microbenchmark, not a TypeScript compiler benchmark: it does not parse TypeScript, type-check, emit output, or measure the production node types.&lt;/p&gt;

&lt;p&gt;On Go 1.23.5, Linux/amd64, and one CPU on an AMD Ryzen 5 5600H, five runs produced the following medians. The final column comes from a separate forced-GC measurement after constructing one retained tree; it is not a &lt;code&gt;testing.B&lt;/code&gt; allocation result.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Construction strategy&lt;/th&gt;
&lt;th&gt;Median time for 100,000 nodes&lt;/th&gt;
&lt;th&gt;Bytes/op&lt;/th&gt;
&lt;th&gt;Allocations/op&lt;/th&gt;
&lt;th&gt;Retained heap objects after one live tree&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Direct &lt;code&gt;&amp;amp;node{}&lt;/code&gt; allocation&lt;/td&gt;
&lt;td&gt;13.59 ms&lt;/td&gt;
&lt;td&gt;7,202,816&lt;/td&gt;
&lt;td&gt;100,001&lt;/td&gt;
&lt;td&gt;99,994&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Typed growing arena&lt;/td&gt;
&lt;td&gt;9.50 ms&lt;/td&gt;
&lt;td&gt;6,422,288&lt;/td&gt;
&lt;td&gt;350&lt;/td&gt;
&lt;td&gt;355&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;In this deliberately narrow setup, the expected effect appeared: batching reduced allocation operations by about 99.7% and construction time by about 30%. Retained bytes were also lower here, but that is not guaranteed. An arena keeps its backing batches alive, including unused capacity in the final batch. This result supports the expected benefit of reducing individually allocated and tracked objects; it does not prove that an arena is always faster or smaller for every AST workload.&lt;/p&gt;

&lt;p&gt;The reusable lesson is to measure the allocation and traversal pattern first. A typed arena is useful when a program creates many short- or similarly-lived objects of known concrete types. It is not a default replacement for ordinary Go allocation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Kinmokusei
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/puffball1567/kinmokusei" rel="noopener noreferrer"&gt;Kinmokusei&lt;/a&gt; is a programming language with TypeScript-inspired syntax that compiles to readable Go. It is designed to work with the normal Go toolchain and package ecosystem.&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>go</category>
      <category>compiler</category>
      <category>performance</category>
    </item>
    <item>
      <title>Jev vs LLMs: When Software Needs a Decision Instead of Generated Text</title>
      <dc:creator>puffball1567</dc:creator>
      <pubDate>Fri, 18 Sep 2026 06:36:22 +0000</pubDate>
      <link>https://dev.to/puffball1567/jev-vs-llms-when-software-needs-a-decision-instead-of-generated-text-3ha4</link>
      <guid>https://dev.to/puffball1567/jev-vs-llms-when-software-needs-a-decision-instead-of-generated-text-3ha4</guid>
      <description>&lt;p&gt;Many developers first meet Jev and ask a reasonable question: “Is this just a smaller LLM that returns JSON?” The useful answer is no—but it also is not a replacement for an LLM. Jev is designed for a narrower job: evaluate a piece of application state against questions that the program defines, then return typed decisions with probabilities and confidence. An LLM is designed to generate language. Once that difference is clear, the practical boundary becomes much easier to see.&lt;/p&gt;

&lt;p&gt;This article explains the model in plain terms, compares its strengths and limits with text-generating LLMs, and shows where a combined design is more useful than choosing one model for everything. Jev was announced by TypeSafe AI on September 15, 2026 and is currently an early-access product, so providers, API details, prices, and performance claims should be checked again before production adoption.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sources and scope
&lt;/h2&gt;

&lt;p&gt;Product behavior and the API-shaped example below are based on TypeSafe AI's &lt;a href="https://typesafe.ai/blog/introducing-system-one-models-and-jev" rel="noopener noreferrer"&gt;Jev introduction&lt;/a&gt; and Cloudflare's &lt;a href="https://developers.cloudflare.com/ai/models/typesafe/jev/" rel="noopener noreferrer"&gt;Jev model documentation&lt;/a&gt;. Performance and price figures attributed to TypeSafe are vendor claims, not independent benchmarks. The diagrams, explanations, and application code in this article are original.&lt;/p&gt;

&lt;h2&gt;
  
  
  Jev in one sentence: state in, defined decisions out
&lt;/h2&gt;

&lt;p&gt;An LLM is usually asked to write the next piece of text. For example: “Read this support ticket, explain the problem, and draft a reply.” The response can be useful, but it is open-ended text. Software must still decide how to parse it, validate it, and react if the model produces an unexpected answer.&lt;/p&gt;

&lt;p&gt;Jev starts from a different contract. The application sends state plus a fixed set of questions. Each question specifies the allowed answer shape: a binary judgment, a choice from named options, or a score against a defined scale. Jev returns one value for each question, along with probabilities or confidence where the response type supports them.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Support ticket + account data + policy
                 │
                 ▼
     Application-defined questions
     ├─ Is the request urgent?          → binary probability
     ├─ Which team owns it?              → one named choice
     └─ How risky is the situation?     → score on a fixed scale
                 │
                 ▼
       Typed answers + confidence
                 │
                 ▼
  Code applies a threshold, routes, or asks a person to review
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The model is not deciding what an application &lt;em&gt;can&lt;/em&gt; do. The developer still defines the actions, allowed categories, rules, and escalation path. Jev supplies a probabilistic judgment inside that bounded space.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this is not merely “LLM JSON mode”
&lt;/h2&gt;

&lt;p&gt;An LLM can be constrained to produce JSON or call a tool with a schema. That is often a good design. Some provider modes can enforce schema conformance, but the LLM still generates an output token sequence and the application must decide whether a schema-valid result is semantically suitable and safe for the next action.&lt;/p&gt;

&lt;p&gt;TypeSafe positions Jev as a System One model: a model trained and served for parallel, typed decisions rather than sequential text generation. Its public interface defines the possible answers in advance and returns calibrated probabilities and confidence. In that sense, JSON is not the product's primary abstraction; a decision schema is.&lt;/p&gt;

&lt;p&gt;This is an interface and workflow distinction, not a guarantee that one model is always more intelligent. A valid choice can still be the wrong choice. “No hallucinated text” means Jev cannot invent a free-form explanation or an undeclared category; it does &lt;strong&gt;not&lt;/strong&gt; mean it cannot misclassify an ambiguous ticket, misunderstand a policy, or receive incomplete state.&lt;/p&gt;

&lt;h2&gt;
  
  
  Jev vs LLM: the practical comparison
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Question&lt;/th&gt;
&lt;th&gt;Jev&lt;/th&gt;
&lt;th&gt;Text-generating LLM&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Primary output&lt;/td&gt;
&lt;td&gt;A declared binary judgment, choice, or score&lt;/td&gt;
&lt;td&gt;Newly generated text or code&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Best input shape&lt;/td&gt;
&lt;td&gt;Application state plus a bounded decision schema&lt;/td&gt;
&lt;td&gt;Natural-language requests, documents, conversation, and broad context&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Can it write an email, explanation, or program?&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Can it select a known route or apply a rubric?&lt;/td&gt;
&lt;td&gt;Yes; this is its intended role&lt;/td&gt;
&lt;td&gt;Yes, but the integration must still decide whether a valid result is suitable for the next action&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Can it invent a category outside the schema?&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;It can, unless the integration constrains and validates output&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Uncertainty signal&lt;/td&gt;
&lt;td&gt;Returns probabilities and confidence for supported decision types; TypeSafe describes these as calibrated&lt;/td&gt;
&lt;td&gt;A provider may expose scores or an application may ask for an estimate, but there is no uniform decision-specific calibration contract&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Good latency/cost target&lt;/td&gt;
&lt;td&gt;High-volume, bounded decisions inside a program&lt;/td&gt;
&lt;td&gt;Rich interaction, generation, synthesis, and open-ended reasoning&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Key limitation&lt;/td&gt;
&lt;td&gt;Cannot generate novel text, plans, categories, or explanations&lt;/td&gt;
&lt;td&gt;Output is flexible, so software must defend against invalid or unsuitable results&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The table describes intended use, not a universal quality ranking. A simple deterministic rule should remain ordinary code; neither Jev nor an LLM should be asked whether &lt;code&gt;amount &amp;gt; 1000&lt;/code&gt; when the application can calculate it exactly.&lt;/p&gt;

&lt;h2&gt;
  
  
  A concrete example: triage, not customer support by itself
&lt;/h2&gt;

&lt;p&gt;Consider an incoming support ticket. A conventional LLM prompt may ask for both a classification and a response. That is convenient for a human operator, but it mixes two jobs: deciding how to route work and writing language for the customer.&lt;/p&gt;

&lt;p&gt;With Jev, the routing decision can be expressed separately. This adapted Cloudflare Workers example follows the documented &lt;code&gt;state&lt;/code&gt; and &lt;code&gt;questions&lt;/code&gt; request shape.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;AI&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;typesafe/jev&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// State is the evidence Jev may use for every question in this request.&lt;/span&gt;
  &lt;span class="na"&gt;state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;ticket&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;subject&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Charged twice&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;I was charged twice for order A-104. Please refund the duplicate.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="c1"&gt;// The application supplies relevant facts instead of asking the model to fetch them.&lt;/span&gt;
    &lt;span class="na"&gt;order&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;charges&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;span class="na"&gt;amountUsd&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;49&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;captured&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;amountUsd&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;49&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;captured&lt;/span&gt;&lt;span class="dl"&gt;"&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;span class="na"&gt;refundPolicy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Duplicate charges are eligible for a refund.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;questions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;department&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;choice&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;instructions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Which team should handle this ticket?&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="c1"&gt;// Jev must select one declared key; it cannot make up a fifth team.&lt;/span&gt;
      &lt;span class="na"&gt;criteria&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;billing&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Charges, invoices, refunds, or subscriptions&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;technical&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Product bugs, outages, or integrations&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;account&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Login, password, profile, or security issues&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;other&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Does not fit the listed teams&lt;/span&gt;&lt;span class="dl"&gt;"&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;span class="na"&gt;needsHumanReview&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;noul&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="c1"&gt;// `noul` is Jev's documented binary-probability question type.&lt;/span&gt;
      &lt;span class="na"&gt;instructions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Should a person review this request before any refund action?&lt;/span&gt;&lt;span class="dl"&gt;"&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;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// The exact response types depend on the provider SDK. The documented response&lt;/span&gt;
&lt;span class="c1"&gt;// contains a selected choice and confidence/probabilities for a choice question.&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;department&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;answers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;department&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;department&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;choice&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;billing&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;department&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;confidence&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.9&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;enqueueForBillingReview&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// Route work; do not issue a refund automatically here.&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;enqueueForHumanTriage&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// Low confidence or another route gets a safer path.&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The model has not performed a refund, queried a payment provider, or created a policy. It has only supplied a bounded judgment. The application owns authorization, idempotency, audit records, and the final state change.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where Jev is strong
&lt;/h2&gt;

&lt;p&gt;Jev is a natural fit when an application already knows the set of possible actions but needs help deciding which one fits unstructured evidence.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Use case&lt;/th&gt;
&lt;th&gt;Decision schema&lt;/th&gt;
&lt;th&gt;What the application still owns&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Support routing&lt;/td&gt;
&lt;td&gt;Department, urgency, escalation&lt;/td&gt;
&lt;td&gt;Queue selection, staffing, access control&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Content or log triage&lt;/td&gt;
&lt;td&gt;Relevance, severity, category&lt;/td&gt;
&lt;td&gt;Retention, alerting, investigation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Policy review&lt;/td&gt;
&lt;td&gt;Eligible / not eligible / uncertain&lt;/td&gt;
&lt;td&gt;The policy source, final approval, audit trail&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Quality gates&lt;/td&gt;
&lt;td&gt;Pass / revise / reject against a rubric&lt;/td&gt;
&lt;td&gt;The rubric, release process, exceptions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Agent routing&lt;/td&gt;
&lt;td&gt;Use tool A, tool B, or request review&lt;/td&gt;
&lt;td&gt;Tool permissions, budgets, side-effect handling&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;These cases share an important property: the output space can be defined before the request arrives. That lets a program associate each answer with an explicit branch and reserve uncertain cases for review.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where an LLM remains the right tool
&lt;/h2&gt;

&lt;p&gt;Use an LLM when the valuable output is language or when the possible answer is not known in advance. Examples include explaining an incident, summarizing a long investigation, writing a migration plan, drafting a customer response, producing code, or exploring an unfamiliar document set.&lt;/p&gt;

&lt;p&gt;An LLM is also useful &lt;em&gt;before&lt;/em&gt; Jev in a workflow. It can turn a large, messy body of information into a proposed structured state, suggest candidate categories, or write a human-readable explanation. Those outputs should still be validated before becoming inputs to an automated decision.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Unstructured documents ──► LLM extracts or summarizes ──► validated state
                                                              │
                                                              ▼
                                                    Jev selects a route
                                                              │
                                                              ▼
                                                   code acts or escalates
                                                              │
                                                              ▼
                                      LLM explains the result to a person, if needed
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is often a better mental model than “Jev versus LLM.” An LLM handles open-ended language work; Jev supplies repeated bounded judgments; ordinary code enforces deterministic rules and carries out side effects.&lt;/p&gt;

&lt;h2&gt;
  
  
  Strengths do not remove operational risk
&lt;/h2&gt;

&lt;p&gt;Typed output removes one integration failure mode: malformed output or an undeclared answer. It does not establish that the input state is complete, that a policy is current, or that a high-confidence decision is safe to execute without review.&lt;/p&gt;

&lt;p&gt;Before automating a consequential action, define a conservative threshold, record the state and decision used, make the action idempotent, and give humans an escalation path. For a payment, suspension, deletion, medical, legal, or security action, a typed model result should normally be one signal in a controlled workflow—not the only authorization check.&lt;/p&gt;

&lt;h2&gt;
  
  
  The boundary to remember
&lt;/h2&gt;

&lt;p&gt;Choose Jev when the question is: “Given this state and these allowed outcomes, which branch should software take?” Choose an LLM when the question is: “What should we say, create, explain, or explore?” Use plain code when the answer follows from deterministic data and rules.&lt;/p&gt;

&lt;p&gt;That boundary is why Jev is interesting. It does not try to make chatbots obsolete. It makes a different part of AI integration—frequent, structured, uncertainty-aware decisions—into a first-class model interface.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>llm</category>
      <category>automation</category>
      <category>machinelearning</category>
    </item>
    <item>
      <title>Inside TypeScript 7's Go Port: Designing Typed AST Nodes Without a Giant Struct</title>
      <dc:creator>puffball1567</dc:creator>
      <pubDate>Thu, 17 Sep 2026 17:34:52 +0000</pubDate>
      <link>https://dev.to/puffball1567/inside-typescript-7s-go-port-designing-typed-ast-nodes-without-a-giant-struct-gep</link>
      <guid>https://dev.to/puffball1567/inside-typescript-7s-go-port-designing-typed-ast-nodes-without-a-giant-struct-gep</guid>
      <description>&lt;p&gt;TypeScript 7 is a version of TypeScript whose compiler and editor tools were reimplemented in Go. This article examines one part of that implementation: its AST node model.&lt;/p&gt;

&lt;p&gt;The intended reader works with Go or TypeScript but does not need compiler-internals experience. The topic is a general modeling problem: how to represent several related data shapes without putting every possible field into one oversized struct full of optional fields.&lt;/p&gt;

&lt;p&gt;I am not part of the TypeScript team. This is an independent technical reading of the publicly available source code and documentation, not an official explanation of the implementation or its design decisions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Source and license note
&lt;/h2&gt;

&lt;p&gt;This article discusses the publicly available &lt;a href="https://github.com/microsoft/typescript-go" rel="noopener noreferrer"&gt;&lt;code&gt;microsoft/typescript-go&lt;/code&gt;&lt;/a&gt; source repository, which is licensed under the &lt;a href="https://github.com/microsoft/typescript-go/blob/main/LICENSE" rel="noopener noreferrer"&gt;Apache License 2.0&lt;/a&gt;. The implementation section includes short source excerpts; the remaining Go and TypeScript snippets are small, independently written examples. Links to the relevant upstream files are included where implementation details are discussed.&lt;/p&gt;

&lt;p&gt;Source excerpts are from Microsoft’s TypeScript-go project:&lt;br&gt;
&lt;a href="https://github.com/microsoft/typescript-go" rel="noopener noreferrer"&gt;https://github.com/microsoft/typescript-go&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Licensed under the Apache License, Version 2.0.&lt;br&gt;
A copy of the License is available at:&lt;br&gt;
&lt;a href="https://github.com/microsoft/typescript-go/blob/main/LICENSE" rel="noopener noreferrer"&gt;https://github.com/microsoft/typescript-go/blob/main/LICENSE&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Step 0: compiler, transpiler, parser, and type checker
&lt;/h2&gt;

&lt;p&gt;A compiler pipeline normally starts with a &lt;strong&gt;parser&lt;/strong&gt;, which turns source text into an &lt;strong&gt;abstract syntax tree (AST)&lt;/strong&gt;. A &lt;strong&gt;type checker&lt;/strong&gt; uses that tree to analyze types, and an emitter writes JavaScript or another output format. A compiler that emits another programming language is often called a &lt;strong&gt;transpiler&lt;/strong&gt;; TypeScript-to-JavaScript is one example. This article focuses only on the AST as a data model, not on parsing or type-checking algorithms.&lt;/p&gt;
&lt;h2&gt;
  
  
  Step 1: a parser needs more than the original text
&lt;/h2&gt;

&lt;p&gt;Suppose a compiler receives this code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nf"&gt;total&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The parser represents this expression as an AST: a tree of nodes that capture source structure.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Call
├── Identifier: total
├── Number: 1
└── Number: 2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, &lt;code&gt;Call&lt;/code&gt;, &lt;code&gt;Identifier&lt;/code&gt;, and &lt;code&gt;Number&lt;/code&gt; are node kinds: labels for distinct syntax roles. Later stages read this tree instead of re-interpreting source text.&lt;/p&gt;

&lt;p&gt;Each kind needs different information.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;An identifier needs text, such as &lt;code&gt;total&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;A number needs its value or original text.&lt;/li&gt;
&lt;li&gt;A call needs the expression being called and its arguments.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Some information is useful for every kind. For example, the compiler often needs the location of a node in the original file so it can show an error in the right place. It may also need a link to the node's parent.&lt;/p&gt;

&lt;p&gt;This gives us the real design problem: how should one Go type represent what every node has, while still keeping call-only fields away from identifier-only fields?&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: the simple approach is one large struct
&lt;/h2&gt;

&lt;p&gt;A normal first implementation in Go is one struct. For a TypeScript reader, it is similar to defining one object shape with several optional properties.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;Node&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Kind&lt;/span&gt;      &lt;span class="n"&gt;Kind&lt;/span&gt;      &lt;span class="c"&gt;// Says whether this is a call, identifier, or number.&lt;/span&gt;
    &lt;span class="n"&gt;Start&lt;/span&gt;     &lt;span class="kt"&gt;int&lt;/span&gt;       &lt;span class="c"&gt;// Byte position where this node begins in the source text.&lt;/span&gt;
    &lt;span class="n"&gt;End&lt;/span&gt;       &lt;span class="kt"&gt;int&lt;/span&gt;       &lt;span class="c"&gt;// Byte position immediately after the node ends.&lt;/span&gt;
    &lt;span class="n"&gt;Parent&lt;/span&gt;    &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Node&lt;/span&gt;     &lt;span class="c"&gt;// The containing node, if there is one.&lt;/span&gt;
    &lt;span class="n"&gt;Text&lt;/span&gt;      &lt;span class="kt"&gt;string&lt;/span&gt;    &lt;span class="c"&gt;// Used by an identifier or number; unused by a call.&lt;/span&gt;
    &lt;span class="n"&gt;Callee&lt;/span&gt;    &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Node&lt;/span&gt;     &lt;span class="c"&gt;// Used by a call; unused by an identifier or number.&lt;/span&gt;
    &lt;span class="n"&gt;Arguments&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Node&lt;/span&gt;   &lt;span class="c"&gt;// Used by a call; unused by an identifier or number.&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is not wrong. It is easy to create, easy to read, and often perfect for a small format.&lt;/p&gt;

&lt;p&gt;The difficulty appears when the model grows. With this type, Go allows combinations that have no meaning. For example, an identifier can have both &lt;code&gt;Text&lt;/code&gt; and &lt;code&gt;Arguments&lt;/code&gt;, even though only a call should have arguments. The struct cannot express the rule itself; every part of the program must remember it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Kind&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;IdentifierKind&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;// `Text` is meaningful only after checking the kind.&lt;/span&gt;
    &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Text&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;One check is harmless. A compiler has many node kinds and many places that read them. Repeating the same checks makes it easier for one reader to forget a rule. Adding a new kind also tends to add more fields that are empty for nearly every existing node.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: TypeScript can describe valid shapes with a union
&lt;/h2&gt;

&lt;p&gt;TypeScript has a &lt;strong&gt;discriminated union&lt;/strong&gt; for this case. A shared tag, usually &lt;code&gt;kind&lt;/code&gt;, connects a runtime value with the fields allowed for that shape. For a Go reader, it is a compile-time relationship between a tag value and the fields valid with it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;IdentifierNode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;identifier&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// This fixed value identifies an identifier node.&lt;/span&gt;
  &lt;span class="nl"&gt;start&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Source position shared by every node.&lt;/span&gt;
  &lt;span class="nl"&gt;end&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Only an identifier has a name.&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;CallNode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;call&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// This fixed value identifies a call node.&lt;/span&gt;
  &lt;span class="nl"&gt;start&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;end&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;callee&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Node&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// The expression to invoke.&lt;/span&gt;
  &lt;span class="nl"&gt;arguments&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Node&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt; &lt;span class="c1"&gt;// The values passed to that expression.&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="c1"&gt;// A Node is one valid shape or the other, not an arbitrary mixture of both.&lt;/span&gt;
&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Node&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;IdentifierNode&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nx"&gt;CallNode&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After checking &lt;code&gt;kind&lt;/code&gt;, TypeScript narrows the union: it knows which fields are safe to use in that branch. This is checked when TypeScript is compiled, before the JavaScript runs.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Node&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;kind&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;identifier&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// In this branch, TypeScript knows that `text` exists.&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// The only remaining shape is CallNode, so `arguments` exists here.&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;`call with &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;arguments&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="s2"&gt; arguments`&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;The important benefit is not the syntax. It is that the type model says which combinations are valid.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4: Go needs a different way to express the same separation
&lt;/h2&gt;

&lt;p&gt;Go does not have built-in discriminated unions. TypeScript-go combines a shared struct header with a private interface. A Go interface can hold a concrete value that provides its methods, and a &lt;strong&gt;type assertion&lt;/strong&gt; checks which concrete value it currently holds.&lt;/p&gt;

&lt;p&gt;The TypeScript-go project uses those features to split its AST in two parts. The small example below is deliberately simpler than the real layout: its &lt;code&gt;callData&lt;/code&gt; is not a TypeScript-go type.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A central &lt;code&gt;Node&lt;/code&gt; holds data shared by all nodes.&lt;/li&gt;
&lt;li&gt;A private &lt;code&gt;nodeData&lt;/code&gt; value holds data that belongs to one specific node kind.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In the actual source, the shared &lt;code&gt;Node&lt;/code&gt; has a kind, flags, a source range, an internal ID, a parent pointer, and private &lt;code&gt;nodeData&lt;/code&gt;. The concrete &lt;code&gt;nodeData&lt;/code&gt; values hold details such as identifier text, call arguments, and function bodies.&lt;/p&gt;

&lt;p&gt;Here is a smaller example of the same idea. It is written for explanation, not copied from TypeScript-go.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;Node&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Kind&lt;/span&gt;   &lt;span class="n"&gt;Kind&lt;/span&gt;     &lt;span class="c"&gt;// Shared: tells generic code which syntax form this is.&lt;/span&gt;
    &lt;span class="n"&gt;Start&lt;/span&gt;  &lt;span class="kt"&gt;int&lt;/span&gt;      &lt;span class="c"&gt;// Shared: start position in the input.&lt;/span&gt;
    &lt;span class="n"&gt;End&lt;/span&gt;    &lt;span class="kt"&gt;int&lt;/span&gt;      &lt;span class="c"&gt;// Shared: end position in the input.&lt;/span&gt;
    &lt;span class="n"&gt;Parent&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Node&lt;/span&gt;    &lt;span class="c"&gt;// Shared: link to the containing node.&lt;/span&gt;
    &lt;span class="c"&gt;// data is not a child node itself. It stores fields specific to this kind.&lt;/span&gt;
    &lt;span class="c"&gt;// For a call expression, its concrete data can conceptually contain:&lt;/span&gt;
    &lt;span class="c"&gt;//   Callee    *Node   // the expression being called&lt;/span&gt;
    &lt;span class="c"&gt;//   Arguments []*Node // the expressions passed to the call&lt;/span&gt;
    &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="n"&gt;nodeData&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;// Visitor is the callback used while walking direct children.&lt;/span&gt;
&lt;span class="c"&gt;// Returning true asks the traversal to stop early.&lt;/span&gt;
&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;Visitor&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Node&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;nodeData&lt;/span&gt; &lt;span class="k"&gt;interface&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;// Every kind-specific data type exposes its direct children.&lt;/span&gt;
    &lt;span class="n"&gt;ForEachChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Visitor&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;identifierData&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Text&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="c"&gt;// Specific: only identifiers have identifier text.&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;identifierData&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;ForEachChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;visitor&lt;/span&gt; &lt;span class="n"&gt;Visitor&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt; &lt;span class="c"&gt;// An identifier is a leaf, so there is nothing to visit.&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;callData&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Callee&lt;/span&gt;    &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Node&lt;/span&gt;   &lt;span class="c"&gt;// Specific: the expression to call.&lt;/span&gt;
    &lt;span class="n"&gt;Arguments&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Node&lt;/span&gt; &lt;span class="c"&gt;// Specific: values supplied to the call.&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;visit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;visitor&lt;/span&gt; &lt;span class="n"&gt;Visitor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Node&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;visitor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c"&gt;// Call the visitor only for a real child node.&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt; &lt;span class="c"&gt;// A missing optional child cannot stop the traversal.&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;visitNodes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;visitor&lt;/span&gt; &lt;span class="n"&gt;Visitor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;nodes&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Node&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="k"&gt;range&lt;/span&gt; &lt;span class="n"&gt;nodes&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;visitor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt; &lt;span class="c"&gt;// Stop as soon as the visitor asks to stop.&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="n"&gt;callData&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;ForEachChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;visitor&lt;/span&gt; &lt;span class="n"&gt;Visitor&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;visit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;visitor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Callee&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt; &lt;span class="c"&gt;// Do not inspect arguments after an early-stop request.&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;visitNodes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;visitor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Arguments&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c"&gt;// Visit arguments in source order.&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The real code starts from a concrete node object. &lt;code&gt;CallExpression&lt;/code&gt; contains its child fields, and its factory obtains one from an arena before filling those fields. &lt;code&gt;newNode&lt;/code&gt; obtains the embedded shared &lt;code&gt;Node&lt;/code&gt; with &lt;code&gt;data.AsNode()&lt;/code&gt;, sets its kind, and stores the same concrete object back in &lt;code&gt;n.data&lt;/code&gt;. Therefore, for a call expression, &lt;code&gt;n.data&lt;/code&gt; holds &lt;code&gt;*CallExpression&lt;/code&gt;, not a separate &lt;code&gt;callData&lt;/code&gt; value. &lt;a href="https://github.com/microsoft/typescript-go/blob/main/internal/ast/ast_generated.go#L4256-L4274" rel="noopener noreferrer"&gt;See the actual &lt;code&gt;CallExpression&lt;/code&gt; factory&lt;/a&gt; and &lt;a href="https://github.com/microsoft/typescript-go/blob/main/internal/ast/ast.go#L69-L83" rel="noopener noreferrer"&gt;the shared &lt;code&gt;newNode&lt;/code&gt; function&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The traversal contract in the example is still useful, but the actual top-level dispatch has one important difference. &lt;code&gt;Node.ForEachChild&lt;/code&gt; uses a generated &lt;code&gt;switch&lt;/code&gt; on &lt;code&gt;Kind&lt;/code&gt;, then type-asserts &lt;code&gt;n.data&lt;/code&gt; and calls the concrete method:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Node&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;ForEachChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="n"&gt;Visitor&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;switch&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Kind&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;// ... other node kinds ...&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;KindCallExpression&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;CallExpression&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ForEachChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;CallExpression.ForEachChild&lt;/code&gt; then passes its direct &lt;code&gt;Expression&lt;/code&gt;, optional &lt;code&gt;QuestionDotToken&lt;/code&gt;, optional type arguments, and arguments to the visitor. In the upstream source, &lt;code&gt;Visitor&lt;/code&gt; is &lt;code&gt;func(*Node) bool&lt;/code&gt;; &lt;code&gt;visit&lt;/code&gt; ignores a nil child and otherwise calls the supplied visitor; &lt;code&gt;visitNodes&lt;/code&gt; applies that visitor to a list until it returns &lt;code&gt;true&lt;/code&gt;. The &lt;code&gt;true&lt;/code&gt; return is an early-stop signal, not an error. &lt;a href="https://github.com/microsoft/typescript-go/blob/main/internal/ast/ast.go#L24-L54" rel="noopener noreferrer"&gt;See the helpers&lt;/a&gt; and &lt;a href="https://github.com/microsoft/typescript-go/blob/main/internal/ast/ast_generated.go#L4284-L4289" rel="noopener noreferrer"&gt;the concrete CallExpression traversal&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;ForEachChild&lt;/code&gt; itself does not recursively walk the whole tree. It enumerates only direct children. The visitor supplied by its caller decides what to do with each child, including whether to recurse. A search visitor can use the early-stop result like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;Find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Node&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;matches&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Node&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Node&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;found&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Node&lt;/span&gt;
    &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;walk&lt;/span&gt; &lt;span class="n"&gt;Visitor&lt;/span&gt;

    &lt;span class="n"&gt;walk&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;current&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Node&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;matches&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;current&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;found&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;current&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt; &lt;span class="c"&gt;// Tell every caller in the chain to stop.&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;current&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ForEachChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;walk&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c"&gt;// Recurse only after testing this node.&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;walk&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c"&gt;// Begin the depth-first traversal at the root.&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;found&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, &lt;code&gt;walk&lt;/code&gt; is the actual visitor implementation. It checks the current node, recursively asks that node for its direct children, and returns &lt;code&gt;true&lt;/code&gt; once it finds a match. Language-service features use the same separation: their visitor performs feature-specific work, while each AST node knows which fields are its children.&lt;/p&gt;

&lt;p&gt;The word &lt;strong&gt;private&lt;/strong&gt; matters here. In Go, a lower-case name such as &lt;code&gt;data&lt;/code&gt; or &lt;code&gt;nodeData&lt;/code&gt; cannot be used by code in another package. This serves the same purpose as keeping an implementation detail unexported from a TypeScript module.&lt;/p&gt;

&lt;p&gt;Generic code can work with &lt;code&gt;*Node&lt;/code&gt; and use the common fields. For example, a tree walker can report source locations for every node. When it needs to move down the tree, generated dispatch selects the concrete node and that node enumerates its children.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 5: the small example keeps &lt;code&gt;Kind&lt;/code&gt; and data together
&lt;/h2&gt;

&lt;p&gt;A &lt;strong&gt;constructor&lt;/strong&gt; is a function that creates a value in a valid state. In Go, &lt;code&gt;New...&lt;/code&gt; factory functions commonly play this role.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;Kind&lt;/span&gt; &lt;span class="kt"&gt;uint8&lt;/span&gt;

&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;IdentifierKind&lt;/span&gt; &lt;span class="n"&gt;Kind&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;iota&lt;/span&gt; &lt;span class="c"&gt;// `iota` assigns the first numeric Kind value; it must use identifierData.&lt;/span&gt;
    &lt;span class="n"&gt;CallKind&lt;/span&gt;                   &lt;span class="c"&gt;// This kind must use callData.&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The concrete data types used by the constructors are repeated here so their contents remain visible nearby.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;identifierData&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Text&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="c"&gt;// The identifier's spelling, such as "total".&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;identifierData&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;ForEachChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;visitor&lt;/span&gt; &lt;span class="n"&gt;Visitor&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt; &lt;span class="c"&gt;// Identifiers are leaf nodes.&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;callData&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Callee&lt;/span&gt;    &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Node&lt;/span&gt;   &lt;span class="c"&gt;// The expression to call.&lt;/span&gt;
    &lt;span class="n"&gt;Arguments&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Node&lt;/span&gt; &lt;span class="c"&gt;// The expressions passed to the call.&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="n"&gt;callData&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;ForEachChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;visitor&lt;/span&gt; &lt;span class="n"&gt;Visitor&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;visit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;visitor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Callee&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt; &lt;span class="c"&gt;// Stop before visiting arguments when requested.&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;visitNodes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;visitor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Arguments&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;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;NewIdentifier&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;end&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Node&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;// Create an identifier kind with identifier-specific data.&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;Node&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Kind&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;  &lt;span class="n"&gt;IdentifierKind&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;Start&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;End&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;   &lt;span class="n"&gt;end&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;  &lt;span class="n"&gt;identifierData&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;text&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;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;NewCall&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;end&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;callee&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Node&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;arguments&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Node&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Node&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;// Create a call kind with call-specific data.&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;Node&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Kind&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;  &lt;span class="n"&gt;CallKind&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;Start&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;End&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;   &lt;span class="n"&gt;end&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;  &lt;span class="n"&gt;callData&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;Callee&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;callee&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Arguments&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;arguments&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;The constructor is where the rule lives. Code elsewhere does not need to remember which fields a call requires. It calls &lt;code&gt;NewCall&lt;/code&gt;, and that function pairs &lt;code&gt;CallKind&lt;/code&gt; with &lt;code&gt;callData&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;When code needs information that belongs only to an identifier, it can use a method that checks the concrete data type.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Node&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;IdentifierText&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ok&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;identifierData&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c"&gt;// This succeeds only for an identifier.&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;ok&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt; &lt;span class="c"&gt;// A call does not have identifier text.&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;n.data.(identifierData)&lt;/code&gt; is Go's type-assertion syntax. In the two-result form, &lt;code&gt;data&lt;/code&gt; receives the concrete &lt;code&gt;identifierData&lt;/code&gt; value only when the assertion succeeds, and &lt;code&gt;ok&lt;/code&gt; reports whether it succeeded. When &lt;code&gt;n.data&lt;/code&gt; holds &lt;code&gt;callData&lt;/code&gt;, &lt;code&gt;ok&lt;/code&gt; is &lt;code&gt;false&lt;/code&gt;, so the function can safely return without reading &lt;code&gt;Text&lt;/code&gt;. The &lt;a href="https://go.dev/ref/spec#Type_assertions" rel="noopener noreferrer"&gt;Go language specification's type-assertion section&lt;/a&gt; documents both this safe two-result form and the one-result form that panics on a mismatch. Returning &lt;code&gt;false&lt;/code&gt; here is safer than silently returning an empty string for the wrong kind of node.&lt;/p&gt;

&lt;h2&gt;
  
  
  What TypeScript-go adds beyond this small example
&lt;/h2&gt;

&lt;p&gt;The real compiler needs more than a tiny expression tree. It needs to clone nodes, visit many different child arrangements, store information discovered during type checking, and handle a large number of language features.&lt;/p&gt;

&lt;p&gt;TypeScript-go's private &lt;code&gt;nodeData&lt;/code&gt; interface therefore has more methods than this example. The project also provides default base types, so an individual node implementation does not have to reimplement every method from scratch. The source comments explain that the team uses both interface methods and type switches: interface calls can help some paths, but hundreds of interface implementations also increase code size. &lt;a href="https://github.com/microsoft/typescript-go/blob/main/internal/ast/ast.go#L169-L215" rel="noopener noreferrer"&gt;The common &lt;code&gt;Node&lt;/code&gt; definition&lt;/a&gt; and &lt;a href="https://github.com/microsoft/typescript-go/blob/main/internal/ast/ast.go#L1128-L1195" rel="noopener noreferrer"&gt;the private &lt;code&gt;nodeData&lt;/code&gt; interface and default base type&lt;/a&gt; show those details.&lt;/p&gt;

&lt;p&gt;The project is not offering &lt;code&gt;nodeData&lt;/code&gt; as a public plugin API. It is an internal representation for a closed set of node kinds that the compiler itself owns. That is useful because the compiler can change traversal or cloning rules and update every known node type at once.&lt;/p&gt;

&lt;h2&gt;
  
  
  Does this AST design explain TypeScript 7's speed?
&lt;/h2&gt;

&lt;p&gt;No. This design can make the model clearer and reduce opportunities to misuse fields, but it is not a general performance recipe.&lt;/p&gt;

&lt;p&gt;The TypeScript team describes TypeScript 7's improvement as a combination of native code speed, shared-memory multithreading, and other optimizations. Parsing, type checking, and JavaScript output generation can run in parallel where that is safe. Type checking is more complicated because files share type information. TypeScript 7 uses a fixed number of type-checker workers so the work stays deterministic; using more workers can help large projects, but it can also use more memory. The official &lt;a href="https://devblogs.microsoft.com/typescript/announcing-typescript-7-0/#custom-scaling-parallelization-and-controls" rel="noopener noreferrer"&gt;TypeScript 7 announcement&lt;/a&gt; explains that trade-off.&lt;/p&gt;

&lt;h3&gt;
  
  
  Reference measurement: traversal dispatch versus a flat struct
&lt;/h3&gt;

&lt;p&gt;One reasonable question is whether the &lt;code&gt;Kind&lt;/code&gt; switch and concrete type assertion in &lt;code&gt;Node.ForEachChild&lt;/code&gt; make this representation slower than one large discriminated struct with every possible node field. The public repository does not provide a benchmark that answers that exact question, so I ran a small synthetic traversal benchmark as reference information. It is not a TypeScript-team result and should not be read as a performance claim about the complete compiler.&lt;/p&gt;

&lt;p&gt;The benchmark constructed a 262,143-node binary AST, then walked every node with no parsing, allocation, binding, type checking, or emission inside the measured loop. The TypeScript-shaped variant used a common &lt;code&gt;node&lt;/code&gt; header embedded in concrete node types and dispatched through a &lt;code&gt;Kind&lt;/code&gt; switch followed by an assertion such as &lt;code&gt;n.data.(*callNode)&lt;/code&gt;. The comparison variant used one intentionally wider &lt;code&gt;flatNode&lt;/code&gt; struct, retaining unused kind-specific fields at their zero values. Both variants visited the same tree shape and reported zero allocations during a traversal.&lt;/p&gt;

&lt;p&gt;On Go 1.23.5, Linux/amd64, with one benchmark CPU on an AMD Ryzen 5 5600H, five runs produced these results:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Representation&lt;/th&gt;
&lt;th&gt;Median full-tree traversal&lt;/th&gt;
&lt;th&gt;Per-traversal allocations&lt;/th&gt;
&lt;th&gt;Synthetic node size&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Embedded header + &lt;code&gt;Kind&lt;/code&gt; switch + concrete assertion&lt;/td&gt;
&lt;td&gt;1.04 ms&lt;/td&gt;
&lt;td&gt;0 B / 0 allocs&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;callNode&lt;/code&gt;: 48 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;One wider discriminated &lt;code&gt;flatNode&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;1.16 ms&lt;/td&gt;
&lt;td&gt;0 B / 0 allocs&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;flatNode&lt;/code&gt;: 104 B&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;In this intentionally narrow test, the switch-and-assert representation was about 11% faster at the median. A CPU profile also showed the dispatch method inlined by this Go version. This does &lt;strong&gt;not&lt;/strong&gt; isolate the cost of a single assertion, and it does not establish that the same result applies to TypeScript 7's full set of node kinds or real-world projects. It does show that a flat struct is not automatically the faster traversal layout: larger per-node memory footprint and cache behavior are part of the trade-off too.&lt;/p&gt;

&lt;h2&gt;
  
  
  When is this pattern useful in an ordinary Go application?
&lt;/h2&gt;

&lt;p&gt;Start with a normal struct when the data is small. A configuration value with two optional fields does not need an interface and several extra types.&lt;/p&gt;

&lt;p&gt;This pattern becomes useful when all of the following are true:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Your program has a fixed set of data shapes with clearly different fields.&lt;/li&gt;
&lt;li&gt;Many parts of the program need a small set of fields shared by every shape.&lt;/li&gt;
&lt;li&gt;Invalid combinations of fields are causing repeated checks or bugs.&lt;/li&gt;
&lt;li&gt;Central constructors and one traversal rule would make the model easier to understand.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The lesson is not “always use interfaces for trees.” It is simpler: keep shared data together, keep kind-specific data with its own kind, and make invalid combinations difficult to create.&lt;/p&gt;

&lt;h2&gt;
  
  
  Kinmokusei
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/puffball1567/kinmokusei" rel="noopener noreferrer"&gt;Kinmokusei&lt;/a&gt; is a programming language with TypeScript-inspired syntax that compiles to readable Go. It is intended for writing web backends and Go libraries while using the normal Go toolchain and package ecosystem directly.&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>go</category>
      <category>compiler</category>
      <category>programming</category>
    </item>
    <item>
      <title>Go Concurrency Safety: Prevent Goroutine Leaks and Data Races</title>
      <dc:creator>puffball1567</dc:creator>
      <pubDate>Thu, 17 Sep 2026 00:31:11 +0000</pubDate>
      <link>https://dev.to/puffball1567/go-concurrency-safety-prevent-goroutine-leaks-and-data-races-deg</link>
      <guid>https://dev.to/puffball1567/go-concurrency-safety-prevent-goroutine-leaks-and-data-races-deg</guid>
      <description>&lt;p&gt;Goroutines make it easy to begin concurrent work. Making that work stop reliably and share state safely takes a little more design. This follow-up explains two problems that often appear after a first worker-pool or channel implementation: a goroutine that never exits, and shared memory that two goroutines access at the same time.&lt;/p&gt;

&lt;p&gt;If you have not read the first article, start with &lt;a href="https://dev.to/puffball1567/go-concurrency-how-to-run-parallel-tasks-with-goroutines-channels-and-context-59ib"&gt;Go Concurrency: How to Run Parallel Tasks with Goroutines, Channels, and Context&lt;/a&gt;. It introduces goroutines, channels, &lt;code&gt;sync.WaitGroup&lt;/code&gt;, bounded worker pools, and &lt;code&gt;context&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is a goroutine leak in Go?
&lt;/h2&gt;

&lt;p&gt;A goroutine leak is a goroutine that is still alive even though the work it was created for is no longer useful. It is not necessarily a memory leak in the usual sense. The goroutine may be blocked while waiting to send to a channel, receive from a channel, acquire a lock, or complete an operation that nobody will ever observe.&lt;/p&gt;

&lt;p&gt;One leaked goroutine may not be noticeable. A leak on every HTTP request, reconnect, failed job, or page refresh eventually consumes memory, file descriptors, connections, and scheduler time. The practical rule is simple: every goroutine needs a clear completion path and a clear cancellation path.&lt;/p&gt;

&lt;h2&gt;
  
  
  A common Go channel leak: the consumer stops early
&lt;/h2&gt;

&lt;p&gt;In this example, the producer sends a sequence of values, but the consumer only wants the first one.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;numbers&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;out&lt;/span&gt; &lt;span class="k"&gt;chan&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;defer&lt;/span&gt; &lt;span class="nb"&gt;close&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;number&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;number&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;number&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;out&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="n"&gt;number&lt;/span&gt; &lt;span class="c"&gt;// The producer waits until somebody receives.&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;firstNumber&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;out&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nb"&gt;make&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;chan&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;go&lt;/span&gt; &lt;span class="n"&gt;numbers&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="n"&gt;out&lt;/span&gt; &lt;span class="c"&gt;// Return after receiving only the first value.&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;firstNumber&lt;/code&gt; returns after receiving &lt;code&gt;1&lt;/code&gt;. The producer then tries to send &lt;code&gt;2&lt;/code&gt;, but no receiver remains. Because &lt;code&gt;out&lt;/code&gt; is unbuffered, that send blocks forever. &lt;code&gt;defer close(out)&lt;/code&gt; does not help: the producer cannot reach its &lt;code&gt;return&lt;/code&gt; statement while blocked on the send.&lt;/p&gt;

&lt;p&gt;This pattern can be less obvious in real code. A caller may return after an HTTP timeout, a user may navigate away, or a worker may stop after seeing the first acceptable result. In all of those cases, the upstream goroutine needs to learn that its output is no longer wanted.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use context cancellation so a producer can stop
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;context.Context&lt;/code&gt; gives a goroutine a cancellation signal that can be selected alongside a channel operation.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"context"&lt;/span&gt;
    &lt;span class="s"&gt;"fmt"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;numbers&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;out&lt;/span&gt; &lt;span class="k"&gt;chan&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;defer&lt;/span&gt; &lt;span class="nb"&gt;close&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;number&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;number&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;number&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;out&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="n"&gt;number&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
            &lt;span class="c"&gt;// The consumer accepted this value.&lt;/span&gt;
        &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Done&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
            &lt;span class="c"&gt;// The consumer no longer needs more values.&lt;/span&gt;
            &lt;span class="k"&gt;return&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;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;firstNumber&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cancel&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WithCancel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Background&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="k"&gt;defer&lt;/span&gt; &lt;span class="n"&gt;cancel&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c"&gt;// Also cancel if this function returns through another path.&lt;/span&gt;

    &lt;span class="n"&gt;out&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nb"&gt;make&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;chan&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;go&lt;/span&gt; &lt;span class="n"&gt;numbers&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;first&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="n"&gt;out&lt;/span&gt;
    &lt;span class="n"&gt;cancel&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c"&gt;// Tell the producer not to send the remaining values.&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;first&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;firstNumber&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;The important part is the &lt;code&gt;select&lt;/code&gt;. A plain &lt;code&gt;out &amp;lt;- number&lt;/code&gt; can only wait for a receiver. The two-case &lt;code&gt;select&lt;/code&gt; can instead stop when &lt;code&gt;ctx.Done()&lt;/code&gt; becomes ready. Cancellation is cooperative: it does not forcibly kill a goroutine. Every blocking point that matters must check the cancellation signal or use an API, such as &lt;code&gt;http.NewRequestWithContext&lt;/code&gt;, that observes it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Avoid blocked result sends in a Go worker pool
&lt;/h2&gt;

&lt;p&gt;The same issue appears when workers send results. A worker should not wait forever to send a result after its caller has timed out.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;results&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
    &lt;span class="c"&gt;// The collector accepted the completed result.&lt;/span&gt;
&lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Done&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
    &lt;span class="c"&gt;// The caller left, so do not remain blocked trying to report it.&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is why the earlier worker-pool example checks &lt;code&gt;ctx.Done()&lt;/code&gt; both while accepting jobs and while sending results. A buffered channel can reduce waiting, but it does not replace cancellation: once a buffer fills, a sender can still block. More importantly, a buffer does not tell a producer that its remaining work is no longer needed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Channel ownership: decide who closes a channel
&lt;/h2&gt;

&lt;p&gt;Closing a channel means “no more values will be sent.” The goroutine that produces those values is usually the right owner of &lt;code&gt;close&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;produce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;jobs&lt;/span&gt; &lt;span class="k"&gt;chan&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;defer&lt;/span&gt; &lt;span class="nb"&gt;close&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;jobs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c"&gt;// This producer knows when there will be no more jobs.&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;job&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="k"&gt;range&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"one"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"two"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;jobs&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="n"&gt;job&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Done&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&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;Receivers normally do not close a channel, because they cannot know whether another sender is still working. Closing the same channel twice panics, and sending on a closed channel also panics. When multiple workers produce results, use a coordinator such as &lt;code&gt;sync.WaitGroup&lt;/code&gt;: wait for every worker to finish, then close the shared results channel exactly once.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is a data race in Go?
&lt;/h2&gt;

&lt;p&gt;A data race happens when two goroutines access the same memory concurrently, at least one access writes, and the accesses are not correctly synchronized. The result may look correct during a local test and fail under a different load or CPU schedule.&lt;/p&gt;

&lt;p&gt;Incrementing an integer is a classic example. &lt;code&gt;total++&lt;/code&gt; looks like one operation in source code, but it is a read, a calculation, and a write. Two goroutines can read the same old value and both write back the same new value.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"fmt"&lt;/span&gt;
    &lt;span class="s"&gt;"sync"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;
    &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;wg&lt;/span&gt; &lt;span class="n"&gt;sync&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WaitGroup&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="n"&gt;_000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;wg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;go&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;defer&lt;/span&gt; &lt;span class="n"&gt;wg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Done&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="n"&gt;total&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt; &lt;span class="c"&gt;// Unsafe: several goroutines write total together.&lt;/span&gt;
        &lt;span class="p"&gt;}()&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;wg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Wait&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;total&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;The program may print &lt;code&gt;1000&lt;/code&gt;, or it may print a smaller number. Either outcome is not proof of safety. Run it with the race detector:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;go run &lt;span class="nt"&gt;-race&lt;/span&gt; main.go
go &lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="nt"&gt;-race&lt;/span&gt; ./...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The detector instruments the program and reports many unsynchronized concurrent accesses. It is an excellent safety net, but it only reports races exercised by the test or program run. Keep tests concurrent enough to execute the paths you care about.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fix shared counters with sync.Mutex
&lt;/h2&gt;

&lt;p&gt;Use a &lt;code&gt;sync.Mutex&lt;/code&gt; when several goroutines need to modify one shared, mutable value.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="s"&gt;"sync"&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;Counter&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;mu&lt;/span&gt;    &lt;span class="n"&gt;sync&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Mutex&lt;/span&gt;
    &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;counter&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Counter&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;Increment&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;counter&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mu&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Lock&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;defer&lt;/span&gt; &lt;span class="n"&gt;counter&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mu&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Unlock&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="n"&gt;counter&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;counter&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Counter&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;counter&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mu&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Lock&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;defer&lt;/span&gt; &lt;span class="n"&gt;counter&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mu&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Unlock&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;counter&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The mutex protects the invariant: while one goroutine reads or changes &lt;code&gt;value&lt;/code&gt;, another cannot enter a method protected by the same mutex. Keep the locked section small, and do not hold a mutex while making a slow network call or waiting for an unbounded external operation.&lt;/p&gt;

&lt;p&gt;For simple counters, &lt;code&gt;sync/atomic&lt;/code&gt; can also be appropriate. For a map plus related counts, a multi-field state change, or a rule that must be checked and updated together, a mutex often expresses the intent more clearly.&lt;/p&gt;

&lt;h2&gt;
  
  
  An alternative to a mutex: one goroutine owns the state
&lt;/h2&gt;

&lt;p&gt;Channels are especially useful when one goroutine can own a piece of mutable state and other goroutines send it requests. That owner becomes the only code that touches the state directly.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;increment&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;reply&lt;/span&gt; &lt;span class="k"&gt;chan&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;runCounter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;requests&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="k"&gt;chan&lt;/span&gt; &lt;span class="n"&gt;increment&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="c"&gt;// Only this goroutine reads or writes total.&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="k"&gt;range&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;total&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;
        &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;reply&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="n"&gt;total&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;incrementCounter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;requests&lt;/span&gt; &lt;span class="k"&gt;chan&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="n"&gt;increment&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;reply&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nb"&gt;make&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;chan&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;requests&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="n"&gt;increment&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;reply&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;reply&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="n"&gt;reply&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This design avoids shared writes to &lt;code&gt;total&lt;/code&gt;, but it is not automatically better than a mutex. It introduces a request channel, a lifecycle for the owner goroutine, and possible backpressure. Choose it when serial ownership matches the domain, such as a connection manager, an in-memory session loop, or a stateful actor-like component. Use a mutex when protected shared state is simpler.&lt;/p&gt;

&lt;h2&gt;
  
  
  Context cancellation is not durable job delivery
&lt;/h2&gt;

&lt;p&gt;Cancellation is for work whose result is no longer needed. It is not a promise that work will finish later. If a request context is cancelled, a worker pool may stop and discard unfinished in-memory jobs by design.&lt;/p&gt;

&lt;p&gt;For payment processing, required notifications, inventory changes, or other state-changing work that must eventually complete, persist the job and its status in a database or durable queue. Use idempotency keys so a retry can be safe even when the earlier attempt may have partly completed. This is a reliability boundary, not something a goroutine or a channel can provide by itself.&lt;/p&gt;

&lt;h2&gt;
  
  
  Go concurrency safety checklist
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Give every goroutine a normal completion path and a cancellation path.&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;select&lt;/code&gt; with &lt;code&gt;ctx.Done()&lt;/code&gt; around channel operations that could otherwise wait forever.&lt;/li&gt;
&lt;li&gt;Decide which producer owns each &lt;code&gt;close(channel)&lt;/code&gt; call.&lt;/li&gt;
&lt;li&gt;Do not assume a channel buffer prevents leaks or proves completion.&lt;/li&gt;
&lt;li&gt;Protect shared mutable state with a mutex, atomics, or single-goroutine ownership.&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;go test -race ./...&lt;/code&gt; in local development and CI.&lt;/li&gt;
&lt;li&gt;Use a durable store and idempotency for work that must survive a process or request ending.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The goal is not to avoid goroutines or channels. It is to make their ownership, stopping behavior, and shared-state rules explicit. The official &lt;a href="https://pkg.go.dev/context" rel="noopener noreferrer"&gt;Go &lt;code&gt;context&lt;/code&gt; documentation&lt;/a&gt;, &lt;a href="https://go.dev/doc/articles/race_detector" rel="noopener noreferrer"&gt;race detector guide&lt;/a&gt;, and &lt;a href="https://go.dev/blog/pipelines" rel="noopener noreferrer"&gt;Go pipelines article&lt;/a&gt; are useful next references.&lt;/p&gt;

&lt;h2&gt;
  
  
  Kinmokusei
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/puffball1567/kinmokusei" rel="noopener noreferrer"&gt;Kinmokusei&lt;/a&gt; is a programming language with TypeScript-inspired syntax that compiles to readable Go. It is intended for writing web backends and Go libraries while using the normal Go toolchain and package ecosystem directly.&lt;/p&gt;

</description>
      <category>go</category>
      <category>backend</category>
      <category>programming</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Nim Concurrency Explained: Async/Await, Threads, Channels, and Parallel Work</title>
      <dc:creator>puffball1567</dc:creator>
      <pubDate>Wed, 16 Sep 2026 13:18:19 +0000</pubDate>
      <link>https://dev.to/puffball1567/nim-concurrency-explained-asyncawait-threads-channels-and-parallel-work-1om9</link>
      <guid>https://dev.to/puffball1567/nim-concurrency-explained-asyncawait-threads-channels-and-parallel-work-1om9</guid>
      <description>&lt;p&gt;Nim gives a program more than one way to work on several tasks at once. That is useful, but it also means that “parallel execution” is not one feature with one answer. A service waiting on many network requests needs a different model from an image-processing job that needs to use several CPU cores.&lt;/p&gt;

&lt;p&gt;This article separates those cases. We will use Nim's standard asynchronous runtime for I/O-bound work, then look at native threads and channels for CPU-bound work. The goal is not to introduce concurrency everywhere; it is to choose a model that matches the work.&lt;/p&gt;

&lt;p&gt;For a Go-oriented view of similar ideas, see &lt;a href="https://dev.to/puffball1567/go-concurrency-how-to-run-parallel-tasks-with-goroutines-channels-and-context-59ib"&gt;Go Concurrency: How to Run Parallel Tasks with Goroutines, Channels, and Context&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Nim concurrency vs parallelism
&lt;/h2&gt;

&lt;p&gt;Concurrency means a program can keep multiple tasks in progress. Parallelism means multiple tasks execute at the same moment on separate CPU cores. They often appear together, but they solve different bottlenecks.&lt;/p&gt;

&lt;p&gt;If ten HTTP requests are waiting on remote servers, the bottleneck is mostly I/O. An event loop can let another request make progress while one is waiting, without giving each request a dedicated OS thread. If ten large images need to be resized, the bottleneck is CPU work. To use several cores, the program needs real parallel execution through threads or a task-pool library.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Workload&lt;/th&gt;
&lt;th&gt;Start with&lt;/th&gt;
&lt;th&gt;Why&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;HTTP, sockets, timers, file readiness&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;asyncdispatch&lt;/code&gt; and &lt;code&gt;async&lt;/code&gt; / &lt;code&gt;await&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;One event loop can manage many waiting operations efficiently.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CPU-heavy parsing, hashing, encoding, image work&lt;/td&gt;
&lt;td&gt;threads or a maintained task-pool package&lt;/td&gt;
&lt;td&gt;CPU work must run on separate threads to use more cores.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;A long-running dedicated background task&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;Thread&lt;/code&gt; and &lt;code&gt;createThread&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;The lifecycle and ownership are explicit.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Durable jobs that must survive restarts&lt;/td&gt;
&lt;td&gt;A database or durable queue&lt;/td&gt;
&lt;td&gt;Threads and channels alone do not persist unfinished work.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Nim async/await for I/O-bound concurrency
&lt;/h2&gt;

&lt;p&gt;Nim's &lt;code&gt;std/asyncdispatch&lt;/code&gt; provides an event loop and &lt;code&gt;Future[T]&lt;/code&gt; values. Mark a procedure with &lt;code&gt;{.async.}&lt;/code&gt; when it will suspend at &lt;code&gt;await&lt;/code&gt;. Calling that procedure returns a future: a handle for a result that will become available later.&lt;/p&gt;

&lt;p&gt;The following program starts two simulated network operations before waiting for either result. &lt;code&gt;sleepAsync&lt;/code&gt; stands in for an operation such as an asynchronous HTTP request; it does not block the event loop while waiting.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nim"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;/[&lt;/span&gt;&lt;span class="n"&gt;asyncdispatch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;strformat&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;

&lt;span class="k"&gt;proc &lt;/span&gt;&lt;span class="nf"&gt;fetchPreview&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="n"&gt;Future&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="p"&gt;{.&lt;/span&gt;&lt;span class="n"&gt;async&lt;/span&gt;&lt;span class="p"&gt;.}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="n"&gt;echo&lt;/span&gt; &lt;span class="s"&gt;"starting "&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;
  &lt;span class="n"&gt;await&lt;/span&gt; &lt;span class="n"&gt;sleepAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c"&gt;# Simulates waiting for network I/O without blocking the loop.&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="s"&gt;"finished {url}"&lt;/span&gt;

&lt;span class="k"&gt;proc &lt;/span&gt;&lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{.&lt;/span&gt;&lt;span class="n"&gt;async&lt;/span&gt;&lt;span class="p"&gt;.}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="c"&gt;# Both procedures reach their first await before main waits for a result.&lt;/span&gt;
  &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;first&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fetchPreview&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"https://api.example.com/one"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;second&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fetchPreview&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"https://api.example.com/two"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;previews&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;await&lt;/span&gt; &lt;span class="n"&gt;all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;first&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;second&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;preview&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;previews&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;echo&lt;/span&gt; &lt;span class="n"&gt;preview&lt;/span&gt;

&lt;span class="n"&gt;waitFor&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;waitFor main()&lt;/code&gt; drives the event loop from a normal command-line program. In a server or GUI application, the application's existing event-loop integration determines where futures are awaited. The key point is that &lt;code&gt;await&lt;/code&gt; yields control while an asynchronous operation is waiting, allowing other ready work on the same event loop to proceed.&lt;/p&gt;

&lt;p&gt;This is concurrent I/O, not a promise that two CPU-intensive loops will run in parallel. Do not put a long CPU loop inside an &lt;code&gt;async&lt;/code&gt; procedure and expect &lt;code&gt;await&lt;/code&gt; elsewhere to make it responsive. The event loop only gets another opportunity to run when the current task reaches an awaitable operation.&lt;/p&gt;

&lt;h2&gt;
  
  
  A real Nim async HTTP example
&lt;/h2&gt;

&lt;p&gt;Nim's standard library includes &lt;code&gt;AsyncHttpClient&lt;/code&gt; for asynchronous HTTP work. The following example starts two GET requests, then waits for both response bodies.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nim"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;/[&lt;/span&gt;&lt;span class="n"&gt;asyncdispatch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;httpclient&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;

&lt;span class="k"&gt;proc &lt;/span&gt;&lt;span class="nf"&gt;fetchBody&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;AsyncHttpClient&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="n"&gt;Future&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="p"&gt;{.&lt;/span&gt;&lt;span class="n"&gt;async&lt;/span&gt;&lt;span class="p"&gt;.}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;await&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;await&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;

&lt;span class="k"&gt;proc &lt;/span&gt;&lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{.&lt;/span&gt;&lt;span class="n"&gt;async&lt;/span&gt;&lt;span class="p"&gt;.}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;newAsyncHttpClient&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

  &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;first&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fetchBody&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"https://example.com"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;second&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fetchBody&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"https://example.org"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;bodies&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;await&lt;/span&gt; &lt;span class="n"&gt;all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;first&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;second&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;bodies&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;echo&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;len&lt;/span&gt;

&lt;span class="n"&gt;waitFor&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Treat a real client as a resource with a lifecycle. Set timeouts, limit how many operations you start at once, handle HTTP status codes and failures, and close or reuse clients according to the API you choose. Starting thousands of futures at once can still overload a remote service or consume too much memory. Async I/O removes the need for one thread per wait; it does not remove rate limits or capacity planning.&lt;/p&gt;

&lt;h2&gt;
  
  
  Native threads for CPU-bound parallel work in Nim
&lt;/h2&gt;

&lt;p&gt;Nim's native threads are appropriate when work should occupy multiple CPU cores. A thread entry procedure is marked with &lt;code&gt;{.thread.}&lt;/code&gt;, a &lt;code&gt;Thread[T]&lt;/code&gt; value represents the running thread, and &lt;code&gt;createThread&lt;/code&gt; starts it. &lt;code&gt;joinThread&lt;/code&gt; waits for it to finish.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nim"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;/[&lt;/span&gt;&lt;span class="n"&gt;math&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;typedthreads&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;Work&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;tuple&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;startAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;endAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;

&lt;span class="k"&gt;proc &lt;/span&gt;&lt;span class="nf"&gt;sumSquares&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;work&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Work&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{.&lt;/span&gt;&lt;span class="n"&gt;thread&lt;/span&gt;&lt;span class="p"&gt;.}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;
  &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;number&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;work&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;startAt&lt;/span&gt;&lt;span class="p"&gt;..&lt;/span&gt;&lt;span class="n"&gt;work&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;endAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;sqrt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;number&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;echo&lt;/span&gt; &lt;span class="n"&gt;total&lt;/span&gt;

&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;worker&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Thread&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Work&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;createThread&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;worker&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sumSquares&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1_000_000&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="c"&gt;# The main thread can perform other work here.&lt;/span&gt;
&lt;span class="n"&gt;worker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;joinThread&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Compile thread-based programs with thread support enabled:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nim c &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="nt"&gt;--threads&lt;/span&gt;:on app.nim
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Current Nim documentation notes that &lt;code&gt;--threads:on&lt;/code&gt; is enabled by default, but keeping the switch in an example makes the threading requirement visible and works with toolchains where it is not the default. A thread is much heavier than an async future. Do not create one native thread per input record; bound the number of active workers to the work and available CPU resources.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pass thread results through a Nim channel
&lt;/h2&gt;

&lt;p&gt;Nim's &lt;code&gt;Channel[T]&lt;/code&gt; is designed for communication between &lt;code&gt;Thread&lt;/code&gt; values. A sending thread places a value into the channel with &lt;code&gt;send&lt;/code&gt;; the receiving thread obtains it with &lt;code&gt;recv&lt;/code&gt;. The example below gives each worker an immutable range and returns its partial result through a module-level channel.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nim"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;/[&lt;/span&gt;&lt;span class="n"&gt;typedthreads&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;math&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;Work&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;tuple&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;startAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;endAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;PartialResult&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;tuple&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;startAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;total&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;

&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Channel&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;PartialResult&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;

&lt;span class="k"&gt;proc &lt;/span&gt;&lt;span class="nf"&gt;sumSquares&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;work&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Work&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{.&lt;/span&gt;&lt;span class="n"&gt;thread&lt;/span&gt;&lt;span class="p"&gt;.}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;
  &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;number&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;work&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;startAt&lt;/span&gt;&lt;span class="p"&gt;..&lt;/span&gt;&lt;span class="n"&gt;work&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;endAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;sqrt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;number&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;send&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;work&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;startAt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;total&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;open&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;workers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;array&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Thread&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Work&lt;/span&gt;&lt;span class="o"&gt;]]&lt;/span&gt;
&lt;span class="n"&gt;createThread&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;workers&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sumSquares&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;500_000&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="n"&gt;createThread&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;workers&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sumSquares&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;500_001&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1_000_000&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;combined&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="err"&gt;_ in 0..&amp;lt;workers.len:&lt;/span&gt;
  &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;partial&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;recv&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c"&gt;# Wait for one worker result.&lt;/span&gt;
  &lt;span class="n"&gt;combined&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;partial&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;total&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;worker&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;workers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mitems&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="n"&gt;worker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;joinThread&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;echo&lt;/span&gt; &lt;span class="n"&gt;combined&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The channel here is module-level on purpose. Nim's documentation explains that channels are safest when stored in process-wide shared memory; passing an ordinary heap-allocated channel to a thread through a pointer requires careful shared allocation. It also notes that values sent through channels are deeply copied and that cyclic data structures are not supported by the current message-passing implementation.&lt;/p&gt;

&lt;p&gt;That means a good first thread design is usually: give a worker compact input data, let it create thread-local temporary state, and return a compact result. Avoid casually sharing a mutable &lt;code&gt;seq&lt;/code&gt;, &lt;code&gt;Table&lt;/code&gt;, ref object, or client instance between threads.&lt;/p&gt;

&lt;h2&gt;
  
  
  Locks and shared state: prefer ownership first
&lt;/h2&gt;

&lt;p&gt;Threads create the possibility of simultaneous access to shared state. If two threads update the same counter, map, or object without synchronization, the result is a data race.&lt;/p&gt;

&lt;p&gt;When a shared value is unavoidable, use the synchronization primitive that represents the rule. A &lt;code&gt;Lock&lt;/code&gt; can protect a short, shared update.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nim"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;locks&lt;/span&gt;

&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;totalLock&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Lock&lt;/span&gt;
&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;total&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;

&lt;span class="k"&gt;proc &lt;/span&gt;&lt;span class="nf"&gt;addToTotal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{.&lt;/span&gt;&lt;span class="n"&gt;thread&lt;/span&gt;&lt;span class="p"&gt;.}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="n"&gt;acquire&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;totalLock&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;
  &lt;span class="n"&gt;release&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;totalLock&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;initLock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;totalLock&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c"&gt;# Create and join threads that call addToTotal here.&lt;/span&gt;
&lt;span class="n"&gt;deinitLock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;totalLock&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Keep the lock scope small. Do not acquire a lock and then make a network request, perform slow disk I/O, or wait for another unbounded operation. More importantly, consider whether the state needs to be shared at all. Passing results through a channel and combining them in one owner thread often produces a simpler design.&lt;/p&gt;

&lt;p&gt;Nim additionally offers guards and lock annotations for selected shared locations. They can make intended locking rules visible and help the compiler check accesses. They are useful once a design genuinely needs shared state, but they are not a substitute for deciding ownership and lifecycle first.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why the built-in Nim threadpool is not the default recommendation
&lt;/h2&gt;

&lt;p&gt;You may find older Nim examples using &lt;code&gt;std/threadpool&lt;/code&gt;, &lt;code&gt;spawn&lt;/code&gt;, &lt;code&gt;FlowVar&lt;/code&gt;, and &lt;code&gt;parallel&lt;/code&gt;. They are part of Nim's history and still appear in documentation, but the current &lt;code&gt;std/threadpool&lt;/code&gt; documentation marks the module deprecated and its API unstable. It recommends maintained Nimble packages such as &lt;code&gt;malebolgia&lt;/code&gt;, &lt;code&gt;taskpools&lt;/code&gt;, or &lt;code&gt;weave&lt;/code&gt; instead.&lt;/p&gt;

&lt;p&gt;That does not mean old code is automatically wrong. It means a new application should choose deliberately: use &lt;code&gt;asyncdispatch&lt;/code&gt; for I/O concurrency, use &lt;code&gt;Thread&lt;/code&gt; and &lt;code&gt;Channel&lt;/code&gt; when a small number of dedicated native threads is enough, or evaluate a maintained task-pool package for a larger CPU work queue. Do not choose a thread pool only because an operation is asynchronous; first identify whether the bottleneck is waiting or computation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cancellation, retries, and durable work are separate concerns
&lt;/h2&gt;

&lt;p&gt;An asynchronous operation or thread does not become a reliable background job merely because it runs outside the caller's immediate control. If a process exits, memory-only jobs and channels disappear. If an HTTP request is cancelled, it can be correct to stop optional work such as a page preview or a search suggestion.&lt;/p&gt;

&lt;p&gt;For a payment, a required notification, or another state change that must eventually happen, persist the job state in a database or durable queue. Make retries idempotent, record whether an attempt may have completed, and make recovery possible after a restart. This is the same distinction that matters in Go worker pools: cancellation is for work that is no longer needed; durable delivery is for work that must not be lost.&lt;/p&gt;

&lt;h2&gt;
  
  
  A practical Nim concurrency checklist
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Use &lt;code&gt;async&lt;/code&gt; / &lt;code&gt;await&lt;/code&gt; and &lt;code&gt;asyncdispatch&lt;/code&gt; for I/O-bound waiting.&lt;/li&gt;
&lt;li&gt;Use native threads only when CPU-bound work benefits from several cores or a task must have its own dedicated thread.&lt;/li&gt;
&lt;li&gt;Do not block an event loop with long CPU work.&lt;/li&gt;
&lt;li&gt;Bound concurrency; async futures and threads can both overwhelm a downstream service when created without limits.&lt;/li&gt;
&lt;li&gt;Keep thread inputs and results small, explicit, and independent where possible.&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;Channel[T]&lt;/code&gt; for thread communication, and be careful about its copying and shared-memory rules.&lt;/li&gt;
&lt;li&gt;Use locks only around short shared-state operations; prefer a single owner when that models the problem cleanly.&lt;/li&gt;
&lt;li&gt;Do not use memory-only concurrency primitives as a durable queue.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Nim's official &lt;a href="https://nim-lang.org/docs/asyncdispatch.html" rel="noopener noreferrer"&gt;asyncdispatch documentation&lt;/a&gt;, &lt;a href="https://nim-lang.org/docs/manual.html#threads" rel="noopener noreferrer"&gt;thread manual section&lt;/a&gt;, &lt;a href="https://nim-lang.org/docs/system.html#channel-support-for-threads" rel="noopener noreferrer"&gt;channel documentation&lt;/a&gt;, and &lt;a href="https://nim-lang.org/docs/threadpool.html" rel="noopener noreferrer"&gt;threadpool documentation&lt;/a&gt; are good references when choosing an implementation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Related projects
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/puffball1567/koutendb" rel="noopener noreferrer"&gt;KoutenDB&lt;/a&gt;&lt;/strong&gt; — An open-source document and vector database written in Nim. Unlike a conventional relational database, it uses a locality-first retrieval model: related records are placed in application-defined &lt;code&gt;ring&lt;/code&gt; coordinates, and that placement determines the initial retrieval scope, persistent disk locality, and cluster routing boundary. KoutenDB uses this model to begin from relevant local data instead of broadly reading unrelated data and filtering it afterward, aiming to reduce retrieval work and the candidate context sent to RAG or LLM systems.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/puffball1567/clay-board-style-system" rel="noopener noreferrer"&gt;Clay Board Style System&lt;/a&gt;&lt;/strong&gt; — A CSS-inspired primitive engine for building expressive native GUI toolkits without a DOM or WebView. It lets applications keep component behavior in typed host-language code while sharing portable presentation across Nim, C++, and Rust.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/puffball1567/joubako" rel="noopener noreferrer"&gt;Joubako&lt;/a&gt;&lt;/strong&gt; — An application-facing asynchronous HTTP client for Nim. It uses standard Nim async foundations while adding typed results, retries, streaming, TLS, and production-oriented request controls.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Related reading
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://dev.to/puffball1567/go-concurrency-how-to-run-parallel-tasks-with-goroutines-channels-and-context-59ib"&gt;Go Concurrency: How to Run Parallel Tasks with Goroutines, Channels, and Context&lt;/a&gt; explains the same broad concerns from a Go perspective, including goroutines, channels, worker pools, cancellation, and retries.&lt;/p&gt;

</description>
      <category>nim</category>
      <category>programming</category>
      <category>backend</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Go Concurrency: How to Run Parallel Tasks with Goroutines, Channels, and Context</title>
      <dc:creator>puffball1567</dc:creator>
      <pubDate>Wed, 16 Sep 2026 08:20:56 +0000</pubDate>
      <link>https://dev.to/puffball1567/go-concurrency-how-to-run-parallel-tasks-with-goroutines-channels-and-context-59ib</link>
      <guid>https://dev.to/puffball1567/go-concurrency-how-to-run-parallel-tasks-with-goroutines-channels-and-context-59ib</guid>
      <description>&lt;p&gt;Go is useful for backend services that need to wait on several slow operations at once. A service may call external HTTP APIs, read files, or handle queue messages. Instead of waiting for one operation to finish before starting the next, Go can let several tasks make progress together.&lt;/p&gt;

&lt;p&gt;The first idea to learn is the goroutine: a lightweight unit of work managed by the Go runtime. We will begin with a small example of what &lt;code&gt;go&lt;/code&gt; changes, then learn how channels let a program wait for a result. Only after those basics will we build toward timeouts and a worker pool.&lt;/p&gt;

&lt;h2&gt;
  
  
  Go concurrency vs parallelism
&lt;/h2&gt;

&lt;p&gt;Concurrency means that a program can manage more than one task at a time. Parallelism means that two or more tasks are physically executing at the same instant on separate CPU resources. Goroutines express concurrency; the Go runtime and the machine determine how much actual CPU parallelism is available.&lt;/p&gt;

&lt;p&gt;For many backend tasks, the immediate benefit is overlapping I/O. While one request waits for a network response, another request can continue. That does not mean every loop should create an unlimited number of goroutines.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is a goroutine in Go?
&lt;/h2&gt;

&lt;p&gt;A goroutine is a function call that Go can run independently from the function that started it. A normal function call waits until the called function returns. Put &lt;code&gt;go&lt;/code&gt; before the same call, and the caller continues without waiting.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"fmt"&lt;/span&gt;
    &lt;span class="s"&gt;"time"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;printTask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"finished:"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"main starts"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;go&lt;/span&gt; &lt;span class="n"&gt;printTask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"background task"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c"&gt;// Start this task separately.&lt;/span&gt;

    &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"main keeps going"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c"&gt;// This can run before printTask finishes.&lt;/span&gt;
    &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;10&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Millisecond&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c"&gt;// Only for this small demonstration.&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The order of the last two messages is not guaranteed. The important point is that &lt;code&gt;main&lt;/code&gt; does not wait at &lt;code&gt;go printTask(...)&lt;/code&gt;. A Go program exits when &lt;code&gt;main&lt;/code&gt; returns, even if another goroutine still has work to do. The &lt;code&gt;time.Sleep&lt;/code&gt; line merely gives the background task time to print in this demonstration; it is not how production code should wait for goroutines.&lt;/p&gt;

&lt;p&gt;The next section introduces channels, which give us a proper way to receive a result and know when a goroutine has finished.&lt;/p&gt;

&lt;h2&gt;
  
  
  Go channels: passing a value between goroutines
&lt;/h2&gt;

&lt;p&gt;A channel is a typed path for values to travel between goroutines. Create one with &lt;code&gt;make(chan Type)&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;messages&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nb"&gt;make&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;chan&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c"&gt;// This channel carries strings.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This form creates an unbuffered channel: a sender waits until another goroutine receives the value. &lt;code&gt;make(chan Type, capacity)&lt;/code&gt; adds a buffer. The second argument is the number of values the channel may hold temporarily before a sender has to wait. For example, &lt;code&gt;make(chan string, 3)&lt;/code&gt; can hold three strings.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;&amp;lt;-&lt;/code&gt; operator is used with channels. There are only two basic forms:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;messages&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="s"&gt;"hello"&lt;/span&gt;      &lt;span class="c"&gt;// Send a string into the channel.&lt;/span&gt;
&lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="n"&gt;messages&lt;/span&gt;     &lt;span class="c"&gt;// Receive the next string from the channel.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The arrow points to the value's destination. A receive waits until a value is available, which is helpful when one goroutine needs the result of another.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="s"&gt;"fmt"&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;greetings&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nb"&gt;make&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;chan&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c"&gt;// A mailbox shared by two goroutines.&lt;/span&gt;

    &lt;span class="k"&gt;go&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c"&gt;// This runs separately from main.&lt;/span&gt;
        &lt;span class="n"&gt;greetings&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="s"&gt;"hello from another goroutine"&lt;/span&gt;
    &lt;span class="p"&gt;}()&lt;/span&gt;

    &lt;span class="c"&gt;// main waits here until the other goroutine sends its message.&lt;/span&gt;
    &lt;span class="n"&gt;greeting&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="n"&gt;greetings&lt;/span&gt;
    &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;greeting&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;h2&gt;
  
  
  WaitGroup and channels have different roles
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;sync.WaitGroup&lt;/code&gt; waits for a known group of goroutines to finish. It does not send results or errors; it only tracks whether work has completed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;wg&lt;/span&gt; &lt;span class="n"&gt;sync&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WaitGroup&lt;/span&gt;

&lt;span class="n"&gt;wg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c"&gt;// We are about to start one goroutine.&lt;/span&gt;
&lt;span class="k"&gt;go&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;defer&lt;/span&gt; &lt;span class="n"&gt;wg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Done&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c"&gt;// Mark this work as finished before returning.&lt;/span&gt;
    &lt;span class="c"&gt;// Do one piece of work.&lt;/span&gt;
&lt;span class="p"&gt;}()&lt;/span&gt;

&lt;span class="n"&gt;wg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Wait&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c"&gt;// Wait until every Add has a matching Done.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Channels move jobs and results. A wait group tells the coordinating code when every worker has finished and a result channel can safely close.&lt;/p&gt;

&lt;h2&gt;
  
  
  Go worker pool example: bounded parallel HTTP requests
&lt;/h2&gt;

&lt;p&gt;Starting one goroutine for every URL can overload a remote API or use too much memory when the input is large. A worker pool starts a fixed number of workers instead. Each worker takes the next URL when it becomes free.&lt;/p&gt;

&lt;p&gt;The following function fetches several URLs with at most &lt;code&gt;workers&lt;/code&gt; requests in progress. The comments describe the purpose of each concurrency primitive.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"context"&lt;/span&gt;
    &lt;span class="s"&gt;"io"&lt;/span&gt;
    &lt;span class="s"&gt;"net/http"&lt;/span&gt;
    &lt;span class="s"&gt;"sync"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;FetchResult&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;URL&lt;/span&gt;    &lt;span class="kt"&gt;string&lt;/span&gt;
    &lt;span class="n"&gt;Status&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;
    &lt;span class="n"&gt;Body&lt;/span&gt;   &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;
    &lt;span class="n"&gt;Err&lt;/span&gt;    &lt;span class="kt"&gt;error&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;FetchAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;urls&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;workers&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="n"&gt;FetchResult&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;workers&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;workers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="c"&gt;// Always create at least one worker.&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;jobs&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nb"&gt;make&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;chan&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c"&gt;// URLs waiting to be fetched; sender and worker meet here.&lt;/span&gt;

    &lt;span class="c"&gt;// The second make argument is the channel buffer size. At most one result&lt;/span&gt;
    &lt;span class="c"&gt;// exists per URL, so len(urls) gives results room for every possible result.&lt;/span&gt;
    &lt;span class="c"&gt;// Workers therefore do not need to wait for the collector after each fetch.&lt;/span&gt;
    &lt;span class="n"&gt;results&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nb"&gt;make&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;chan&lt;/span&gt; &lt;span class="n"&gt;FetchResult&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;urls&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

    &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;wg&lt;/span&gt; &lt;span class="n"&gt;sync&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WaitGroup&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;workers&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;wg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c"&gt;// Register this worker before it starts.&lt;/span&gt;

        &lt;span class="k"&gt;go&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;defer&lt;/span&gt; &lt;span class="n"&gt;wg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Done&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c"&gt;// Tell the coordinator this worker has stopped.&lt;/span&gt;

            &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Done&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
                    &lt;span class="c"&gt;// The caller cancelled or timed out. Stop this worker.&lt;/span&gt;
                    &lt;span class="k"&gt;return&lt;/span&gt;

                &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;open&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="n"&gt;jobs&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
                    &lt;span class="c"&gt;// Receiving from a closed channel returns open == false.&lt;/span&gt;
                    &lt;span class="c"&gt;// That means the producer has no more URLs to send.&lt;/span&gt;
                    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;open&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                        &lt;span class="k"&gt;return&lt;/span&gt;
                    &lt;span class="p"&gt;}&lt;/span&gt;

                    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;fetchOne&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

                    &lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;results&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
                        &lt;span class="c"&gt;// Give the completed result to the caller.&lt;/span&gt;
                    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Done&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
                        &lt;span class="c"&gt;// Do not remain blocked if the caller has left.&lt;/span&gt;
                        &lt;span class="k"&gt;return&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;span class="p"&gt;}()&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;go&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c"&gt;// This goroutine produces jobs for the workers.&lt;/span&gt;
        &lt;span class="k"&gt;defer&lt;/span&gt; &lt;span class="nb"&gt;close&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;jobs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c"&gt;// Closing jobs tells workers that production is over.&lt;/span&gt;

        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="k"&gt;range&lt;/span&gt; &lt;span class="n"&gt;urls&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;jobs&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
                &lt;span class="c"&gt;// Send one URL to whichever worker is ready.&lt;/span&gt;
            &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Done&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
                &lt;span class="c"&gt;// Stop producing jobs when the caller cancels.&lt;/span&gt;
                &lt;span class="k"&gt;return&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;span class="k"&gt;go&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c"&gt;// Only close results after every worker has finished sending.&lt;/span&gt;
        &lt;span class="n"&gt;wg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Wait&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="nb"&gt;close&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}()&lt;/span&gt;

    &lt;span class="n"&gt;collected&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nb"&gt;make&lt;/span&gt;&lt;span class="p"&gt;([]&lt;/span&gt;&lt;span class="n"&gt;FetchResult&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;urls&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="k"&gt;range&lt;/span&gt; &lt;span class="n"&gt;results&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c"&gt;// range ends automatically after the results channel is closed.&lt;/span&gt;
        &lt;span class="n"&gt;collected&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;collected&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;collected&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;fetchOne&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;FetchResult&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;// Attach ctx so cancellation can stop the HTTP request too.&lt;/span&gt;
    &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewRequestWithContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MethodGet&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;FetchResult&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;URL&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Err&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Do&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;FetchResult&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;URL&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Err&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;defer&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Body&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c"&gt;// Release the HTTP connection when we are done.&lt;/span&gt;

    &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ReadAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Body&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;FetchResult&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;URL&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Status&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StatusCode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Body&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Err&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;err&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;&lt;code&gt;select&lt;/code&gt; may look complex, but its job is simple: wait until one of several channel operations can proceed. Here, a worker either receives another URL or notices that cancellation was requested. Read &lt;code&gt;case &amp;lt;-ctx.Done()&lt;/code&gt; as “stop when the caller says this work is no longer needed.”&lt;/p&gt;

&lt;h2&gt;
  
  
  Go context cancellation and timeouts in plain language
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;context.Context&lt;/code&gt; is a value passed along with an operation. It lets child work learn two things: whether the caller has cancelled, and whether the operation has reached its time limit.&lt;/p&gt;

&lt;p&gt;This example creates a context with a three-second timeout:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"context"&lt;/span&gt;
    &lt;span class="s"&gt;"time"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cancel&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WithTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Background&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Second&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;defer&lt;/span&gt; &lt;span class="n"&gt;cancel&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c"&gt;// Release resources even when the timeout did not fire.&lt;/span&gt;

&lt;span class="n"&gt;results&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;FetchAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DefaultClient&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;urls&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;8&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;8&lt;/code&gt; is a limit: no more than eight workers fetch URLs at once. It is not a magic performance value. Choose a limit based on the downstream API, connection pool, rate limits, memory cost, and whether the work is mostly I/O or CPU.&lt;/p&gt;

&lt;h2&gt;
  
  
  What cancellation means in this simple worker pool
&lt;/h2&gt;

&lt;p&gt;The worker pool above is intentionally a simple, best-effort pattern. When its context is cancelled, it stops producing new jobs and asks workers to stop. Jobs that have not completed are not put back into the channel and are not retried. This is appropriate when the caller no longer needs the answer, such as collecting optional search results or data for a page the user has left.&lt;/p&gt;

&lt;p&gt;It is not a durable queue. Unlike a system such as SQS, the in-memory &lt;code&gt;jobs&lt;/code&gt; channel does not record acknowledgements, persist unfinished work, or make another worker retry a job after a process stops. Work that must happen exactly once or eventually happen, such as payment-related state changes or a required notification, needs a different implementation from the one above.&lt;/p&gt;

&lt;p&gt;In an HTTP server, the handler receives the request as &lt;code&gt;*http.Request&lt;/code&gt;. Call &lt;code&gt;request.Context()&lt;/code&gt; to get the context that belongs to that specific client request, then pass it to the work started for that request.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;handleSearch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ResponseWriter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;// This context belongs to the current HTTP request.&lt;/span&gt;
    &lt;span class="c"&gt;// It is cancelled if the client goes away.&lt;/span&gt;
    &lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="n"&gt;urls&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="s"&gt;"https://api.example.com/one"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s"&gt;"https://api.example.com/two"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c"&gt;// FetchAll and every worker receive the same cancellation signal.&lt;/span&gt;
    &lt;span class="n"&gt;results&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;FetchAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DefaultClient&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;urls&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c"&gt;// Write results only if this handler still has a client to answer.&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewEncoder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;results&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;For request-scoped backend work, use &lt;code&gt;request.Context()&lt;/code&gt; rather than creating &lt;code&gt;context.Background()&lt;/code&gt; inside the handler. &lt;code&gt;Background()&lt;/code&gt; has no connection to the client request, so child work would continue even after the client disconnects. The example assumes &lt;code&gt;encoding/json&lt;/code&gt; and &lt;code&gt;net/http&lt;/code&gt; are imported.&lt;/p&gt;

&lt;h2&gt;
  
  
  Go retry example: retry a failed read with a limit
&lt;/h2&gt;

&lt;p&gt;Some practical tasks can retry a failure before returning it. For a read-only HTTP request, a small bounded retry loop can be reasonable when a temporary network error, rate limit, or server error occurs. The retry loop itself should still obey the same context, so it stops immediately when the caller cancels or the deadline expires.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"context"&lt;/span&gt;
    &lt;span class="s"&gt;"net/http"&lt;/span&gt;
    &lt;span class="s"&gt;"time"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;fetchWithRetry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;maxAttempts&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;FetchResult&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="n"&gt;FetchResult&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;attempt&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;attempt&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="n"&gt;maxAttempts&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;attempt&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c"&gt;// Do not start another attempt after the caller has cancelled.&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;FetchResult&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;URL&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Err&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fetchOne&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;shouldRetry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;attempt&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;maxAttempts&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c"&gt;// Success, a non-retryable failure, or no attempts left.&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="c"&gt;// Wait a little longer after each failure: 200ms, 400ms, 600ms, ...&lt;/span&gt;
        &lt;span class="n"&gt;delay&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Duration&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;attempt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="m"&gt;200&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Millisecond&lt;/span&gt;
        &lt;span class="n"&gt;timer&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewTimer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;delay&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Done&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
            &lt;span class="c"&gt;// Stop waiting and return the cancellation error immediately.&lt;/span&gt;
            &lt;span class="n"&gt;timer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Stop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;FetchResult&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;URL&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Err&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;
        &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="n"&gt;timer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;C&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
            &lt;span class="c"&gt;// The delay ended; the loop starts the next attempt.&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="c"&gt;// The loop always returns earlier; this keeps the compiler satisfied.&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;shouldRetry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="n"&gt;FetchResult&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt; &lt;span class="c"&gt;// A temporary network error may succeed on a later attempt.&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c"&gt;// Retry rate limits and server errors, but not ordinary client errors such as 404.&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Status&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StatusTooManyRequests&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Status&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="m"&gt;500&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To use this policy, replace &lt;code&gt;fetchOne(ctx, client, url)&lt;/code&gt; in the worker with &lt;code&gt;fetchWithRetry(ctx, client, url, 3)&lt;/code&gt;. The number &lt;code&gt;3&lt;/code&gt; is a policy decision, not a default that fits every API. A real client may also respect a &lt;code&gt;Retry-After&lt;/code&gt; response header and add random jitter so many workers do not retry at the same moment.&lt;/p&gt;

&lt;p&gt;Retry only operations whose repeated execution is safe. Retrying a &lt;code&gt;GET&lt;/code&gt; request is usually easier to reason about than retrying a payment or a &lt;code&gt;POST&lt;/code&gt; that creates a record. For state-changing work, use an idempotency key and persist job state in a database or durable queue. That lets a later worker distinguish “never started,” “may have completed,” and “needs another attempt.”&lt;/p&gt;

&lt;h2&gt;
  
  
  Avoid common Go concurrency mistakes
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Limit work instead of creating one goroutine for every untrusted input item.&lt;/li&gt;
&lt;li&gt;Decide which goroutine closes each channel. Usually it is the sender that knows there will be no more values.&lt;/li&gt;
&lt;li&gt;Never close the same channel from multiple goroutines.&lt;/li&gt;
&lt;li&gt;Give every goroutine a way to stop when its consumer leaves or its context is cancelled.&lt;/li&gt;
&lt;li&gt;Use channels to hand values between goroutines. Use &lt;code&gt;sync.Mutex&lt;/code&gt; when several goroutines must safely change the same shared value.&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;go test -race ./...&lt;/code&gt; regularly. The race detector can find accidental simultaneous access to shared memory.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The goal is not to create as many goroutines as possible. It is to make the number of active tasks, the cancellation behavior, and the owner of each result clear. The official &lt;a href="https://pkg.go.dev/context" rel="noopener noreferrer"&gt;Go context documentation&lt;/a&gt;, &lt;a href="https://pkg.go.dev/sync" rel="noopener noreferrer"&gt;sync package documentation&lt;/a&gt;, and &lt;a href="https://go.dev/blog/pipelines" rel="noopener noreferrer"&gt;pipeline and cancellation article&lt;/a&gt; are useful next references.&lt;/p&gt;

&lt;h2&gt;
  
  
  Kinmokusei
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/puffball1567/kinmokusei" rel="noopener noreferrer"&gt;Kinmokusei&lt;/a&gt; is a programming language with TypeScript-inspired syntax that compiles to readable Go. It is intended for writing web backends and Go libraries while using the normal Go toolchain and package ecosystem directly.&lt;/p&gt;

</description>
      <category>go</category>
      <category>backend</category>
      <category>programming</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>How to Write Object-Oriented Code in Go: Structs, Methods, Interfaces, and Composition</title>
      <dc:creator>puffball1567</dc:creator>
      <pubDate>Mon, 14 Sep 2026 02:59:00 +0000</pubDate>
      <link>https://dev.to/puffball1567/how-to-write-object-oriented-code-in-go-structs-methods-interfaces-and-composition-3fok</link>
      <guid>https://dev.to/puffball1567/how-to-write-object-oriented-code-in-go-structs-methods-interfaces-and-composition-3fok</guid>
      <description>&lt;p&gt;Go does not have classes or inheritance, but it does not prevent object-oriented design. It simply moves the building blocks into different language features: structs hold state, methods attach behavior, interfaces describe capabilities, and composition assembles collaborating parts.&lt;/p&gt;

&lt;p&gt;The productive question is not “how can I recreate a class hierarchy in Go?” It is “which data owns this behavior, and which dependency should this component require?” This article builds a small application service around that question.&lt;/p&gt;

&lt;h2&gt;
  
  
  Start with data and behavior
&lt;/h2&gt;

&lt;p&gt;Suppose an application lets a user change their display name. The domain value is a struct, while the operation belongs on a service that has access to user storage.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"context"&lt;/span&gt;
    &lt;span class="s"&gt;"errors"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;User&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;ID&lt;/span&gt;          &lt;span class="kt"&gt;int64&lt;/span&gt;
    &lt;span class="n"&gt;DisplayName&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;Repository&lt;/span&gt; &lt;span class="k"&gt;interface&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;FindByID&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="kt"&gt;int64&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;Save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;Service&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;repository&lt;/span&gt; &lt;span class="n"&gt;Repository&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;NewService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;repository&lt;/span&gt; &lt;span class="n"&gt;Repository&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Service&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;Service&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;repository&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;repository&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Service&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;Rename&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="kt"&gt;int64&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;{},&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;New&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"display name is required"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;repository&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FindByID&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;{},&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DisplayName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;repository&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;{},&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;User&lt;/code&gt; is a value with data. &lt;code&gt;Service&lt;/code&gt; is a struct with a dependency and a cohesive operation. The constructor function makes the dependency explicit at creation time. None of these types needs a base class to have a clear responsibility.&lt;/p&gt;

&lt;h2&gt;
  
  
  Define interfaces where they are consumed
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;Repository&lt;/code&gt; interface above belongs next to &lt;code&gt;Service&lt;/code&gt;, not necessarily next to a database implementation. The service states the smallest capability it needs: load a user and save a user. Any type with those methods satisfies the interface automatically.&lt;/p&gt;

&lt;p&gt;That has two useful consequences. Production code can use a PostgreSQL-backed repository, while a test can use a small in-memory fake. Neither implementation needs to import a shared “repository base class” or declare that it implements the interface.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;MemoryRepository&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="k"&gt;map&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;int64&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;MemoryRepository&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;FindByID&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="kt"&gt;int64&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ok&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;ok&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;{},&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;New&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"user not found"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;MemoryRepository&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;Save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is dependency inversion without a framework. The application service depends on a capability, rather than on a particular storage driver.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prefer composition to inheritance
&lt;/h2&gt;

&lt;p&gt;Go supports struct embedding, but embedding is not a substitute for a deep inheritance tree. It promotes fields and methods from one struct into another; it does not give Go classes, virtual methods, or a general subtype model.&lt;/p&gt;

&lt;p&gt;For backend code, explicit fields are usually clearer than embedding. If a service needs a clock, a logger, and an email sender, name them as dependencies:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;EmailSender&lt;/span&gt; &lt;span class="k"&gt;interface&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;subject&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;NotificationService&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="n"&gt;UserLookup&lt;/span&gt;
    &lt;span class="n"&gt;email&lt;/span&gt; &lt;span class="n"&gt;EmailSender&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This makes the object graph visible, keeps testing straightforward, and avoids coupling unrelated behavior through a shared parent type. Use embedding when the promoted behavior genuinely reads as part of the receiving type, not merely to reduce typing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keep interfaces narrow and delay abstraction
&lt;/h2&gt;

&lt;p&gt;Not every struct needs an interface. A useful default is to create a concrete type first, then introduce an interface at the consumer boundary when a second implementation or a test seam actually exists.&lt;/p&gt;

&lt;p&gt;Large interfaces tend to become accidental framework contracts. Small interfaces are easier to implement, easier to test, and clearer about what an operation needs. Go’s standard library follows this style: a type only needs to provide the methods required by the consumer.&lt;/p&gt;

&lt;h2&gt;
  
  
  A practical Go OOP checklist
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Use structs to group state.&lt;/li&gt;
&lt;li&gt;Attach behavior with methods when that behavior naturally belongs to the state or service.&lt;/li&gt;
&lt;li&gt;Use constructor functions to make required dependencies explicit.&lt;/li&gt;
&lt;li&gt;Define small interfaces at the point where they are consumed.&lt;/li&gt;
&lt;li&gt;Prefer explicit composition to inheritance-shaped abstractions.&lt;/li&gt;
&lt;li&gt;Pass &lt;code&gt;context.Context&lt;/code&gt; through request-scoped backend operations.&lt;/li&gt;
&lt;li&gt;Keep transport concerns such as HTTP request parsing outside domain services.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This approach is object-oriented in the useful sense: state, behavior, dependencies, and boundaries have clear ownership. It just does not require a class hierarchy to achieve it. The official &lt;a href="https://go.dev/tour/methods/1" rel="noopener noreferrer"&gt;Go documentation on methods and interfaces&lt;/a&gt; is a good companion when learning the language mechanics.&lt;/p&gt;

&lt;h2&gt;
  
  
  Kinmokusei
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/puffball1567/kinmokusei" rel="noopener noreferrer"&gt;Kinmokusei&lt;/a&gt; is a programming language with TypeScript-inspired syntax that compiles to readable Go. It is intended for writing web backends and Go libraries while using the normal Go toolchain and package ecosystem directly.&lt;/p&gt;

</description>
      <category>go</category>
      <category>backend</category>
      <category>programming</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Kinmokusei v0.2.0: Generic OOP and Go Type Constraints for a TypeScript-Inspired Language</title>
      <dc:creator>puffball1567</dc:creator>
      <pubDate>Sat, 12 Sep 2026 18:37:19 +0000</pubDate>
      <link>https://dev.to/puffball1567/kinmokusei-v020-generic-oop-and-go-type-constraints-for-a-typescript-inspired-language-1joh</link>
      <guid>https://dev.to/puffball1567/kinmokusei-v020-generic-oop-and-go-type-constraints-for-a-typescript-inspired-language-1joh</guid>
      <description>&lt;p&gt;Kinmokusei v0.2.0 expands the language in the places where application code and the Go ecosystem meet: generic object-oriented programming, generic constraints, type aliases, and JSON interop. Kinmokusei uses TypeScript-inspired syntax, but compiles to readable Go and uses the ordinary Go toolchain and package ecosystem directly. This release makes several of those boundaries more capable without treating generated Go as an opaque implementation detail.&lt;/p&gt;

&lt;h2&gt;
  
  
  Generic class inheritance and virtual dispatch
&lt;/h2&gt;

&lt;p&gt;Classes in Kinmokusei are reference types. v0.2.0 adds generic class static methods, generic class inheritance, and generic virtual dispatch. A generic derived class can inherit a base class with concrete or remapped type arguments, retain the inherited state, and override a virtual member while preserving the expected dispatch behavior through the hierarchy.&lt;/p&gt;

&lt;p&gt;That matters when a reusable abstraction carries its data type through more than one layer. A paged result, a domain-specific collection, or a typed application model should not need to discard its type parameter merely because it adds behavior in a derived class.&lt;/p&gt;

&lt;p&gt;The release also adds descendant-aware class downcasts. A checked downcast can recover a more specific type within the same class hierarchy while keeping the failure path explicit, rather than silently assuming that every base-class value has the desired derived shape.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;guide&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;animal&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;GuideDog&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;console&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;guide&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;speak&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;The checked form returns a value together with a boolean result. It makes the runtime boundary visible at the call site, while reference identity and virtual dispatch remain part of the class model.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use Go type-set constraints from Kinmokusei
&lt;/h2&gt;

&lt;p&gt;Go libraries increasingly express generic capabilities through interface type sets. v0.2.0 lets Kinmokusei use exported constraints from the Go standard library and external Go modules, rather than limiting generic code to &lt;code&gt;comparable&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;For example, code can use a constraint such as &lt;code&gt;cmp.Ordered&lt;/code&gt; when an operation really requires an ordered value:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;go&lt;/span&gt; &lt;span class="nx"&gt;cmp&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;cmp&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;earlier&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;cmp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Ordered&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;right&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;left&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;right&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;This is useful because it keeps the constraint owned by the Go package that defines it. Kinmokusei code can participate in the same generic API shapes as Go code, instead of recreating a parallel constraint vocabulary.&lt;/p&gt;

&lt;h2&gt;
  
  
  Generic aliases without raising the minimum Go version
&lt;/h2&gt;

&lt;p&gt;v0.2.0 also adds transparent generic aliases. An alias can give a reusable Kinmokusei name to a parameterized shape without introducing a new nominal type:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;alias&lt;/span&gt; &lt;span class="nx"&gt;Values&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;
&lt;span class="nx"&gt;alias&lt;/span&gt; &lt;span class="nx"&gt;Lookup&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;K&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;V&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;K&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;V&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The compiler expands a generic alias to its instantiated underlying type at generated-Go boundaries. That detail is important for compatibility: the emitted code remains usable with the project's Go 1.23 minimum instead of depending on later Go generic-alias declarations. Kinmokusei tooling still retains the source-level alias, while Go consumers see the concrete Go type they expect.&lt;/p&gt;

&lt;p&gt;v0.2.0 also permits a native struct as the underlying representation of a distinct type. This gives application code a way to add nominal meaning to an existing value layout while keeping conversions explicit and retaining Go-compatible value behavior.&lt;/p&gt;

&lt;h2&gt;
  
  
  Stable JSON behavior for generic and inherited classes
&lt;/h2&gt;

&lt;p&gt;JSON is often the point where a type system meets an external API, stored data, or a frontend. v0.2.0 stabilizes JSON interop for generic classes: public fields use stable Kinmokusei field names, inherited public state is included, and private or protected state is excluded.&lt;/p&gt;

&lt;p&gt;Decoding into a class instance preserves the class's reference-oriented model. Constructor-created instances retain their identity and virtual dispatch behavior, rather than being reduced to an unstructured map at the application boundary. Where a wire format needs special initialization or a different representation, an application can still define its own JSON handling.&lt;/p&gt;

&lt;h2&gt;
  
  
  A release focused on typed boundaries
&lt;/h2&gt;

&lt;p&gt;These features are connected by the same goal: carry useful type information across the boundaries that real Go applications need to cross. Generic classes can model reusable reference-oriented behavior, Go type sets make interop constraints available directly, generic aliases keep source code readable without increasing the Go version requirement, and stable JSON rules make external data handling predictable.&lt;/p&gt;

&lt;p&gt;Kinmokusei v0.2.0 is available now. Developers with Go installed can try the release with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;go &lt;span class="nb"&gt;install &lt;/span&gt;github.com/puffball1567/kinmokusei/cmd/keika@v0.2.0
keika version
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The compiler supports Go 1.23 through Go 1.27. The release notes, source, and executable examples are available in the &lt;a href="https://github.com/puffball1567/kinmokusei" rel="noopener noreferrer"&gt;Kinmokusei repository&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>go</category>
      <category>typescript</category>
      <category>programming</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Introducing Kinmokusei: TypeScript-Inspired Syntax That Compiles to Readable Go</title>
      <dc:creator>puffball1567</dc:creator>
      <pubDate>Fri, 04 Sep 2026 16:31:55 +0000</pubDate>
      <link>https://dev.to/puffball1567/introducing-kinmokusei-typescript-inspired-syntax-that-compiles-to-readable-go-29ph</link>
      <guid>https://dev.to/puffball1567/introducing-kinmokusei-typescript-inspired-syntax-that-compiles-to-readable-go-29ph</guid>
      <description>&lt;p&gt;On August 31, 2026, I released &lt;a href="https://github.com/puffball1567/kinmokusei/releases/tag/v0.1.0" rel="noopener noreferrer"&gt;Kinmokusei v0.1.0&lt;/a&gt;, the first public preview of a programming language for writing web backends and Go libraries with TypeScript-inspired syntax and compiling them to readable Go source.&lt;/p&gt;

&lt;p&gt;The compiler command is named &lt;code&gt;keika&lt;/code&gt;, source files use the &lt;code&gt;.km&lt;/code&gt; extension, and the generated program remains part of the ordinary Go ecosystem: it uses Go modules, the Go toolchain, the Go ABI, the Go runtime, and existing Go packages directly. Go—not TypeScript—is the compatibility target.&lt;/p&gt;

&lt;p&gt;Repository: &lt;a href="https://github.com/puffball1567/kinmokusei" rel="noopener noreferrer"&gt;https://github.com/puffball1567/kinmokusei&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What Kinmokusei is
&lt;/h2&gt;

&lt;p&gt;Kinmokusei is a source language for the Go ecosystem. It resembles TypeScript, but it is not TypeScript. It is intended for developers who like TypeScript-shaped syntax but want to build and publish ordinary Go programs and libraries. Its syntax is the approachable frontend; Go is the platform underneath and the main compatibility boundary.&lt;/p&gt;

&lt;p&gt;It is not a TypeScript-to-Go converter, transpiler, or TypeScript implementation. It does not accept &lt;code&gt;.ts&lt;/code&gt; files or existing TypeScript projects and turn them into Go. It is also not Go source with alternate punctuation. Kinmokusei is a separate language with its own &lt;code&gt;.km&lt;/code&gt; source, type system, and explicit constructs for behavior that must lower predictably to Go, including Go named types, pointers, multiple return values, interfaces, generics, channels, errors, and &lt;code&gt;nil&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The central design choice is that generated Go is not a hidden temporary representation. It is deterministic, formatted with &lt;code&gt;gofmt&lt;/code&gt;, readable by a Go developer, and suitable for inspection or publication.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.km source
    ↓ keika check / build / emit-go
readable Go source
    ↓ standard Go toolchain
native executable or Go library
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Authors can distribute the &lt;code&gt;.km&lt;/code&gt; source, the generated Go module, or both.&lt;/p&gt;

&lt;h2&gt;
  
  
  Go compatibility instead of npm compatibility
&lt;/h2&gt;

&lt;p&gt;Kinmokusei deliberately does not target npm, Node.js, browser JavaScript, or the TypeScript package ecosystem. An npm package cannot be imported merely because the source looks similar to TypeScript, and JavaScript runtime behavior is not reproduced behind the generated program.&lt;/p&gt;

&lt;p&gt;That is an intentional tradeoff rather than a missing transpiler feature. By giving up npm compatibility, the language can concentrate on Go compatibility: existing Go module graphs, exported package data, named types, pointers, structs, interfaces and method sets, multiple results, generics, channels, &lt;code&gt;error&lt;/code&gt;, goroutines, the Go ABI, and the standard Go build toolchain.&lt;/p&gt;

&lt;p&gt;The practical ecosystem escape hatch is therefore &lt;code&gt;import go&lt;/code&gt;, not an npm compatibility layer. Higher-level Kinmokusei libraries can be built on that boundary, while the low-level types and behavior remain recognizable to Go developers.&lt;/p&gt;

&lt;h2&gt;
  
  
  A first program
&lt;/h2&gt;

&lt;p&gt;The smallest program can import the real Go standard library through an explicit namespace:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;go&lt;/span&gt; &lt;span class="nx"&gt;fmt&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;fmt&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Hello from Kinmokusei&lt;/span&gt;&lt;span class="dl"&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;Save this as &lt;code&gt;hello.km&lt;/code&gt;, then check and run it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;keika check hello.km
keika run hello.km
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;keika emit-go hello.km&lt;/code&gt; prints the generated source:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// Code generated by Kinmokusei. DO NOT EDIT.&lt;/span&gt;
&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="n"&gt;fmt&lt;/span&gt; &lt;span class="s"&gt;"fmt"&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Hello from Kinmokusei"&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;There is no separate JavaScript runtime or compatibility layer in this path. The result is Go code that the normal Go toolchain can build.&lt;/p&gt;

&lt;h2&gt;
  
  
  TypeScript-inspired classes with Go underneath
&lt;/h2&gt;

&lt;p&gt;Kinmokusei provides reference-type classes, interfaces, visibility, constructors, and explicit single inheritance. These features lower to Go structs, methods, embedding, and interfaces instead of introducing a second object runtime.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;CounterValue&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./counter-contract&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Counter&lt;/span&gt; &lt;span class="k"&gt;implements&lt;/span&gt; &lt;span class="nx"&gt;CounterValue&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;increment&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;current&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nx"&gt;int&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&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;Classes are reference types, while native &lt;code&gt;struct&lt;/code&gt; declarations provide nominal Go-style value types with copy semantics. The language also includes interfaces, generic functions and types, fixed arrays and slices, maps, enums, defined types, nullable references, explicit &lt;code&gt;Result&amp;lt;T&amp;gt;&lt;/code&gt; propagation, exceptions isolated from ordinary Go panics, channels, &lt;code&gt;select&lt;/code&gt;, goroutines, and structured &lt;code&gt;Task&amp;lt;T&amp;gt;&lt;/code&gt; values.&lt;/p&gt;

&lt;p&gt;The aim is not to imitate every TypeScript or JavaScript behavior. The syntax should make the source approachable while preserving the distinctions that matter to generated Go, such as value versus reference semantics, copying versus aliasing, and a checked language &lt;code&gt;null&lt;/code&gt; versus raw Go &lt;code&gt;nil&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use Go packages directly
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;import go&lt;/code&gt; form is the low-level bridge to the Go ecosystem. It works with both the standard library and external modules:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;go&lt;/span&gt; &lt;span class="nx"&gt;gin&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;github.com/gin-gonic/gin&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;go&lt;/span&gt; &lt;span class="nx"&gt;http&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;net/http&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;NewRouter&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;gin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Engine&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;router&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;gin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;New&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="nx"&gt;router&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;GET&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/api/health&lt;/span&gt;&lt;span class="dl"&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;ctx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;gin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;StatusOK&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ok&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;language&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Kinmokusei&lt;/span&gt;&lt;span class="dl"&gt;"&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;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;router&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;The compiler reads exported Go package data and preserves Go types rather than flattening everything into a smaller foreign-function interface. Functions, constants, variables, structs, fields, methods, interfaces, pointers, multiple results, generics, callbacks, and explicit conversions are checked at the &lt;code&gt;.km&lt;/code&gt; source location.&lt;/p&gt;

&lt;p&gt;Kinmokusei can use an existing &lt;code&gt;go.mod&lt;/code&gt;, or it can manage a locked module graph through its project manifest and lockfile. Normal checking and building validate that graph without silently updating dependencies.&lt;/p&gt;

&lt;h2&gt;
  
  
  Web backends are the first application target
&lt;/h2&gt;

&lt;p&gt;The initial release focuses on web backends, Go library production, and goroutine-based workloads. The repository contains a JSON API built directly on &lt;code&gt;net/http&lt;/code&gt;, an embedded HTTP package, and a full-stack example with one React and TypeScript frontend connected to interchangeable Gin and Fiber backends written in Kinmokusei.&lt;/p&gt;

&lt;p&gt;Both framework examples call the real upstream Go packages. They compile into ordinary Go services and are tested against independently handwritten Go implementations of the same HTTP contract.&lt;/p&gt;

&lt;p&gt;Kinmokusei also provides checked C boundaries for cases where Go is not the final consumer. Explicitly exported functions can produce a versioned C ABI, while incoming C FFI generation supports checked scalar, string, byte, array, struct, tagged-union, callback, error, handle, and ownership contracts.&lt;/p&gt;

&lt;h2&gt;
  
  
  The generated program must remain trustworthy
&lt;/h2&gt;

&lt;p&gt;A source-to-source language can look convincing while small semantic differences accumulate underneath. Kinmokusei therefore keeps a registry of Go-equivalent runtime contracts. In v0.1.0, all 75 registered contracts have isolated differential tests against independently handwritten Go programs.&lt;/p&gt;

&lt;p&gt;Generated programs are also compiled and tested across supported Go toolchains and target platforms. The release supports Go 1.23 through Go 1.27, and the published compiler is built with Go 1.27 so it can read export data produced by each supported toolchain.&lt;/p&gt;

&lt;p&gt;This does not mean that Kinmokusei already covers the entire Go language or ecosystem. It means that accepted behavior is expected to be explicit, testable, and predictable instead of being passed through silently.&lt;/p&gt;

&lt;h2&gt;
  
  
  Compiler and editor support ship together
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;keika&lt;/code&gt; distribution includes the compiler and &lt;code&gt;keika lsp --stdio&lt;/code&gt;. The official Visual Studio Code extension is a thin client for that same language server, so diagnostics and semantic behavior stay aligned with the compiler version.&lt;/p&gt;

&lt;p&gt;The current LSP provides diagnostics, hover, definition, references, rename, document symbols, completion, and signature help for Kinmokusei declarations and supported Go APIs. Matching VSIX files are attached to each release.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try the public preview
&lt;/h2&gt;

&lt;p&gt;Go 1.23 through Go 1.27 are supported. If Go is already installed, the tagged compiler can be installed with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;go &lt;span class="nb"&gt;install &lt;/span&gt;github.com/puffball1567/kinmokusei/cmd/keika@v0.1.0
keika version
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Prebuilt archives for Linux, macOS, and Windows, checksums, and the matching Visual Studio Code extension are available on the &lt;a href="https://github.com/puffball1567/kinmokusei/releases/tag/v0.1.0" rel="noopener noreferrer"&gt;v0.1.0 release page&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This is a pre-1.0 public preview. Source syntax and generated APIs may change between minor releases, and several areas remain intentionally incomplete. The current goal is to make the implemented boundary solid enough for real experiments, feedback, and incremental expansion.&lt;/p&gt;

&lt;p&gt;If TypeScript-inspired source that produces readable Go sounds useful for a backend or library project, I would be glad to hear what you try, where the generated interface feels natural, and where it does not.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/puffball1567/kinmokusei" rel="noopener noreferrer"&gt;Kinmokusei repository&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/puffball1567/kinmokusei/releases/tag/v0.1.0" rel="noopener noreferrer"&gt;v0.1.0 release&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/puffball1567/kinmokusei/blob/v0.1.0/docs/installation.md" rel="noopener noreferrer"&gt;Installation guide&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/puffball1567/kinmokusei/blob/v0.1.0/docs/language-design.md" rel="noopener noreferrer"&gt;Language design&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/puffball1567/kinmokusei/tree/v0.1.0/examples/react-web-frameworks" rel="noopener noreferrer"&gt;React with Gin and Fiber example&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>go</category>
      <category>typescript</category>
      <category>webdev</category>
      <category>programming</category>
    </item>
  </channel>
</rss>
