<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
    <title>Erdinç Yaşan</title>
    <subtitle>Personal website</subtitle>
    <link rel="self" type="application/atom+xml" href="https://erdincyasan.com/en/atom.xml"/>
    <link rel="alternate" type="text/html" href="https://erdincyasan.com"/>
    <generator uri="https://www.getzola.org/">Zola</generator>
    <updated>2026-08-14T00:00:00+00:00</updated>
    <id>https://erdincyasan.com/en/atom.xml</id>
    <entry xml:lang="en">
        <title>Storing credentials in the repo: SOPS + age</title>
        <published>2026-08-14T00:00:00+00:00</published>
        <updated>2026-08-14T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://erdincyasan.com/en/blog/sops-age-ile-secret-saklamak/"/>
        <id>https://erdincyasan.com/en/blog/sops-age-ile-secret-saklamak/</id>
        
        <content type="html" xml:base="https://erdincyasan.com/en/blog/sops-age-ile-secret-saklamak/">&lt;p&gt;On one of my projects there was a question I kept hitting during deployment:
where should I keep the secret values? At first I stored them as GitHub secrets.
It worked fine, but over time reading them back became a hassle.&lt;&#x2F;p&gt;
&lt;p&gt;My first approach was to put the whole &lt;code&gt;.env&lt;&#x2F;code&gt; file into a single GitHub secret.
It was easy, but this time I couldn&#x27;t see which secret was actually set:
everything was one block. If instead I made each value a separate secret, then as
the number of secrets grew I had to set dozens of values one by one, a management
headache of its own.&lt;&#x2F;p&gt;
&lt;p&gt;I could also have kept a secret file directly on the server. But then I&#x27;d have to
connect to the server for every change. I clarified what I wanted: to change a
secret and deploy it automatically, without touching the CI process at all.&lt;&#x2F;p&gt;
&lt;p&gt;After some research I came across a method the Mozilla team put out: SOPS.
Combined with age, I could keep the secrets encrypted inside the repo itself.
Wherever the code is, the secret is right there too, but not as plaintext. In
this post I explain how I did it, where it works well, and where you need to be
careful.&lt;&#x2F;p&gt;
&lt;p&gt;All the examples are in a working demo repo:
https:&#x2F;&#x2F;github.com&#x2F;erdyasan&#x2F;sops-age-example&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-idea&quot;&gt;The idea&lt;&#x2F;h2&gt;
&lt;p&gt;There are two pieces:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;age&lt;&#x2F;strong&gt;: a simple, modern encryption tool. We generate a public&#x2F;private key
pair. What is encrypted with the public key can only be opened with the private
key.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;SOPS&lt;&#x2F;strong&gt; (Secrets OPerationS): it encrypts &lt;strong&gt;only the values&lt;&#x2F;strong&gt; of a
YAML&#x2F;JSON&#x2F;env file, leaving the keys (the field names) in the clear. So the
file still stays in a readable shape, but the values are encrypted. When we take
a diff we can see &quot;which field changed&quot;, but not the value itself.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;age works with asymmetric keys: we generate a public&#x2F;private key pair. We encrypt
the data with the &lt;strong&gt;public key&lt;&#x2F;strong&gt;, and that data can only be opened with the
&lt;strong&gt;matching private key&lt;&#x2F;strong&gt;, no other key. That&#x27;s where the asymmetry is: we can
hand the public key to everyone, anyone can prepare encrypted data for us with it,
but the only party who can open it is the owner of the private key. So being able
to encrypt is a capability open to everyone, while decrypting is in a single hand.
(GPG basically works with the same asymmetric logic: encrypt with public, open
with private.)&lt;&#x2F;p&gt;
&lt;p&gt;age&#x27;s difference from GPG is in weight. GPG (GNU Privacy Guard) is powerful but
heavy: a trust model called the web-of-trust, keyring management, keyservers, key
expiry dates, subkeys, agent processes running in the background, dozens of
subcommands. There&#x27;s quite a bit of concept to learn before you even encrypt a
file. age is focused on a single job: no config file, short keys, one-line
commands. For everyday secret encryption you rarely need all of GPG&#x27;s machinery;
age does the same job far more plainly.&lt;&#x2F;p&gt;
&lt;p&gt;Putting these together, we get this: the secret file can be committed to git.
Because the values inside it are encrypted with age. Someone who clones it without
the private key sees only a useless piece of ciphertext. Who changed what and when
stays in the git history. On a new machine everything comes with a single
&lt;code&gt;git clone&lt;&#x2F;code&gt; (plus the age key).&lt;&#x2F;p&gt;
&lt;p&gt;Let me say this upfront: this approach is a perfect fit for small and mid-sized
projects. But it has a limit. Because the public keys are out in the open in
&lt;code&gt;.sops.yaml&lt;&#x2F;code&gt;, anyone with write access to the repo, even if they can&#x27;t read the
values of the secrets, can write new values over them (I explain this below in
the &quot;Being able to encrypt is not being able to read&quot; section). In a small and
trusted team this is an acceptable risk. As the team and the blast radius grow,
you need extra protections on the git side (branch protection, mandatory review,
rules specific to the secret path) or, past a certain point, a centralized secret
manager.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;setup&quot;&gt;Setup&lt;&#x2F;h2&gt;
&lt;p&gt;On macOS both are in brew:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;brew&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; install&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; sops&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; age&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h2 id=&quot;1-generate-an-age-key&quot;&gt;1. Generate an age key&lt;&#x2F;h2&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;age-keygen&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;o&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; key.txt&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Its output is a file like this:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;# created: 2026-08-12T12:02:42+03:00&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;# public key: age1cpyuq5ss7w85phtrkhsxntek7k4ypx3gr8qa8c4vd5y0k772wqssrd4w4q&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;AGE-SECRET-KEY-1DC8QTSCYAXRADPLAQA853TMCYDYZXSJ9JW8MFWRYLZFM6J4E67CS3K6ZW5&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The top &lt;code&gt;age1...&lt;&#x2F;code&gt; line is the &lt;strong&gt;public key&lt;&#x2F;strong&gt; (recipient). The bottom
&lt;code&gt;AGE-SECRET-KEY-...&lt;&#x2F;code&gt; is the &lt;strong&gt;private key&lt;&#x2F;strong&gt;. We encrypt with the public key, we
open with the private key. The private key stays secret, and we can share the
public key freely. In practice the private key lives on each machine under
&lt;code&gt;~&#x2F;.config&#x2F;sops&#x2F;age&#x2F;keys.txt&lt;&#x2F;code&gt; and is never committed.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;2-file-layout-secrets-and-env-enc&quot;&gt;2. File layout: secrets&#x2F; and .env.enc&lt;&#x2F;h2&gt;
&lt;p&gt;In my own setup I keep the secrets in a &lt;code&gt;secrets&#x2F;&lt;&#x2F;code&gt; directory, separated per
environment. There are three files per environment:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;secrets&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  app.local.env.enc         # encrypted, committed&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  app.local.env.example     # keys only, committed (so which secrets exist is clear)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  app.local.env             # plaintext working file, GITIGNORE (local, decrypted copy)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The logic is this: &lt;code&gt;.enc&lt;&#x2F;code&gt; is the encrypted, committed file; &lt;code&gt;.example&lt;&#x2F;code&gt; is the
template that carries only the key names, which a newcomer looks at to see &quot;which
values do I need to fill in&quot;; and the plain &lt;code&gt;.env&lt;&#x2F;code&gt; is the decrypted copy created
while editing, which stays in &lt;code&gt;.gitignore&lt;&#x2F;code&gt; and is never committed.&lt;&#x2F;p&gt;
&lt;p&gt;Another benefit of the &lt;code&gt;.enc&lt;&#x2F;code&gt; extension is on the CI side: the pipeline can find
all the encrypted files at once with a pattern like &lt;code&gt;secrets&#x2F;*.env.enc&lt;&#x2F;code&gt; and
decrypt them in bulk. The &lt;code&gt;*&lt;&#x2F;code&gt; here is a wildcard character; it means &quot;bring me all
the file names that match this pattern&quot;. Matching file names by a pattern like
this is called globbing (everything in &lt;code&gt;secrets&#x2F;&lt;&#x2F;code&gt; that ends with &lt;code&gt;.env.enc&lt;&#x2F;code&gt;). The
extension is also a signal that says &quot;this file is encrypted, it will be decrypted
at deploy&quot;.&lt;&#x2F;p&gt;
&lt;p&gt;One point: the &lt;code&gt;.enc&lt;&#x2F;code&gt; extension doesn&#x27;t tell SOPS the format. The files are in
dotenv (&lt;code&gt;KEY=value&lt;&#x2F;code&gt;) format, but since SOPS can&#x27;t infer that from the extension,
you have to pass the format by hand on every command: &lt;code&gt;--input-type dotenv --output-type dotenv&lt;&#x2F;code&gt;. The little manager below always adds this automatically.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;3-who-it-s-encrypted-for-sops-yaml&quot;&gt;3. Who it&#x27;s encrypted for: .sops.yaml&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;code&gt;.sops.yaml&lt;&#x2F;code&gt; tells SOPS which file to encrypt for whom. We define different
recipients per environment. First we write the keys once as anchors and reference
them below:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;yaml&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;#&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; .sops.yaml&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag z-yaml&quot;&gt;k&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag z-yaml&quot;&gt;eys&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-key-value&quot;&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;  -&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-anchor z-yaml&quot;&gt; &amp;amp;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type z-anchor z-yaml&quot;&gt;local&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;          a&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;ge1cpyuq5ss...&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;   #&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; my machine&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;  -&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-anchor z-yaml&quot;&gt; &amp;amp;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type z-anchor z-yaml&quot;&gt;server_staging&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; a&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;ge12clzmdhl...&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;   #&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; staging server&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;  -&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-anchor z-yaml&quot;&gt; &amp;amp;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type z-anchor z-yaml&quot;&gt;server_prod&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;    a&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;ge1g5uwzup8...&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;   #&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; prod server&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag z-yaml&quot;&gt;c&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag z-yaml&quot;&gt;reation_rules&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-key-value&quot;&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;  #&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; local: only my machine can open&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;  -&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag z-yaml&quot;&gt; p&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag z-yaml&quot;&gt;ath_regex&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-key-value&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; s&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;ecrets[&#x2F;\\]app\.local\.env(\.enc)?$&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag z-yaml&quot;&gt;    k&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag z-yaml&quot;&gt;ey_groups&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-key-value&quot;&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;      -&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag z-yaml&quot;&gt; a&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag z-yaml&quot;&gt;ge&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-key-value&quot;&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;          -&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-alias z-yaml&quot;&gt; *&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-alias z-yaml&quot;&gt;local&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;  #&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; staging: staging server + me&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;  -&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag z-yaml&quot;&gt; p&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag z-yaml&quot;&gt;ath_regex&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-key-value&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; s&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;ecrets[&#x2F;\\]app\.staging\.env(\.enc)?$&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag z-yaml&quot;&gt;    k&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag z-yaml&quot;&gt;ey_groups&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-key-value&quot;&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;      -&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag z-yaml&quot;&gt; a&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag z-yaml&quot;&gt;ge&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-key-value&quot;&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;          -&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-alias z-yaml&quot;&gt; *&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-alias z-yaml&quot;&gt;server_staging&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;          -&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-alias z-yaml&quot;&gt; *&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-alias z-yaml&quot;&gt;local&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;  #&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; prod: prod server + me&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;  -&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag z-yaml&quot;&gt; p&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag z-yaml&quot;&gt;ath_regex&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-key-value&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; s&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;ecrets[&#x2F;\\]app\.prod\.env(\.enc)?$&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag z-yaml&quot;&gt;    k&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag z-yaml&quot;&gt;ey_groups&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-key-value&quot;&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;      -&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag z-yaml&quot;&gt; a&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag z-yaml&quot;&gt;ge&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-key-value&quot;&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;          -&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-alias z-yaml&quot;&gt; *&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-alias z-yaml&quot;&gt;server_prod&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;          -&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-alias z-yaml&quot;&gt; *&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-alias z-yaml&quot;&gt;local&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The recipient model here matters: each environment is encrypted with &lt;strong&gt;that
environment&#x27;s server public key + my local public key&lt;&#x2F;strong&gt;. The result:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;The server opens it at deploy time with its own private key.&lt;&#x2F;li&gt;
&lt;li&gt;I open and edit it locally with my own private key.&lt;&#x2F;li&gt;
&lt;li&gt;Only the prod server (and me) can open the prod file; the staging server or
another developer can&#x27;t decrypt the prod secret, because their key isn&#x27;t in that
rule.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;All the &lt;code&gt;age1...&lt;&#x2F;code&gt; lines we write in &lt;code&gt;.sops.yaml&lt;&#x2F;code&gt; are &lt;strong&gt;public keys&lt;&#x2F;strong&gt;. No private
key goes in here.&lt;&#x2F;p&gt;
&lt;p&gt;For a while I set up sops+age a bit by rote; I didn&#x27;t fully know how this
structure works. Back then I hadn&#x27;t read the whole documentation, and honestly I
had no experience either. Later I realized that &lt;code&gt;key_groups&lt;&#x2F;code&gt; here is actually a
term that needs care: the moment we say &quot;encrypt for local too&quot; in the prod rule,
the prod secret becomes decryptable on local as well. When SOPS encrypts a value
it first generates a random data key, encrypts the actual data with it, then wraps
that data key separately for each recipient&#x27;s public key in the group. So the data
key in the prod file can be opened both with the prod server&#x27;s private key and
with my local private key; either one is enough. When I saw that I could decrypt
the prod secrets locally with &lt;code&gt;sops -d&lt;&#x2F;code&gt;, honestly I was quite surprised; my first
thought was &quot;is there a vulnerability here?&quot;. There isn&#x27;t: this is exactly the
result of my adding &lt;code&gt;&amp;amp;local&lt;&#x2F;code&gt; to every environment&#x27;s key group, the design itself.
The same holds for staging&#x2F;qa: as long as &lt;code&gt;&amp;amp;local&lt;&#x2F;code&gt; is a recipient in the rule, I
can open that environment locally.&lt;&#x2F;p&gt;
&lt;p&gt;But you need to see the trade-off it brings: someone who gets hold of the local
private key can decrypt the secrets of every environment (prod included) where
that key is a recipient. So it matters to protect the &lt;code&gt;keys.txt&lt;&#x2F;code&gt; file and to
choose deliberately who is a recipient for which environment. In a small and
trusted team this is an acceptable convenience. If we want to narrow access, we
take local out of the prod group and encrypt prod with only the server key; then
no one can open the prod secret locally.&lt;&#x2F;p&gt;
&lt;p&gt;(The &lt;code&gt;secrets[&#x2F;\\]&lt;&#x2F;code&gt; in &lt;code&gt;path_regex&lt;&#x2F;code&gt; covers both the &lt;code&gt;&#x2F;&lt;&#x2F;code&gt; and &lt;code&gt;\&lt;&#x2F;code&gt; separator: a
Windows portability detail, which I touch on in the encoding section below.)&lt;&#x2F;p&gt;
&lt;p&gt;A terminology note too: &lt;code&gt;key_groups&lt;&#x2F;code&gt; here is just a container that wraps
recipients within a single group. For environment separation, a single group and
the recipient list we write into it are enough.&lt;&#x2F;p&gt;
&lt;p&gt;Full file:
https:&#x2F;&#x2F;github.com&#x2F;erdyasan&#x2F;sops-age-example&#x2F;blob&#x2F;811a70873d040b2be83f68f5cfaaf972cef7deeb&#x2F;.sops.yaml&lt;&#x2F;p&gt;
&lt;h2 id=&quot;4-write-and-encrypt-the-secret&quot;&gt;4. Write and encrypt the secret&lt;&#x2F;h2&gt;
&lt;p&gt;First we prepare the plain working file (dotenv):&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;# secrets&#x2F;app.local.env  (gitignored, plaintext)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;HELLO=world&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;FAKE_API_KEY=demo_local_sk_0000&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;DB_PASSWORD=local-demo-password&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Then we encrypt it (passing the dotenv format by hand):&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;sops&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;e&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;-input-type&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; dotenv&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;-output-type&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; dotenv&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; secrets&#x2F;app.local.env&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; &amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; secrets&#x2F;app.local.env.enc&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The &lt;code&gt;.enc&lt;&#x2F;code&gt; file now looks like this:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;HELLO=ENC[AES256_GCM,data:wPU91TF7...,iv:bbS2qHGd...,tag:ALssHW9H...,type:str]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;FAKE_API_KEY=ENC[AES256_GCM,data:...,type:str]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;DB_PASSWORD=ENC[AES256_GCM,data:...,type:str]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;sops_age__list_0__map_recipient=age1cpyuq5ss...&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;sops_mac=ENC[AES256_GCM,...]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;sops_version=3.13.2&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The field names (&lt;code&gt;HELLO&lt;&#x2F;code&gt;, &lt;code&gt;FAKE_API_KEY&lt;&#x2F;code&gt;, &lt;code&gt;DB_PASSWORD&lt;&#x2F;code&gt;) are in the clear, their
values encrypted. That&#x27;s exactly why the diffs stay meaningful. The &lt;code&gt;sops_...&lt;&#x2F;code&gt;
lines at the bottom are SOPS&#x27;s metadata: which recipient it was encrypted for and
the integrity check (&lt;code&gt;mac&lt;&#x2F;code&gt;). If we used YAML this would be a &lt;code&gt;sops:&lt;&#x2F;code&gt; block; since
dotenv holds plain key=value, the metadata is also spread across plain keys as
&lt;code&gt;sops_...&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;We can commit this &lt;code&gt;.enc&lt;&#x2F;code&gt; file without worry. The plain &lt;code&gt;app.local.env&lt;&#x2F;code&gt;, on the
other hand, git doesn&#x27;t see.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;5-decrypting-at-runtime&quot;&gt;5. Decrypting at runtime&lt;&#x2F;h2&gt;
&lt;p&gt;The encrypted file is nice, but how will the application read it? The cleanest way
is to never write the plaintext to disk at all: decrypt the file in memory and
inject the values into the process as environment variables.&lt;&#x2F;p&gt;
&lt;p&gt;For this I put a small manager in the repo (&lt;code&gt;secrets.mjs&lt;&#x2F;code&gt;), a simplified version
of the more thorough one in my real project. It doesn&#x27;t roll its own crypto, it
just calls the &lt;code&gt;sops&lt;&#x2F;code&gt; CLI. The &lt;code&gt;run&lt;&#x2F;code&gt; command decrypts the environment and runs the
given command with those variables:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;node&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; secrets.mjs&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; run&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; local&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;-&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; node&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; index.js&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The application side just reads &lt;code&gt;process.env&lt;&#x2F;code&gt;, it doesn&#x27;t even know about SOPS:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; index.js&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;const&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-constant z-js&quot;&gt; KEYS&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-source&quot;&gt; [&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;HELLO&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;FAKE_API_KEY&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;DB_PASSWORD&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-source&quot;&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-source&quot;&gt;console&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;log&lt;&#x2F;span&gt;&lt;span class=&quot;z-source&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;Secrets decrypted by SOPS, read from process.env:&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-character z-escape&quot;&gt;\n&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-source&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;for&lt;&#x2F;span&gt;&lt;span class=&quot;z-source&quot;&gt; (&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;const&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-constant z-js&quot;&gt; key&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-expression z-of z-js&quot;&gt; of&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-constant z-js&quot;&gt; KEYS&lt;&#x2F;span&gt;&lt;span class=&quot;z-source&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-source&quot;&gt;  console&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;log&lt;&#x2F;span&gt;&lt;span class=&quot;z-source&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;`&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;  ${&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-readwrite&quot;&gt;key&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;}&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; = &lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;${&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;process&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-property z-js&quot;&gt;env&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-readwrite&quot;&gt;key&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; ??&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;(missing)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;}&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;`&lt;&#x2F;span&gt;&lt;span class=&quot;z-source&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Output:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;$ npm start&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Secrets decrypted by SOPS, read from process.env:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  HELLO = world (local)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  FAKE_API_KEY = demo_local_sk_0000_not_a_real_key&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  DB_PASSWORD = local-demo-password&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Why my own little script instead of &lt;code&gt;sops exec-env&lt;&#x2F;code&gt;? Because &lt;code&gt;exec-env&lt;&#x2F;code&gt; doesn&#x27;t
accept the &lt;code&gt;--input-type&lt;&#x2F;code&gt; flag; since it can&#x27;t infer the format from the
extension, it doesn&#x27;t work with extensionless&#x2F;&lt;code&gt;.enc&lt;&#x2F;code&gt; dotenv files. On the Node
side, decrypting with &lt;code&gt;sops -d --input-type dotenv ...&lt;&#x2F;code&gt; and parsing the output
both solves this problem and keeps the shell from mangling values that contain
spaces (like &lt;code&gt;world (local)&lt;&#x2F;code&gt;). The decrypted content is never written to disk, it
only stays in memory as long as the process is alive.&lt;&#x2F;p&gt;
&lt;p&gt;The full manager:
https:&#x2F;&#x2F;github.com&#x2F;erdyasan&#x2F;sops-age-example&#x2F;blob&#x2F;811a70873d040b2be83f68f5cfaaf972cef7deeb&#x2F;secrets.mjs&lt;&#x2F;p&gt;
&lt;h2 id=&quot;team-not-sharing-a-key-but-adding-a-recipient&quot;&gt;Team: not sharing a key, but adding a recipient&lt;&#x2F;h2&gt;
&lt;p&gt;At the start we generated a single key pair, because we were a single person. So
what happens when it becomes a team, do we hand everyone the same private key? No.
The trick is exactly here.&lt;&#x2F;p&gt;
&lt;p&gt;Each person on the team runs &lt;code&gt;age-keygen&lt;&#x2F;code&gt; on their own machine and generates a
pair of their own: their own private key (secret, never leaves their machine) and
their own public key (shareable). So there isn&#x27;t a single public key out there;
every member has a different pair. All the &lt;code&gt;age1...&lt;&#x2F;code&gt; lines in &lt;code&gt;.sops.yaml&lt;&#x2F;code&gt; are
public keys.&lt;&#x2F;p&gt;
&lt;p&gt;Here&#x27;s how SOPS works when it encrypts a file: it generates a random data key,
encrypts the actual values with it, then wraps this data key separately with each
recipient&#x27;s public key in that rule. So anyone (or any server) in the list can
open the data key with their own private key and decrypt the file. Since everyone
has a separate private key, no one needs to know anyone else&#x27;s key.&lt;&#x2F;p&gt;
&lt;p&gt;Why this way? Because if we handed a single private key to everyone: when one of
them leaked it, they&#x27;d all burn at once, you couldn&#x27;t tell who opened what, and to
change the key we&#x27;d have to redistribute it to everyone. When they&#x27;re separate,
only the public keys are shared, and the private keys never leave the machine.&lt;&#x2F;p&gt;
&lt;p&gt;When someone new joins, we add their public key to the relevant rule, then, to
wrap the file for them too:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;sops&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; updatekeys&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; secrets&#x2F;app.local.env.enc&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;When someone leaves: we remove their public key from &lt;code&gt;.sops.yaml&lt;&#x2F;code&gt;, run &lt;code&gt;sops updatekeys&lt;&#x2F;code&gt;, and also rotate the actual secret values. (The person who left can
still open the encrypted file in an old commit, because that version stays in the
history. That&#x27;s why changing the value is a must.)&lt;&#x2F;p&gt;
&lt;h2 id=&quot;ci-github-actions&quot;&gt;CI (GitHub Actions)&lt;&#x2F;h2&gt;
&lt;p&gt;In the CI environment we provide that environment&#x27;s private key as a repo secret:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;yaml&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;-&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag z-yaml&quot;&gt; r&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag z-yaml&quot;&gt;un&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-key-value&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; n&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;ode secrets.mjs run prod -- node index.js&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag z-yaml&quot;&gt;  e&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag z-yaml&quot;&gt;nv&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-key-value&quot;&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag z-yaml&quot;&gt;    S&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag z-yaml&quot;&gt;OPS_AGE_KEY&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-key-value&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; $&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;{{ secrets.PROD_AGE_KEY }}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;code&gt;SOPS_AGE_KEY&lt;&#x2F;code&gt; takes the key&#x27;s content directly; &lt;code&gt;SOPS_AGE_KEY_FILE&lt;&#x2F;code&gt; instead wants
a file path. That&#x27;s exactly what I wanted: without touching CI at all, I update a
secret by committing the encrypted file, and since the prod key lives only in the
prod CI&#x27;s secret, environment isolation isn&#x27;t broken.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;being-able-to-encrypt-is-not-being-able-to-read-repo-access&quot;&gt;Being able to encrypt is not being able to read: repo access&lt;&#x2F;h2&gt;
&lt;p&gt;At some point I realized this: the public keys are out in the open in
&lt;code&gt;.sops.yaml&lt;&#x2F;code&gt;. So can someone with write access to the repo change the secrets?
Yes, they can. Encrypting needs only the public key, no private key. So that
person &lt;strong&gt;can&#x27;t read&lt;&#x2F;strong&gt; the existing values (reading requires the private key), but
they can write new values over them, re-encrypt, and commit. Since at deploy the
server decrypts and uses the committed value, someone with write access can set
&lt;code&gt;DB_PASSWORD&lt;&#x2F;code&gt; to a different value or point an API address to the wrong place.&lt;&#x2F;p&gt;
&lt;p&gt;So SOPS gives us &lt;strong&gt;confidentiality&lt;&#x2F;strong&gt;, not &lt;strong&gt;integrity&lt;&#x2F;strong&gt;. It protects against
reading the values, not against changing them. In a small project this is usually
enough; as scale grows you need to close this gap on the git side. If you decide
to use sops in a team, my recommendation is these few measures:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Branch protection + mandatory review.&lt;&#x2F;strong&gt; Close off direct push to &lt;code&gt;main&lt;&#x2F;code&gt;, take
every change via PR + approval. That way no one can write over a secret and send
it to deploy on their own.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Reviewer specific to the secret path (CODEOWNERS).&lt;&#x2F;strong&gt; Make certain people&#x27;s
approval mandatory on every PR that touches &lt;code&gt;secrets&#x2F;&lt;&#x2F;code&gt; and &lt;code&gt;.sops.yaml&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Automatic warning on the PR.&lt;&#x2F;strong&gt; Drop an automatic warning comment on a PR when
it opens touching the secret files. That way the change doesn&#x27;t slip past the
reviewer&#x27;s eye easily. If we want, we can tie this to a required status check to
block merges without approval, and go further with commit signing.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Note: the &lt;code&gt;mac&lt;&#x2F;code&gt; field in SOPS catches manual (non-sops) tampering with the file,
but someone who properly re-encrypts with sops produces a valid &lt;code&gt;mac&lt;&#x2F;code&gt;. So &lt;code&gt;mac&lt;&#x2F;code&gt;
prevents a blind hex edit, not a re-encryption by an authorized person.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;something-to-watch-out-for-metadata-leaks&quot;&gt;Something to watch out for: metadata leaks&lt;&#x2F;h2&gt;
&lt;p&gt;SOPS encrypts &lt;strong&gt;only the values&lt;&#x2F;strong&gt;, &lt;strong&gt;the field names stay in the clear&lt;&#x2F;strong&gt;. It&#x27;s
this way so that diffs stay readable, but there&#x27;s a cost: someone looking at the
file sees which secrets are being talked about. &lt;code&gt;DB_PASSWORD&lt;&#x2F;code&gt;, &lt;code&gt;STRIPE_SECRET_KEY&lt;&#x2F;code&gt;,
&lt;code&gt;ADMIN_TOKEN&lt;&#x2F;code&gt;... the values are secret but the names give away the shape of the
infrastructure. If the field name itself is sensitive, you either encrypt the
whole file or neutralize the name.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-line-ending-crlf-lf-and-encoding-trap&quot;&gt;The line ending (CRLF&#x2F;LF) and encoding trap&lt;&#x2F;h2&gt;
&lt;p&gt;I struggled quite a bit with this on the deploy side, this was the most
infuriating part. I created the &lt;code&gt;.enc&lt;&#x2F;code&gt; file on one machine (mac, LF line endings),
then opened the same repo on Windows. The values were correct but decryption just
wouldn&#x27;t happen. The reason: SOPS computes a &lt;code&gt;mac&lt;&#x2F;code&gt; (integrity check) over the
content for each file. When the bytes change even a little, the mac doesn&#x27;t match
and the file is considered corrupted. On Windows there are a few separate things
that change these bytes.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;1. Git autocrlf.&lt;&#x2F;strong&gt; On Windows, git can convert LFs to CRLF during checkout. The
&lt;code&gt;.enc&lt;&#x2F;code&gt;&#x27;s bytes change, the mac blows up. The fix is to turn off the conversion for
these files with &lt;code&gt;.gitattributes&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;# .gitattributes&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;secrets&#x2F;*.enc -text&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;code&gt;-text&lt;&#x2F;code&gt; tells git &quot;don&#x27;t treat these files as text, don&#x27;t touch the line
endings&quot;.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;2. PowerShell &lt;code&gt;&amp;gt;&lt;&#x2F;code&gt; redirection writes UTF-16.&lt;&#x2F;strong&gt; This one took time to find. On
Windows, when you do &lt;code&gt;sops -e ... &amp;gt; file.enc&lt;&#x2F;code&gt;, PowerShell&#x27;s &lt;code&gt;&amp;gt;&lt;&#x2F;code&gt; operator writes the
output as &lt;strong&gt;UTF-16 LE&lt;&#x2F;strong&gt;, not UTF-8, and adds a BOM on top. The file is born
corrupted from the start. A BOM inserted by editors gives the same result.&lt;&#x2F;p&gt;
&lt;p&gt;That&#x27;s why I wrote a small PowerShell script for Windows (&lt;code&gt;encrypt.ps1&lt;&#x2F;code&gt;). Roughly:
it first normalizes the plaintext to LF-only + UTF-8 no BOM, then takes the sops
output into a variable rather than via &lt;code&gt;&amp;gt;&lt;&#x2F;code&gt; and writes it with the .NET File API
(again LF + UTF-8 no BOM, a single trailing newline), and finally looks at the
file&#x27;s first bytes and throws if there&#x27;s a UTF-16&#x2F;UTF-8 BOM or CRLF. So it makes
producing a corrupted &lt;code&gt;.enc&lt;&#x2F;code&gt; impossible from the start. All of it is in the repo:
https:&#x2F;&#x2F;github.com&#x2F;erdyasan&#x2F;sops-age-example&#x2F;blob&#x2F;811a70873d040b2be83f68f5cfaaf972cef7deeb&#x2F;encrypt.ps1&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;3. Path separator.&lt;&#x2F;strong&gt; A small but annoying other Windows difference is the
&lt;code&gt;path_regex&lt;&#x2F;code&gt; in &lt;code&gt;.sops.yaml&lt;&#x2F;code&gt;. sops passes the file path to the regex as the OS
gives it; on Windows the separator is &lt;code&gt;\&lt;&#x2F;code&gt;, so a pattern written only with &lt;code&gt;&#x2F;&lt;&#x2F;code&gt;
doesn&#x27;t match and the file&#x27;s &quot;which rule it belongs to&quot; can&#x27;t be found. The reason
I wrote &lt;code&gt;path_regex&lt;&#x2F;code&gt; as &lt;code&gt;secrets[&#x2F;\\]...&lt;&#x2F;code&gt; in the &lt;code&gt;.sops.yaml&lt;&#x2F;code&gt; example above is
this: the bracket covers both separators.&lt;&#x2F;p&gt;
&lt;p&gt;In short: the bytes of the &lt;code&gt;.enc&lt;&#x2F;code&gt; files must be preserved exactly. Line endings,
encoding, BOM, anything that gets in between breaks the mac. You shouldn&#x27;t open and
save the &lt;code&gt;.enc&lt;&#x2F;code&gt; by hand in an editor; we always edit through the tools (sops,
secrets.mjs, encrypt.ps1). The encoding of the files matters.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;when-sops-when-the-alternatives&quot;&gt;When SOPS, when the alternatives&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;git-crypt&lt;&#x2F;strong&gt;: encrypts the whole file, works transparently (opens on
checkout). But since it&#x27;s file-based rather than value-based, the diffs are
unreadable. An advantage if we don&#x27;t want field-name leakage.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Vault &#x2F; cloud secret manager (Azure Key Vault, AWS Secrets Manager)&lt;&#x2F;strong&gt;:
centralized, audit-logged, heavy solutions that generate dynamic secrets. The
right place if you have a large team and strict compliance requirements. But
standing them up and operating them is costly.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;SOPS + age&lt;&#x2F;strong&gt;: the sweet spot for small&#x2F;mid projects. No extra service, the
secret is versioned in the same place as the code, setup is a handful of
commands. It also fits GitOps flows well.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;One might ask, &quot;We already have a private repo, isn&#x27;t encrypting on top of it
unnecessary?&quot; I don&#x27;t think so: defense in depth. If the repo accidentally goes
public, if a fork leaks, or if someone&#x27;s GitHub token is stolen, a plain &lt;code&gt;.env&lt;&#x2F;code&gt; is
compromised instantly; the encrypted file is still useless without the private
key.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;&#x2F;h2&gt;
&lt;p&gt;Keeping secrets encrypted in the repo largely removes the &quot;store the secret
somewhere separate&quot; hassle. With SOPS + age the setup is a few commands, and daily
use is almost invisible. On small projects it provides a reasonable level of
security and a comfortable workflow without needing Vault.&lt;&#x2F;p&gt;
&lt;p&gt;The full working demo, with all its files:
https:&#x2F;&#x2F;github.com&#x2F;erdyasan&#x2F;sops-age-example&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Free Custom Domain Email with Cloudflare and Gmail</title>
        <published>2026-08-10T00:00:00+00:00</published>
        <updated>2026-08-10T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://erdincyasan.com/en/blog/cloudflare-gmail-custom-domain-email/"/>
        <id>https://erdincyasan.com/en/blog/cloudflare-gmail-custom-domain-email/</id>
        
        <content type="html" xml:base="https://erdincyasan.com/en/blog/cloudflare-gmail-custom-domain-email/">&lt;p&gt;Sending email from a custom domain looks more professional. Most hosting
companies even offer it as a plus feature. But setting up and dealing with your
own email server is genuinely a hassle. It drags in SPF records, domain
verification, even reverse DNS. There are MX records too. So if you want your
mail to be delivered properly, there is a whole pile of settings to wrestle with.&lt;&#x2F;p&gt;
&lt;p&gt;Most companies handle all of this for you automatically, of course. But the
moment you want a custom email service, these challenges show up.&lt;&#x2F;p&gt;
&lt;p&gt;Anyway. Recently I decided to keep my website static, so I have no admin panel.
In short, there is no hosting or VPS running in the background, which means I have
no server cost. When I looked into how to handle email sending for the sake of a
professional identity&#x2F;image, I saw that Gmail lets you send mail as another
person. I was already using mail forwarding on Cloudflare: for example, if you
send a mail to &lt;code&gt;iam@erdincyasan.com&lt;&#x2F;code&gt;, it lands in my personal account,
&lt;code&gt;erdincyasan@gmail.com&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Now let&#x27;s go through how I set all this up.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;prerequisites&quot;&gt;Prerequisites&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;Your domain&#x27;s DNS must be managed on Cloudflare (the nameservers point to
Cloudflare).&lt;&#x2F;li&gt;
&lt;li&gt;A Gmail account.&lt;&#x2F;li&gt;
&lt;li&gt;Two-step verification (2FA) enabled on the Gmail account. This is required to
generate an app password, which I&#x27;ll get to.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;step-1-receiving-mail-with-cloudflare-email-routing&quot;&gt;Step 1: Receiving mail with Cloudflare Email Routing&lt;&#x2F;h2&gt;
&lt;p&gt;In the Cloudflare dashboard, select your domain and go to &lt;strong&gt;Email &amp;gt; Email
Routing&lt;&#x2F;strong&gt; in the left menu. If you&#x27;re opening it for the first time, it walks you
through a setup flow.&lt;&#x2F;p&gt;
&lt;p&gt;You do two things:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Add a destination address.&lt;&#x2F;strong&gt; You tell it where the mail should land, that is
your own Gmail address (&lt;code&gt;erdincyasan@gmail.com&lt;&#x2F;code&gt;). Cloudflare sends a
verification mail there; you click the link and confirm the address. Forwarding
won&#x27;t work until you confirm.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Add a routing rule.&lt;&#x2F;strong&gt; For example, everything sent to &lt;code&gt;iam@erdincyasan.com&lt;&#x2F;code&gt;
should go to &lt;code&gt;erdincyasan@gmail.com&lt;&#x2F;code&gt;. You can write a rule per address, or turn
on &lt;strong&gt;catch-all&lt;&#x2F;strong&gt; (whatever comes to the domain all goes into a single inbox).
Catch-all is practical but it also collects spam, so keep that in mind.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;When you enable Email Routing, Cloudflare adds the required DNS records on your
behalf automatically. You don&#x27;t need to touch them by hand, but it&#x27;s good to know
what gets added:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Three &lt;strong&gt;MX&lt;&#x2F;strong&gt; records (like &lt;code&gt;route1.mx.cloudflare.net&lt;&#x2F;code&gt;, &lt;code&gt;route2...&lt;&#x2F;code&gt;,
&lt;code&gt;route3...&lt;&#x2F;code&gt;, with different priority values). These are the records that say
&quot;this domain&#x27;s mail should come here&quot;.&lt;&#x2F;li&gt;
&lt;li&gt;One &lt;strong&gt;SPF&lt;&#x2F;strong&gt; record (TXT): &lt;code&gt;v=spf1 include:_spf.mx.cloudflare.net ~all&lt;&#x2F;code&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;And a &lt;strong&gt;DKIM&lt;&#x2F;strong&gt; record (TXT, like &lt;code&gt;cf2024-1._domainkey...&lt;&#x2F;code&gt;). This is a signing
record for Cloudflare&#x27;s own mail path; it doesn&#x27;t help mail sent from Gmail,
which I&#x27;ll touch on in the &quot;Limits&quot; section below.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;That&#x27;s it. Now mail sent to &lt;code&gt;iam@erdincyasan.com&lt;&#x2F;code&gt; lands in your Gmail. Test it:
send a mail to this address from another account and check whether it arrives in
Gmail.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;step-2-generating-a-gmail-app-password&quot;&gt;Step 2: Generating a Gmail app password&lt;&#x2F;h2&gt;
&lt;p&gt;Now we move to the outbound side. You can&#x27;t connect to SMTP with your normal
Gmail password; Google rejects it. Instead you generate a 16-character special
password called an &lt;strong&gt;app password&lt;&#x2F;strong&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;The prerequisite for this is having 2FA enabled. If 2FA is off, the app password
screen doesn&#x27;t show up at all.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Go to your Google account: &lt;strong&gt;Google Account &amp;gt; Security&lt;&#x2F;strong&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;If two-step verification isn&#x27;t on, turn it on first.&lt;&#x2F;li&gt;
&lt;li&gt;Then go to the &lt;strong&gt;App passwords&lt;&#x2F;strong&gt; section (if you type &quot;app passwords&quot; into the
search box you&#x27;ll find it directly).&lt;&#x2F;li&gt;
&lt;li&gt;Create a new app password and give it a name (say &quot;Gmail SMTP&quot;).&lt;&#x2F;li&gt;
&lt;li&gt;Google gives you a 16-character code that appears with spaces (like
&lt;code&gt;abcd efgh ijkl mnop&lt;&#x2F;code&gt;). Save this code somewhere; once you leave the screen it
won&#x27;t show it again.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;There&#x27;s a point here: Google shows the password with spaces between the groups,
but when you enter it into the SMTP settings you need to remove those spaces and
type it as one continuous string. Keep this in mind while filling in the SMTP
server details in Step 3, otherwise the password won&#x27;t be accepted.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;step-3-introducing-the-custom-address-to-gmail-send-mail-as&quot;&gt;Step 3: Introducing the custom address to Gmail (Send mail as)&lt;&#x2F;h2&gt;
&lt;p&gt;The last step is telling Gmail &quot;I can also send from this address&quot;.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Gmail &amp;gt; &lt;strong&gt;Settings (gear) &amp;gt; See all settings &amp;gt; Accounts and Import&lt;&#x2F;strong&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;In the &lt;strong&gt;Send mail as&lt;&#x2F;strong&gt; section, click &lt;strong&gt;Add another email address&lt;&#x2F;strong&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;Enter a name and the address you want to send from: &lt;code&gt;iam@erdincyasan.com&lt;&#x2F;code&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;The &lt;strong&gt;Treat as an alias&lt;&#x2F;strong&gt; checkbox appears. You can leave it checked; if you
uncheck it, an &quot;on behalf of&quot; note may show up on the recipient&#x27;s side. For the
initial setup, leaving it checked gives the cleanest result.&lt;&#x2F;li&gt;
&lt;li&gt;The next screen asks for the SMTP details:&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;SMTP Server : smtp.gmail.com&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Port        : 465  (SSL)   or   587 (TLS)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Username    : erdincyasan@gmail.com   (the full Gmail address, not just &amp;quot;erdincyasan&amp;quot;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Password    : the 16-digit app password you generated (no spaces, one continuous string)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;ul&gt;
&lt;li&gt;Once you save, Google sends a verification code to &lt;code&gt;iam@erdincyasan.com&lt;&#x2F;code&gt;.
Thanks to Step 1, that mail lands in your Gmail. Click the link in the code or
enter the code.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;When it&#x27;s done, you can pick &lt;code&gt;iam@erdincyasan.com&lt;&#x2F;code&gt; from the &quot;From&quot; field while
composing a new mail in Gmail. Inbound is in Gmail, outbound is in Gmail. You
didn&#x27;t pay a cent.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;where-you-get-stuck-535-5-7-8-username-and-password-not-accepted&quot;&gt;Where you get stuck: &lt;code&gt;535-5.7.8 Username and Password not accepted&lt;&#x2F;code&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;When saving the SMTP settings in Step 3, this is the error you hit most often.
The reasons, in order:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;You entered your normal Gmail password.&lt;&#x2F;strong&gt; You need to use the app password.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;2FA is off.&lt;&#x2F;strong&gt; Since you can&#x27;t generate an app password, you&#x27;re already
entering something wrong.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;The username is incomplete.&lt;&#x2F;strong&gt; Not &lt;code&gt;erdincyasan&lt;&#x2F;code&gt;, but the full address:
&lt;code&gt;erdincyasan@gmail.com&lt;&#x2F;code&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;You didn&#x27;t remove the spaces from the app password.&lt;&#x2F;strong&gt; Google shows the code
with spaces like &lt;code&gt;abcd efgh ijkl mnop&lt;&#x2F;code&gt;; you need to enter it into SMTP as one
continuous string (&lt;code&gt;abcdefghijklmnop&lt;&#x2F;code&gt;).&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;An invisible character slipped in while copying.&lt;&#x2F;strong&gt; Enter the app password by
hand, carefully.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;limits-to-be-honest&quot;&gt;Limits (to be honest)&lt;&#x2F;h2&gt;
&lt;p&gt;This setup is free and gets the job done, but it&#x27;s not magic:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;It&#x27;s not a real mailbox. Nothing is stored on the Cloudflare side, it just
forwards. If you delete it in Gmail, that mail is gone.&lt;&#x2F;li&gt;
&lt;li&gt;Outbound mail technically leaves from Gmail&#x27;s server. That&#x27;s why SPF&#x2F;DKIM
alignment doesn&#x27;t &lt;strong&gt;fully&lt;&#x2F;strong&gt; line up with your domain; traces of Gmail remain in
the header. It&#x27;s not a problem for personal&#x2F;small use, but it&#x27;s not enough for
someone looking for a corporate mail infrastructure.&lt;&#x2F;li&gt;
&lt;li&gt;If you have serious sending volume or delivery guarantees matter, a separate
mail provider (like Zoho, Google Workspace) or a transactional SMTP service is
the better choice.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;But if you&#x27;re saying &quot;I just want a professional-looking address from my domain
without paying&quot;, this duo is exactly for that.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Local Ignore Rules in Git: .git&#x2F;info&#x2F;exclude and core.excludesFile</title>
        <published>2026-08-10T00:00:00+00:00</published>
        <updated>2026-08-10T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://erdincyasan.com/en/blog/git-yerel-ignore-kurallari/"/>
        <id>https://erdincyasan.com/en/blog/git-yerel-ignore-kurallari/</id>
        
        <content type="html" xml:base="https://erdincyasan.com/en/blog/git-yerel-ignore-kurallari/">&lt;h2 id=&quot;intro&quot;&gt;Intro&lt;&#x2F;h2&gt;
&lt;p&gt;I was working on a big project together with a team. Not everyone on the
project used Claude. While pairing, I kept dropping things into the project
root. I would scan a topic and get docs as .md output, throw in temporary json
files, add images. None of these had anything directly to do with the project.&lt;&#x2F;p&gt;
&lt;p&gt;There were two things I did not want. First, adding these files to .gitignore,
because .gitignore gets committed and goes to everyone as an unrelated change.
Second, having them show up in the file changes; I did not want to pollute the
diff with my own local work.&lt;&#x2F;p&gt;
&lt;p&gt;So I needed an ignore solution that touched the repo not at all and only applied
on my own machine. I looked, and I found it: .git&#x2F;info&#x2F;exclude.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;three-places-three-purposes&quot;&gt;Three places, three purposes&lt;&#x2F;h2&gt;
&lt;p&gt;In Git there are three places you can write an ignore rule, and each serves a
different purpose:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;.gitignore&lt;&#x2F;code&gt;: rules shared with the repo, committed, binding on everyone.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;.git&#x2F;info&#x2F;exclude&lt;&#x2F;code&gt;: only for your machine and your clone. Not committed,
pushed, or cloned; nobody on the team sees it.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;core.excludesFile&lt;&#x2F;code&gt;: a file you point to via git config. Can be global (for
all your repos, per-user) or local to a single repo.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;What I needed was the second one, but knowing all three is useful.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;git-info-exclude-personal-and-never-enters-the-repo&quot;&gt;.git&#x2F;info&#x2F;exclude, personal and never enters the repo&lt;&#x2F;h2&gt;
&lt;p&gt;This was exactly what I was looking for. The rules stay only with me and touch
the repo not at all. The file already exists in every repo, you do not need to
create anything extra, just appending lines is enough:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-support z-function z-builtin&quot;&gt;echo&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;*.local&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; &amp;gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; .git&#x2F;info&#x2F;exclude&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-support z-function z-builtin&quot;&gt;echo&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;scratch&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; &amp;gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; .git&#x2F;info&#x2F;exclude&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;I tested this locally. After writing the &lt;code&gt;*.local&lt;&#x2F;code&gt; rule, the untracked
&lt;code&gt;note.local&lt;&#x2F;code&gt; file was ignored:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;git&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; check-ignore&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;v&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; note.local&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;#&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; .git&#x2F;info&#x2F;exclude:1:*.local	note.local&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The best part is that this file is not cloned and not pushed. So I kept the docs
and json files I dropped into the project root here, and they did not show up in
anyone&#x27;s diff.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;important-pitfall-a-file-that-is-already-tracked&quot;&gt;Important pitfall: a file that is already tracked&lt;&#x2F;h2&gt;
&lt;p&gt;This is very commonly misunderstood, so I am writing it separately. If a file is
already committed (tracked), adding it to exclude changes nothing. Git does not
apply ignore rules to tracked files.&lt;&#x2F;p&gt;
&lt;p&gt;I tested it: I added a tracked file to exclude, and &lt;code&gt;git check-ignore -v&lt;&#x2F;code&gt;
returned nothing. Because a tracked file is not considered &quot;ignored&quot;.&lt;&#x2F;p&gt;
&lt;p&gt;The fix is to first drop the file from the index. The file in your working
directory is not deleted, only git&#x27;s tracking ends:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;git&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; rm&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;-cached&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; tracked.txt&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;#&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; the file stays on disk, git just no longer tracks it&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;#&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; after this the .gitignore or exclude rule kicks in&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h2 id=&quot;core-excludesfile-a-global-personal-ignore&quot;&gt;core.excludesFile, a global personal ignore&lt;&#x2F;h2&gt;
&lt;p&gt;Then there are things you do not want to write over and over in every repo.
Like &lt;code&gt;.DS_Store&lt;&#x2F;code&gt;, &lt;code&gt;.idea&#x2F;&lt;&#x2F;code&gt;, &lt;code&gt;*.swp&lt;&#x2F;code&gt;. Instead of writing these separately in
every project, you can define them centrally once:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;git&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; config&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;-global&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; core.excludesFile&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; ~&#x2F;.gitignore_global&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-support z-function z-builtin&quot;&gt;echo&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;*.conf&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; &amp;gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; ~&#x2F;.gitignore_global&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;I tried it locally, and the &lt;code&gt;*.conf&lt;&#x2F;code&gt; rule ignored &lt;code&gt;app.conf&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;git&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; check-ignore&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;v&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; app.conf&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;#&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; ~&#x2F;.gitignore_global:1:*.conf	app.conf&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Global is not required; without &lt;code&gt;--global&lt;&#x2F;code&gt;, you can also use it for a single
repo with &lt;code&gt;git config core.excludesFile &amp;lt;path&amp;gt;&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;precedence-order&quot;&gt;Precedence order&lt;&#x2F;h2&gt;
&lt;p&gt;Now we get to the part that really causes confusion. If there is a rule in more
than one place for the same file, who wins? I tested it locally; from highest to
lowest the order is:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Command-line patterns (like git ls-files)&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;.gitignore&lt;&#x2F;code&gt; files (a rule in a deeper directory overrides the one above, and
within the same file the last matching rule wins)&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;.git&#x2F;info&#x2F;exclude&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;core.excludesFile&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;I confirmed this with two negation tests. When I wrote &lt;code&gt;!note.local&lt;&#x2F;code&gt; into
&lt;code&gt;.gitignore&lt;&#x2F;code&gt;, it overrode the &lt;code&gt;*.local&lt;&#x2F;code&gt; rule in exclude and the file was not
ignored. So &lt;code&gt;.gitignore&lt;&#x2F;code&gt; has priority over exclude. Then when I wrote &lt;code&gt;!build&#x2F;&lt;&#x2F;code&gt;
into exclude, it overrode the &lt;code&gt;build&#x2F;&lt;&#x2F;code&gt; rule in core.excludesFile. So exclude
also has priority over core.excludesFile.&lt;&#x2F;p&gt;
&lt;p&gt;In short, the shared rule (&lt;code&gt;.gitignore&lt;&#x2F;code&gt;) always overrides your personal rules.
That is worth keeping in mind.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;debugging-why-is-a-file-ignored&quot;&gt;Debugging: why is a file ignored?&lt;&#x2F;h2&gt;
&lt;p&gt;When you wonder why a file is ignored (or not ignored), a single command is
enough:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;git&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; check-ignore&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;v&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; &amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;fil&lt;&#x2F;span&gt;&lt;span class=&quot;z-source&quot;&gt;e&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The &lt;code&gt;-v&lt;&#x2F;code&gt; flag shows which file, which line, and which rule matched:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;git&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; check-ignore&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;v&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; note.local&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;#&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; .git&#x2F;info&#x2F;exclude:1:*.local	note.local&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;If no output comes, there are two possibilities: either the file is not ignored,
or it is already tracked (tracked files are not reported by this command).&lt;&#x2F;p&gt;
&lt;h2 id=&quot;from-the-team-s-perspective-exclude-or-a-shared-rule&quot;&gt;From the team&#x27;s perspective: exclude or a shared rule?&lt;&#x2F;h2&gt;
&lt;p&gt;In my case the answer was clear. The files I dropped were entirely personal, the
team did not care about them, so &lt;code&gt;.git&#x2F;info&#x2F;exclude&lt;&#x2F;code&gt; was the right choice. I kept
my own mess on my own machine without touching the repo.&lt;&#x2F;p&gt;
&lt;p&gt;I have a simple distinction in mind:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Things specific only to you (IDE settings, personal scratch, pairing
leftovers): &lt;code&gt;.git&#x2F;info&#x2F;exclude&lt;&#x2F;code&gt; or global &lt;code&gt;core.excludesFile&lt;&#x2F;code&gt;. Does not
pollute the repo.&lt;&#x2F;li&gt;
&lt;li&gt;Things that should be excluded by the whole team (&lt;code&gt;node_modules&#x2F;&lt;&#x2F;code&gt;, build
output): a committed &lt;code&gt;.gitignore&lt;&#x2F;code&gt;. Because exclude is not shared, and everyone
needs to see the same behavior.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;&#x2F;h2&gt;
&lt;p&gt;In short, .gitignore is not the only place. For personal work there are
&lt;code&gt;.git&#x2F;info&#x2F;exclude&lt;&#x2F;code&gt; and &lt;code&gt;core.excludesFile&lt;&#x2F;code&gt;, and these touch the repo not at
all. The next time you drop a temporary file into the project root and think
&quot;how do I hide this&quot;, this is the solution.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>PostgreSQL LIKE Performance: CAST, Concat, and OR Comparison</title>
        <published>2026-06-29T00:00:00+00:00</published>
        <updated>2026-06-29T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://erdincyasan.com/en/blog/postgresql-like-concat-vs-or/"/>
        <id>https://erdincyasan.com/en/blog/postgresql-like-concat-vs-or/</id>
        
        <content type="html" xml:base="https://erdincyasan.com/en/blog/postgresql-like-concat-vs-or/">&lt;p&gt;I started thinking about a query I came across recently. There were two columns: &lt;code&gt;LongColumn&lt;&#x2F;code&gt; (&lt;code&gt;bigint&lt;&#x2F;code&gt;) and &lt;code&gt;Description&lt;&#x2F;code&gt; (&lt;code&gt;text&lt;&#x2F;code&gt;). The search worked like this:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;csharp&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; Old code: CAST + concat&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-source&quot;&gt;context&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-source&quot;&gt;Documents&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-accessor&quot;&gt;    .&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;Where&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-source&quot;&gt;d&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-source&quot;&gt; EF&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-source&quot;&gt;Functions&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;Like&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-source&quot;&gt;        d&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-source&quot;&gt;LongColumn&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;ToString&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; +&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; +&lt;&#x2F;span&gt;&lt;span class=&quot;z-source&quot;&gt; d&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-source&quot;&gt;Description&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-readwrite&quot;&gt; pattern&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;EF Core sent this to PostgreSQL as &lt;code&gt;LongColumn::text || &#x27; &#x27; || Description&lt;&#x2F;code&gt;. To run a LIKE search on a &lt;code&gt;bigint&lt;&#x2F;code&gt; column, you needed to cast with &lt;code&gt;::text&lt;&#x2F;code&gt; and concatenate it with Description. Later, the data in &lt;code&gt;LongColumn&lt;&#x2F;code&gt; was moved to a new &lt;code&gt;text&lt;&#x2F;code&gt; column called &lt;code&gt;TextColumn&lt;&#x2F;code&gt;. The CAST was no longer needed.&lt;&#x2F;p&gt;
&lt;p&gt;So how should we write the query now? Six different approaches:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;csharp&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; A: CAST + concat (old way)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-accessor&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;Where&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-source&quot;&gt;d&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-source&quot;&gt; EF&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-source&quot;&gt;Functions&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;Like&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-source&quot;&gt;    d&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-source&quot;&gt;LongColumn&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;ToString&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; +&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; +&lt;&#x2F;span&gt;&lt;span class=&quot;z-source&quot;&gt; d&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-source&quot;&gt;Description&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-readwrite&quot;&gt; pattern&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; B: CAST, single column&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-accessor&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;Where&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-source&quot;&gt;d&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-source&quot;&gt; EF&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-source&quot;&gt;Functions&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;Like&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-source&quot;&gt;d&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-source&quot;&gt;LongColumn&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;ToString&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-readwrite&quot;&gt; pattern&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; C: TextColumn concat&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-accessor&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;Where&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-source&quot;&gt;d&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-source&quot;&gt; EF&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-source&quot;&gt;Functions&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;Like&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-source&quot;&gt;    d&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-source&quot;&gt;TextColumn&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; +&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; +&lt;&#x2F;span&gt;&lt;span class=&quot;z-source&quot;&gt; d&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-source&quot;&gt;Description&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-readwrite&quot;&gt; pattern&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; D: TextColumn single column&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-accessor&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;Where&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-source&quot;&gt;d&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-source&quot;&gt; EF&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-source&quot;&gt;Functions&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;Like&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-source&quot;&gt;d&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-source&quot;&gt;TextColumn&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-readwrite&quot;&gt; pattern&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; E: TextColumn OR Description (separate LIKE)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-accessor&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;Where&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-source&quot;&gt;d&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-source&quot;&gt; EF&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-source&quot;&gt;Functions&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;Like&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-source&quot;&gt;d&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-source&quot;&gt;TextColumn&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-readwrite&quot;&gt; pattern&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;         ||&lt;&#x2F;span&gt;&lt;span class=&quot;z-source&quot;&gt; EF&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-source&quot;&gt;Functions&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;Like&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-source&quot;&gt;d&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-source&quot;&gt;Description&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-readwrite&quot;&gt; pattern&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; F: LongColumn::text OR Description (CAST OR)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-accessor&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;Where&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-source&quot;&gt;d&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-source&quot;&gt; EF&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-source&quot;&gt;Functions&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;Like&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-source&quot;&gt;d&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-source&quot;&gt;LongColumn&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;ToString&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-readwrite&quot;&gt; pattern&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;         ||&lt;&#x2F;span&gt;&lt;span class=&quot;z-source&quot;&gt; EF&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-source&quot;&gt;Functions&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;Like&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-source&quot;&gt;d&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-source&quot;&gt;Description&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-readwrite&quot;&gt; pattern&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Which one is faster? How expensive is CAST? Does concat break index usage? Can the OR approach use an index? To answer these questions, I set up a benchmark with 2 million rows.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;benchmark-setup&quot;&gt;Benchmark Setup&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;PostgreSQL 16 (Docker), port 5433&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;documents&lt;&#x2F;code&gt; table: &lt;code&gt;Id&lt;&#x2F;code&gt;, &lt;code&gt;LongColumn (bigint)&lt;&#x2F;code&gt;, &lt;code&gt;TextColumn (text)&lt;&#x2F;code&gt;, &lt;code&gt;Description (text)&lt;&#x2F;code&gt; - 2 million rows&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;TextColumn&lt;&#x2F;code&gt;: string representation of &lt;code&gt;LongColumn&lt;&#x2F;code&gt; + Turkish text. Average 439 bytes&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;LongColumn&lt;&#x2F;code&gt;: random 10-digit bigint. String representation averages 14 bytes&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;Description&lt;&#x2F;code&gt;: Turkish text, average 423 bytes&lt;&#x2F;li&gt;
&lt;li&gt;Indexes on &lt;code&gt;TextColumn&lt;&#x2F;code&gt; and &lt;code&gt;Description&lt;&#x2F;code&gt; (you cannot create a text index on a &lt;code&gt;bigint&lt;&#x2F;code&gt; column)&lt;&#x2F;li&gt;
&lt;li&gt;3 index strategies: None, B-tree, GIN trigram (&lt;code&gt;pg_trgm&lt;&#x2F;code&gt;)&lt;&#x2F;li&gt;
&lt;li&gt;3 patterns: &lt;code&gt;%ankara%&lt;&#x2F;code&gt; (&lt;del&gt;%42 rows), &lt;code&gt;%de%&lt;&#x2F;code&gt; (&lt;&#x2F;del&gt;%99 rows), &lt;code&gt;%xyzqwerty%&lt;&#x2F;code&gt; (none)&lt;&#x2F;li&gt;
&lt;li&gt;Each scenario run 5 times, best&#x2F;worst discarded, average of 3&lt;&#x2F;li&gt;
&lt;li&gt;Measured with &lt;code&gt;EXPLAIN (ANALYZE, BUFFERS, FORMAT JSON)&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;sql-generated-by-ef-core&quot;&gt;SQL Generated by EF Core&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;strong&gt;A - CAST + concat&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;sql&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;--&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; @__pattern_1=&amp;#39;%ankara%&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;SELECT&lt;&#x2F;span&gt;&lt;span class=&quot;z-source&quot;&gt; d.&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;Id&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-source&quot;&gt;, d.&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;Description&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-source&quot;&gt;, d.&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;LongColumn&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-source&quot;&gt;, d.&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;TextColumn&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;FROM&lt;&#x2F;span&gt;&lt;span class=&quot;z-source&quot;&gt; documents &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;AS&lt;&#x2F;span&gt;&lt;span class=&quot;z-source&quot;&gt; d&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;WHERE&lt;&#x2F;span&gt;&lt;span class=&quot;z-source&quot;&gt; d.&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;LongColumn&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-source&quot;&gt;::&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;text&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; ||&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; ||&lt;&#x2F;span&gt;&lt;span class=&quot;z-source&quot;&gt; d.&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;Description&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; LIKE&lt;&#x2F;span&gt;&lt;span class=&quot;z-text&quot;&gt; @__pattern_1&lt;&#x2F;span&gt;&lt;span class=&quot;z-source&quot;&gt; ESCAPE &lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;strong&gt;B - CAST, single column&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;sql&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;WHERE&lt;&#x2F;span&gt;&lt;span class=&quot;z-source&quot;&gt; d.&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;LongColumn&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-source&quot;&gt;::&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;text&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; LIKE&lt;&#x2F;span&gt;&lt;span class=&quot;z-text&quot;&gt; @__pattern_1&lt;&#x2F;span&gt;&lt;span class=&quot;z-source&quot;&gt; ESCAPE &lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;strong&gt;C - TextColumn concat&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;sql&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;WHERE&lt;&#x2F;span&gt;&lt;span class=&quot;z-source&quot;&gt; d.&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;TextColumn&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; ||&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; ||&lt;&#x2F;span&gt;&lt;span class=&quot;z-source&quot;&gt; d.&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;Description&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; LIKE&lt;&#x2F;span&gt;&lt;span class=&quot;z-text&quot;&gt; @__pattern_1&lt;&#x2F;span&gt;&lt;span class=&quot;z-source&quot;&gt; ESCAPE &lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;strong&gt;D - TextColumn single column&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;sql&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;WHERE&lt;&#x2F;span&gt;&lt;span class=&quot;z-source&quot;&gt; d.&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;TextColumn&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; LIKE&lt;&#x2F;span&gt;&lt;span class=&quot;z-text&quot;&gt; @__pattern_1&lt;&#x2F;span&gt;&lt;span class=&quot;z-source&quot;&gt; ESCAPE &lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;strong&gt;E - TextColumn OR Description&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;sql&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;WHERE&lt;&#x2F;span&gt;&lt;span class=&quot;z-source&quot;&gt; d.&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;TextColumn&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; LIKE&lt;&#x2F;span&gt;&lt;span class=&quot;z-text&quot;&gt; @__pattern_1&lt;&#x2F;span&gt;&lt;span class=&quot;z-source&quot;&gt; ESCAPE &lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; OR&lt;&#x2F;span&gt;&lt;span class=&quot;z-source&quot;&gt; d.&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;Description&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; LIKE&lt;&#x2F;span&gt;&lt;span class=&quot;z-text&quot;&gt; @__pattern_1&lt;&#x2F;span&gt;&lt;span class=&quot;z-source&quot;&gt; ESCAPE &lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;strong&gt;F - LongColumn::text OR Description&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;sql&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;WHERE&lt;&#x2F;span&gt;&lt;span class=&quot;z-source&quot;&gt; d.&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;LongColumn&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-source&quot;&gt;::&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;text&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; LIKE&lt;&#x2F;span&gt;&lt;span class=&quot;z-text&quot;&gt; @__pattern_1&lt;&#x2F;span&gt;&lt;span class=&quot;z-source&quot;&gt; ESCAPE &lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; OR&lt;&#x2F;span&gt;&lt;span class=&quot;z-source&quot;&gt; d.&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;Description&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; LIKE&lt;&#x2F;span&gt;&lt;span class=&quot;z-text&quot;&gt; @__pattern_1&lt;&#x2F;span&gt;&lt;span class=&quot;z-source&quot;&gt; ESCAPE &lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;code&gt;d.LongColumn.ToString()&lt;&#x2F;code&gt; becomes &lt;code&gt;LongColumn::text&lt;&#x2F;code&gt;. &lt;code&gt;string + string&lt;&#x2F;code&gt; becomes &lt;code&gt;||&lt;&#x2F;code&gt;. &lt;code&gt;ESCAPE &#x27;&#x27;&lt;&#x2F;code&gt; is EF Core&#x27;s default, no performance impact.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;results-with-gin-trigram-index&quot;&gt;Results - With GIN Trigram Index&lt;&#x2F;h2&gt;
&lt;p&gt;The most critical scenario is with a GIN index. I skip B-tree since it gives the same result as None - &lt;code&gt;LIKE &#x27;%x%&#x27;&lt;&#x2F;code&gt; is fundamentally incompatible with B-tree.&lt;&#x2F;p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Approach&lt;&#x2F;th&gt;&lt;th&gt;Pattern&lt;&#x2F;th&gt;&lt;th&gt;Time (ms)&lt;&#x2F;th&gt;&lt;th&gt;Read Blocks&lt;&#x2F;th&gt;&lt;th&gt;Rows&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;A: CAST+concat&lt;&#x2F;td&gt;&lt;td&gt;%ankara%&lt;&#x2F;td&gt;&lt;td&gt;702.35&lt;&#x2F;td&gt;&lt;td&gt;465,400&lt;&#x2F;td&gt;&lt;td&gt;1,036,239&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;A: CAST+concat&lt;&#x2F;td&gt;&lt;td&gt;%de%&lt;&#x2F;td&gt;&lt;td&gt;645.73&lt;&#x2F;td&gt;&lt;td&gt;232,304&lt;&#x2F;td&gt;&lt;td&gt;1,991,384&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;A: CAST+concat&lt;&#x2F;td&gt;&lt;td&gt;%xyzqwerty%&lt;&#x2F;td&gt;&lt;td&gt;468.41&lt;&#x2F;td&gt;&lt;td&gt;464,416&lt;&#x2F;td&gt;&lt;td&gt;0&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;B: CAST single&lt;&#x2F;td&gt;&lt;td&gt;%ankara%&lt;&#x2F;td&gt;&lt;td&gt;131.93&lt;&#x2F;td&gt;&lt;td&gt;459,568&lt;&#x2F;td&gt;&lt;td&gt;0&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;B: CAST single&lt;&#x2F;td&gt;&lt;td&gt;%de%&lt;&#x2F;td&gt;&lt;td&gt;328.45&lt;&#x2F;td&gt;&lt;td&gt;229,556&lt;&#x2F;td&gt;&lt;td&gt;0&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;B: CAST single&lt;&#x2F;td&gt;&lt;td&gt;%xyzqwerty%&lt;&#x2F;td&gt;&lt;td&gt;137.11&lt;&#x2F;td&gt;&lt;td&gt;457,680&lt;&#x2F;td&gt;&lt;td&gt;0&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;C: TextConcat&lt;&#x2F;td&gt;&lt;td&gt;%ankara%&lt;&#x2F;td&gt;&lt;td&gt;996.65&lt;&#x2F;td&gt;&lt;td&gt;453,224&lt;&#x2F;td&gt;&lt;td&gt;1,729,401&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;C: TextConcat&lt;&#x2F;td&gt;&lt;td&gt;%de%&lt;&#x2F;td&gt;&lt;td&gt;640.01&lt;&#x2F;td&gt;&lt;td&gt;226,244&lt;&#x2F;td&gt;&lt;td&gt;1,999,945&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;C: TextConcat&lt;&#x2F;td&gt;&lt;td&gt;%xyzqwerty%&lt;&#x2F;td&gt;&lt;td&gt;780.89&lt;&#x2F;td&gt;&lt;td&gt;451,480&lt;&#x2F;td&gt;&lt;td&gt;0&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;D: TextSingle&lt;&#x2F;td&gt;&lt;td&gt;%ankara%&lt;&#x2F;td&gt;&lt;td&gt;1,644.34&lt;&#x2F;td&gt;&lt;td&gt;224,260&lt;&#x2F;td&gt;&lt;td&gt;850,373&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;D: TextSingle&lt;&#x2F;td&gt;&lt;td&gt;%de%&lt;&#x2F;td&gt;&lt;td&gt;552.69&lt;&#x2F;td&gt;&lt;td&gt;224,148&lt;&#x2F;td&gt;&lt;td&gt;1,987,804&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;D: TextSingle&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td&gt;&lt;strong&gt;%xyzqwerty%&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td&gt;&lt;strong&gt;0.01&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td&gt;&lt;strong&gt;0&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td&gt;0&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;E: TextOR&lt;&#x2F;td&gt;&lt;td&gt;%ankara%&lt;&#x2F;td&gt;&lt;td&gt;2,480.59&lt;&#x2F;td&gt;&lt;td&gt;223,484&lt;&#x2F;td&gt;&lt;td&gt;1,297,051&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;E: TextOR&lt;&#x2F;td&gt;&lt;td&gt;%de%&lt;&#x2F;td&gt;&lt;td&gt;543.10&lt;&#x2F;td&gt;&lt;td&gt;223,204&lt;&#x2F;td&gt;&lt;td&gt;1,999,945&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;E: TextOR&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td&gt;&lt;strong&gt;%xyzqwerty%&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td&gt;&lt;strong&gt;0.01&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td&gt;&lt;strong&gt;0&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td&gt;0&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;F: CAST OR&lt;&#x2F;td&gt;&lt;td&gt;%ankara%&lt;&#x2F;td&gt;&lt;td&gt;1,776.36&lt;&#x2F;td&gt;&lt;td&gt;222,064&lt;&#x2F;td&gt;&lt;td&gt;777,179&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;F: CAST OR&lt;&#x2F;td&gt;&lt;td&gt;%de%&lt;&#x2F;td&gt;&lt;td&gt;627.46&lt;&#x2F;td&gt;&lt;td&gt;221,848&lt;&#x2F;td&gt;&lt;td&gt;1,991,384&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;F: CAST OR&lt;&#x2F;td&gt;&lt;td&gt;%xyzqwerty%&lt;&#x2F;td&gt;&lt;td&gt;459.11&lt;&#x2F;td&gt;&lt;td&gt;443,016&lt;&#x2F;td&gt;&lt;td&gt;0&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;h2 id=&quot;key-finding-1-gin-index-who-uses-it-who-can-t&quot;&gt;Key Finding 1: GIN Index - Who Uses It, Who Can&#x27;t?&lt;&#x2F;h2&gt;
&lt;p&gt;Focus on the &lt;code&gt;%xyzqwerty%&lt;&#x2F;code&gt; pattern. This pattern exists in zero rows - it shows the index&#x27;s real effect most clearly:&lt;&#x2F;p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;#&lt;&#x2F;th&gt;&lt;th&gt;Approach&lt;&#x2F;th&gt;&lt;th&gt;SQL Summary&lt;&#x2F;th&gt;&lt;th&gt;Time&lt;&#x2F;th&gt;&lt;th&gt;Scan Type&lt;&#x2F;th&gt;&lt;th&gt;Index?&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;A&lt;&#x2F;td&gt;&lt;td&gt;CAST+concat&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;LongCol::text || Desc&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;468ms&lt;&#x2F;td&gt;&lt;td&gt;Seq Scan&lt;&#x2F;td&gt;&lt;td&gt;❌&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;B&lt;&#x2F;td&gt;&lt;td&gt;CAST single&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;LongCol::text&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;137ms&lt;&#x2F;td&gt;&lt;td&gt;Seq Scan&lt;&#x2F;td&gt;&lt;td&gt;❌&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;C&lt;&#x2F;td&gt;&lt;td&gt;TextConcat&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;TextCol || Desc&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;781ms&lt;&#x2F;td&gt;&lt;td&gt;Seq Scan&lt;&#x2F;td&gt;&lt;td&gt;❌&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;D&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td&gt;&lt;strong&gt;TextSingle&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;TextCol&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;&lt;strong&gt;0.01ms&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td&gt;&lt;strong&gt;Bitmap Index Scan&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td&gt;✅&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;E&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td&gt;&lt;strong&gt;Text OR&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;TextCol OR Desc&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;&lt;strong&gt;0.01ms&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td&gt;&lt;strong&gt;BitmapOr&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td&gt;✅✅&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;F&lt;&#x2F;td&gt;&lt;td&gt;CAST OR&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;LongCol::text OR Desc&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;459ms&lt;&#x2F;td&gt;&lt;td&gt;Seq Scan&lt;&#x2F;td&gt;&lt;td&gt;❌&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;p&gt;Only D and E can use the GIN index. Why?&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;A, B, C:&lt;&#x2F;strong&gt; &lt;code&gt;||&lt;&#x2F;code&gt; concat and &lt;code&gt;::text&lt;&#x2F;code&gt; CAST are &lt;em&gt;expressions&lt;&#x2F;em&gt;. The GIN index is on the &lt;code&gt;TextColumn&lt;&#x2F;code&gt; and &lt;code&gt;Description&lt;&#x2F;code&gt; columns. Expressions don&#x27;t match these indexes - the planner falls back to Seq Scan.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;D:&lt;&#x2F;strong&gt; &lt;code&gt;TextColumn&lt;&#x2F;code&gt; is a direct column reference. &lt;code&gt;ix_text_gin&lt;&#x2F;code&gt; is exactly on this column. The planner immediately uses Bitmap Index Scan.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;E:&lt;&#x2F;strong&gt; Two direct column references connected by OR. PostgreSQL scans &lt;code&gt;ix_text_gin&lt;&#x2F;code&gt; and &lt;code&gt;ix_desc_gin&lt;&#x2F;code&gt; separately, then combines results with &lt;strong&gt;BitmapOr&lt;&#x2F;strong&gt;. Two indexes, one pass.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;F:&lt;&#x2F;strong&gt; &lt;code&gt;LongColumn::text&lt;&#x2F;code&gt; is CAST, &lt;code&gt;Description&lt;&#x2F;code&gt; is direct. The CAST part cannot use an index. PostgreSQL drops the &lt;em&gt;entire&lt;&#x2F;em&gt; OR expression to Seq Scan - even though Description has a GIN index.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;key-finding-2-e-s-explain-plan-bitmapor&quot;&gt;Key Finding 2: E&#x27;s EXPLAIN Plan - BitmapOr&lt;&#x2F;h2&gt;
&lt;p&gt;Approach E (&lt;code&gt;TextColumn OR Description&lt;&#x2F;code&gt;) with GIN index produces this plan:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Bitmap Heap Scan (time=0.003ms, rows=0)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  -&amp;gt; BitmapOr&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    -&amp;gt; Bitmap Index Scan on ix_text_gin (time=0.003ms)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;       Index Cond: (&amp;quot;TextColumn&amp;quot; ~~ &amp;#39;%xyzqwerty%&amp;#39;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    -&amp;gt; Bitmap Index Scan on ix_desc_gin (time=0.003ms)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;       Index Cond: (&amp;quot;Description&amp;quot; ~~ &amp;#39;%xyzqwerty%&amp;#39;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Two indexes scanned in parallel, combined with BitmapOr, heap never touched. Result: 0.01ms.&lt;&#x2F;p&gt;
&lt;p&gt;This shows why separate LIKE + OR is so much better than concat. Concat (A, C) turns the expression into a single expression and disables the index, while OR lets each column use its index independently.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;key-finding-3-cast-poisons-or&quot;&gt;Key Finding 3: CAST Poisons OR&lt;&#x2F;h2&gt;
&lt;p&gt;F (&lt;code&gt;LongColumn::text OR Description&lt;&#x2F;code&gt;) does a Seq Scan even with a GIN index:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Gather (workers=2) -&amp;gt; Parallel Seq Scan&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  Filter: ((LongColumn::text ~~ &amp;#39;%xyzqwerty%&amp;#39;) OR (Description ~~ &amp;#39;%xyzqwerty%&amp;#39;))&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  Rows Removed by Filter: 666,667 (x3 workers = 2M)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  Execution Time: 459ms&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Description has a GIN index (&lt;code&gt;ix_desc_gin&lt;&#x2F;code&gt;). But because &lt;code&gt;LongColumn::text&lt;&#x2F;code&gt; CAST is non-indexable, PostgreSQL plans the entire OR expression as a Seq Scan. A single non-indexable term leaves the whole query without index benefits.&lt;&#x2F;p&gt;
&lt;p&gt;This shows why the migration was critical: as long as &lt;code&gt;LongColumn&lt;&#x2F;code&gt; (bigint) remains, whether you use concat or OR, you get no index benefit.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;key-finding-4-speed-ranking-gin-sparse-pattern&quot;&gt;Key Finding 4: Speed Ranking (GIN, sparse pattern)&lt;&#x2F;h2&gt;
&lt;p&gt;Winners:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;D and E: 0.01ms&lt;&#x2F;strong&gt; - uses index, sub-millisecond&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;B: 137ms&lt;&#x2F;strong&gt; - Seq Scan but shortest data (14 bytes)&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;A: 468ms&lt;&#x2F;strong&gt; - Seq Scan, CAST + concat (~438 bytes)&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;F: 459ms&lt;&#x2F;strong&gt; - Seq Scan, CAST OR (~438 bytes)&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;C: 781ms&lt;&#x2F;strong&gt; - Seq Scan, TextConcat (~863 bytes)&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;C is slowest because it scans the largest data (TextColumn 439B + Description 423B = 862B). B is the fastest Seq Scan because it scans only 14 bytes of LongColumn::text.&lt;&#x2F;p&gt;
&lt;p&gt;But when the index is in play, data size no longer matters - D scans 439 bytes but still takes 0.01ms thanks to the index.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;other-findings&quot;&gt;Other Findings&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;strong&gt;Index is not used for frequent patterns.&lt;&#x2F;strong&gt; For &lt;code&gt;%de%&lt;&#x2F;code&gt; and &lt;code&gt;%ankara%&lt;&#x2F;code&gt;, all approaches use Seq Scan. The planner knows that when many rows match, heap lookup cost exceeds a direct scan.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;CAST cost is low.&lt;&#x2F;strong&gt; The &lt;code&gt;bigint::text&lt;&#x2F;code&gt; conversion is microsecond-level. The real cost is concatenating two large columns.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;A is faster than C.&lt;&#x2F;strong&gt; CAST+concat (468ms) is faster than TextConcat (781ms). Reason: &lt;code&gt;LongColumn::text&lt;&#x2F;code&gt; is 14 bytes, &lt;code&gt;TextColumn&lt;&#x2F;code&gt; is 439 bytes. Data size is the deciding factor, not CAST cost.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;practical-recommendations&quot;&gt;Practical Recommendations&lt;&#x2F;h2&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Never use concat in a LIKE query.&lt;&#x2F;strong&gt; The &lt;code&gt;col1 || &#x27; &#x27; || col2&lt;&#x2F;code&gt; expression completely disables column-level indexes. Always use &lt;strong&gt;separate LIKE + OR&lt;&#x2F;strong&gt; instead.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;When using OR, don&#x27;t include CAST columns.&lt;&#x2F;strong&gt; Even a single CAST term makes the entire OR fall to Seq Scan. Consider writing the CAST column as a separate WHERE condition and combining with &lt;code&gt;UNION&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Migrate non-text columns to text.&lt;&#x2F;strong&gt; If you need LIKE search on types like &lt;code&gt;bigint&lt;&#x2F;code&gt;, migrate to a text column. CAST disappears and GIN index becomes usable.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;GIN trigram index is devastating for sparse patterns.&lt;&#x2F;strong&gt; But the planner won&#x27;t use it for frequent patterns. Evaluate the INSERT&#x2F;UPDATE cost of the index.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Separate LIKE + OR is as fast as single-column LIKE.&lt;&#x2F;strong&gt; Both E (&lt;code&gt;TextColumn OR Description&lt;&#x2F;code&gt;) and D (&lt;code&gt;TextColumn&lt;&#x2F;code&gt;) take 0.01ms. Adding the second column has negligible index cost (BitmapOr).&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Measure on your own data.&lt;&#x2F;strong&gt; This benchmark is 2M rows, Docker macOS. Test with &lt;code&gt;EXPLAIN ANALYZE&lt;&#x2F;code&gt; in your own environment.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;h2 id=&quot;sources&quot;&gt;Sources&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;erdyasan&#x2F;pg-perf&quot;&gt;GitHub - pg-perf benchmark project&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.postgresql.org&#x2F;docs&#x2F;current&#x2F;pgtrgm.html&quot;&gt;PostgreSQL pg_trgm documentation&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.npgsql.org&#x2F;efcore&#x2F;mapping&#x2F;string.html&quot;&gt;Npgsql EF Core - string translations&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>.NET Memory Management 1: Stack, Heap and Strings</title>
        <published>2026-06-28T00:00:00+00:00</published>
        <updated>2026-06-28T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://erdincyasan.com/en/blog/bellek-yonetimi-1-stack-heap-string/"/>
        <id>https://erdincyasan.com/en/blog/bellek-yonetimi-1-stack-heap-string/</id>
        
        <content type="html" xml:base="https://erdincyasan.com/en/blog/bellek-yonetimi-1-stack-heap-string/">&lt;p&gt;Hey there. This week I decided to focus on memory management after a recent
interview. I had surface-level knowledge on the topic and wanted to go deeper.
What is the stack, what is the heap, why does string live on the heap, what
happens behind the scenes when CLR creates an object, how does &lt;code&gt;Span&amp;lt;T&amp;gt;&lt;&#x2F;code&gt; work
without allocating memory, when does GC kick in, who benefits from
&lt;code&gt;IDisposable&lt;&#x2F;code&gt; - I&#x27;ll cover all of it in order.&lt;&#x2F;p&gt;
&lt;p&gt;But first, let&#x27;s start from the basics. What exactly are &quot;stack and heap&quot;?&lt;&#x2F;p&gt;
&lt;h2 id=&quot;finding-space-for-data&quot;&gt;Finding Space for Data&lt;&#x2F;h2&gt;
&lt;p&gt;When you define a variable:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;csharp&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;int&lt;&#x2F;span&gt;&lt;span class=&quot;z-source&quot;&gt; x&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 5&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;string&lt;&#x2F;span&gt;&lt;span class=&quot;z-source&quot;&gt; name&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;Erdinc&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;User&lt;&#x2F;span&gt;&lt;span class=&quot;z-source&quot;&gt; user&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; new&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; User&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; {&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-readwrite&quot;&gt; Name&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;Erdinc&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-readwrite&quot;&gt; Age&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 30&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; }&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Where do &lt;code&gt;x&lt;&#x2F;code&gt;, &lt;code&gt;name&lt;&#x2F;code&gt;, and &lt;code&gt;user&lt;&#x2F;code&gt; live? In your computer&#x27;s RAM - okay.
But where in RAM? In some corner, the middle? Answer: &lt;strong&gt;it depends on the type.&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;The .NET runtime uses two main regions for memory management:&lt;&#x2F;p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Region&lt;&#x2F;th&gt;&lt;th&gt;Description&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Stack&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td&gt;A LIFO (Last-In-First-Out) structure where method calls, local variables, and parameters live. Small, fast, automatically cleaned.&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Heap&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td&gt;The region where dynamically allocated, long-lived objects live. Large, managed by the GC.&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;p&gt;Understanding the difference between the two is the first step in grasping the
performance of the code you write.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;stack-lifo-structure&quot;&gt;Stack: LIFO Structure&lt;&#x2F;h2&gt;
&lt;p&gt;The best analogy for understanding how the stack works is &lt;strong&gt;a stack of plates.&lt;&#x2F;strong&gt;
When you want to add a plate, you put it on top. When you want to take one,
you take the top one. You can&#x27;t pull one from the middle. The last plate you
put on is the first one taken. That&#x27;s LIFO.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;[Plate 3]  ← Last placed, first taken (Last-In)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;[Plate 2]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;[Plate 1]  ← First placed, last taken (First-Out)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This is exactly what happens on the stack. Methods run stacked on top of each
other, and when a method finishes, its variables are cleaned up immediately:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;csharp&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;&#x2F;*&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-comment&quot;&gt; * When Calculate finishes, all its variables (a, b, result) are&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-comment&quot;&gt; * completely removed from memory. But what exactly is this &amp;quot;removal&amp;quot;?&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-comment&quot;&gt; *&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-comment&quot;&gt; * At the CPU level, the stack is managed by the RSP (Stack Pointer) register.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-comment&quot;&gt; * When entering a method, RSP is pulled down (sub rsp, N), allocating&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-comment&quot;&gt; * N bytes of space. When the method returns, RSP is pushed up&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-comment&quot;&gt; * (add rsp, N), &amp;quot;freeing&amp;quot; that space.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-comment&quot;&gt; *&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-comment&quot;&gt; * This is called &amp;quot;Stack Pointer Adjustment&amp;quot; or simply &amp;quot;stack unwinding.&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-comment&quot;&gt; *&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-comment&quot;&gt; * Key point: the data in the freed region is NOT physically DELETED.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-comment&quot;&gt; * It is just marked as no longer &amp;quot;in use.&amp;quot; The operating system can&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-comment&quot;&gt; * overwrite the same memory addresses with new values on the next&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-comment&quot;&gt; * method call. This is why the compiler gives an error if you try to&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-comment&quot;&gt; * read an uninitialized local variable in C# - the value you read&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-comment&quot;&gt; * could be leftover garbage from the previous method.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt; *&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;void&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; Calculate&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;{&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    int&lt;&#x2F;span&gt;&lt;span class=&quot;z-source&quot;&gt; a&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 10&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;       &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; a = 10 written to stack&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    int&lt;&#x2F;span&gt;&lt;span class=&quot;z-source&quot;&gt; b&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 20&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;       &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; b = 20 written to stack&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    int&lt;&#x2F;span&gt;&lt;span class=&quot;z-source&quot;&gt; result&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; Add&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-readwrite&quot;&gt;a&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-readwrite&quot;&gt; b&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;  &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; Add method called&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;}&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;   &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; When Calculate returns, RSP is pushed up, frame is freed&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;int&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; Add&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;int&lt;&#x2F;span&gt;&lt;span class=&quot;z-source&quot;&gt; x&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; int&lt;&#x2F;span&gt;&lt;span class=&quot;z-source&quot;&gt; y&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;{&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    int&lt;&#x2F;span&gt;&lt;span class=&quot;z-source&quot;&gt; sum&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-readwrite&quot;&gt; x&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; +&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-readwrite&quot;&gt; y&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;  &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; New frame for x, y, sum opened on stack&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    return&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-readwrite&quot;&gt; sum&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;       &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; When Add returns, its own frame is freed&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The stack grows as methods are called and shrinks as methods return. Each
method creates its own &lt;strong&gt;stack frame.&lt;&#x2F;strong&gt; The frame contains the method&#x27;s
parameters, local variables, and return address. Visually:&lt;&#x2F;p&gt;
&lt;pre class=&quot;mermaid&quot;&gt;
flowchart TB
    subgraph T1[&quot;1. Calculate called&quot;]
        direction LR
        A1[&quot;[Empty]&quot;]
    end
    subgraph T2[&quot;2. int a = 10&quot;]
        direction LR
        A2[&quot;a = 10&quot;]
    end
    subgraph T3[&quot;3. int b = 20&quot;]
        direction LR
        A3[&quot;b = 20&quot;]
        A3b[&quot;a = 10&quot;]
    end
    subgraph T4[&quot;4. Add(10, 20) called&quot;]
        direction LR
        A4[&quot;sum = 30&quot;]
        A4b[&quot;y = 20&quot;]
        A4c[&quot;x = 10&quot;]
        A4d[&quot;--- frame boundary ---&quot;]
        A4e[&quot;b = 20&quot;]
        A4f[&quot;a = 10&quot;]
    end
    subgraph T5[&quot;5. Add returned (frame removed)&quot;]
        direction LR
        A5[&quot;result = 30&quot;]
        A5b[&quot;b = 20&quot;]
        A5c[&quot;a = 10&quot;]
    end
    subgraph T6[&quot;6. Calculate returned (all removed)&quot;]
        direction LR
        A6[&quot;[Empty]&quot;]
    end

    T1 --&gt; T2 --&gt; T3 --&gt; T4 --&gt; T5 --&gt; T6
&lt;&#x2F;pre&gt;
&lt;p&gt;Notice: when Add returns, &lt;code&gt;x&lt;&#x2F;code&gt;, &lt;code&gt;y&lt;&#x2F;code&gt;, &lt;code&gt;sum&lt;&#x2F;code&gt; - along with the frame - were
&lt;strong&gt;immediately&lt;&#x2F;strong&gt; removed. We didn&#x27;t wait for the GC to come. This is called
&lt;strong&gt;deterministic cleanup.&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;garbage-values-and-memory-safety&quot;&gt;Garbage Values and Memory Safety&lt;&#x2F;h2&gt;
&lt;p&gt;We said earlier: the data in the freed stack region is not physically deleted.
So what happens if we try to access this &quot;unswept&quot; data?&lt;&#x2F;p&gt;
&lt;h3 id=&quot;the-c-side-here-s-an-address-go-wherever-you-want&quot;&gt;The C++ Side: Here&#x27;s an Address, Go Wherever You Want&lt;&#x2F;h3&gt;
&lt;p&gt;C++ puts up no barriers. If you declare a variable on the stack, don&#x27;t
initialize it, and try to read it:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;cpp&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;void&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; Foo&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;{&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;    int&lt;&#x2F;span&gt;&lt;span class=&quot;z-source&quot;&gt; previousData &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 42&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;    &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; left a value on the stack&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;void&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; Bar&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;{&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;    int&lt;&#x2F;span&gt;&lt;span class=&quot;z-source&quot;&gt; x&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;                    &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; not initialized!&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-scope-resolution z-cpp&quot;&gt;    std&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;::&lt;&#x2F;span&gt;&lt;span class=&quot;z-source&quot;&gt;cout &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;&amp;lt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-source&quot;&gt; x&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;           &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; prints 42 or some other random number&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;When &lt;code&gt;Bar&lt;&#x2F;code&gt; is called, its stack frame might land exactly on the region
&lt;code&gt;Foo&lt;&#x2F;code&gt; left behind. &lt;code&gt;x&lt;&#x2F;code&gt;&#x27;s value will be &lt;code&gt;42&lt;&#x2F;code&gt; - but this is pure coincidence.
In another run you&#x27;ll get a completely different number. This is called
&lt;strong&gt;undefined behavior.&lt;&#x2F;strong&gt; The compiler won&#x27;t error, you won&#x27;t crash at runtime;
you&#x27;ll just get wrong results without ever knowing it.&lt;&#x2F;p&gt;
&lt;p&gt;Even worse, C++ pointer arithmetic lets you step completely outside the stack
and access completely different data within the same process:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;cpp&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;int&lt;&#x2F;span&gt;&lt;span class=&quot;z-source&quot;&gt; arr&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;3&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; {&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;1&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 2&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 3&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;}&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;int&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;*&lt;&#x2F;span&gt;&lt;span class=&quot;z-source&quot;&gt; p &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-source&quot;&gt; arr&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-source&quot;&gt;p &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;+=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 100&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;                &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; 100 elements past the array - undefined region&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-scope-resolution z-cpp&quot;&gt;std&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;::&lt;&#x2F;span&gt;&lt;span class=&quot;z-source&quot;&gt;cout &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;&amp;lt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; *&lt;&#x2F;span&gt;&lt;span class=&quot;z-source&quot;&gt;p&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;         &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; reads whatever is there&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;On modern operating systems, thanks to virtual memory, you can&#x27;t access
another &lt;strong&gt;program&#x27;s&lt;&#x2F;strong&gt; memory - the MMU won&#x27;t allow it, you&#x27;d get a segfault.
But within your own process, you can go to any address and read it.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;the-c-side-the-compiler-blocks-you&quot;&gt;The C# Side: The Compiler Blocks You&lt;&#x2F;h3&gt;
&lt;p&gt;C# is the polar opposite of C++ in this regard. &lt;strong&gt;C# memory operations are safe.&lt;&#x2F;strong&gt;
Let&#x27;s try the same scenario in C#:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;csharp&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;void&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; Bar&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;{&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    int&lt;&#x2F;span&gt;&lt;span class=&quot;z-source&quot;&gt; x&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-source&quot;&gt;    Console&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;WriteLine&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-readwrite&quot;&gt;x&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;  &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; COMPILE ERROR: Use of unassigned local variable &amp;#39;x&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The C# compiler performs &lt;strong&gt;definite assignment&lt;&#x2F;strong&gt; analysis. It requires that
a local variable must always have a value assigned before it is read. If it
isn&#x27;t assigned, you get an error at compile time - it never even reaches
runtime.&lt;&#x2F;p&gt;
&lt;p&gt;The reason is exactly what you said: reading an uninitialized variable means
you get whatever happens to be in memory at that moment. There&#x27;s a random
value there, and that value can break your program&#x27;s logic.&lt;&#x2F;p&gt;
&lt;p&gt;Consider a hospital software system:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;csharp&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; This code won&amp;#39;t compile in C# - but let&amp;#39;s assume it did&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;int&lt;&#x2F;span&gt;&lt;span class=&quot;z-source&quot;&gt; insulinDose&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;                  &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; not assigned!&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;AdministerInsulin&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-readwrite&quot;&gt;patient&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-readwrite&quot;&gt; insulinDose&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;  &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; applies a random dose&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;If &lt;code&gt;insulinDose&lt;&#x2F;code&gt; reads the previous leftover value in memory (say, 999), the
patient could die. C# puts the &quot;assign before use&quot; rule precisely to prevent
this scenario. C++ offers no such protection - the error silently continues
running.&lt;&#x2F;p&gt;
&lt;p&gt;You can use pointers in C#&#x27;s &lt;code&gt;unsafe&lt;&#x2F;code&gt; context, but that&#x27;s a deliberate choice
and requires the &lt;code&gt;&#x2F;unsafe&lt;&#x2F;code&gt; compiler flag. In normal C# code, you cannot
accidentally read an uninitialized value.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;summary-why-does-c-block-it&quot;&gt;Summary: Why Does C# Block It?&lt;&#x2F;h3&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Situation&lt;&#x2F;th&gt;&lt;th&gt;C++&lt;&#x2F;th&gt;&lt;th&gt;C#&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;Reading uninitialized local variable&lt;&#x2F;td&gt;&lt;td&gt;Compiles, returns random value (undefined behavior)&lt;&#x2F;td&gt;&lt;td&gt;Compile error (CS0165)&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Going outside the stack with a pointer&lt;&#x2F;td&gt;&lt;td&gt;Compiles, segfault or random data&lt;&#x2F;td&gt;&lt;td&gt;Can&#x27;t do it without &lt;code&gt;unsafe&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Array bounds overflow&lt;&#x2F;td&gt;&lt;td&gt;Compiles, undefined behavior&lt;&#x2F;td&gt;&lt;td&gt;Throws &lt;code&gt;IndexOutOfRangeException&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Using a null reference&lt;&#x2F;td&gt;&lt;td&gt;Compiles, segfault&lt;&#x2F;td&gt;&lt;td&gt;Throws &lt;code&gt;NullReferenceException&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;p&gt;The C# runtime performs extra checks to protect you. These checks have a
performance cost (like array bounds checking), but it&#x27;s worth it for safety
and debugging convenience.&lt;&#x2F;p&gt;
&lt;p&gt;Stack properties:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Size:&lt;&#x2F;strong&gt; 1-8 MB per thread. Won&#x27;t fill up unless you use &lt;code&gt;stackalloc&lt;&#x2F;code&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Speed:&lt;&#x2F;strong&gt; Allocation and deallocation is a single CPU instruction (stack pointer movement). Cache-friendly.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Lifetime:&lt;&#x2F;strong&gt; Limited to method scope. When the method ends, the variable dies.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;What lives here:&lt;&#x2F;strong&gt; Value types like &lt;code&gt;int&lt;&#x2F;code&gt;, &lt;code&gt;bool&lt;&#x2F;code&gt;, &lt;code&gt;double&lt;&#x2F;code&gt;, &lt;code&gt;struct&lt;&#x2F;code&gt;, &lt;code&gt;enum&lt;&#x2F;code&gt;. Also the &lt;strong&gt;references&lt;&#x2F;strong&gt; (addresses) of reference types.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The most critical rule of the stack: &lt;strong&gt;nothing whose size is unknown at
compile time can live on the stack.&lt;&#x2F;strong&gt; The stack frame size in a method call
must be calculated in advance.&lt;&#x2F;p&gt;
&lt;p&gt;That&#x27;s why:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;csharp&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;byte&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-source&quot;&gt; buffer&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; new&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; byte&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;4096&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;  &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; buffer reference (8 bytes) on stack, 4096 bytes on heap&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The &lt;code&gt;buffer&lt;&#x2F;code&gt; variable (an 8-byte address) is on the stack. But the 4096 bytes
it points to are on the heap. Because the lifetime of those 4096 bytes might
not end when the method finishes - someone else might still be using them.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;the-real-structure-of-an-array-on-the-heap&quot;&gt;The Real Structure of an Array on the Heap&lt;&#x2F;h3&gt;
&lt;p&gt;So how exactly does &lt;code&gt;byte[4096]&lt;&#x2F;code&gt; sit on the heap? And when you write
&lt;code&gt;buffer[150]&lt;&#x2F;code&gt;, how does CLR find the 150th byte? How does it know the
boundary? Let&#x27;s break it down.&lt;&#x2F;p&gt;
&lt;p&gt;The structure of an array object on the heap:&lt;&#x2F;p&gt;
&lt;pre class=&quot;mermaid&quot;&gt;
flowchart LR
    subgraph Stack[&quot;Stack&quot;]
        U[&quot;buffer = 0x00E100 (starting address of byte[] object on heap)&quot;]
    end
    subgraph Heap[&quot;Heap&quot;]
        direction TB
        subgraph Arr[&quot;byte[] object (address: 0x00E100)&quot;]
            direction LR
            A1[&quot;MethodTable* (0x00A000) (8 bytes, address of Byte[] MethodTable)&quot;]
            A2[&quot;Length = 4096 (4 bytes)&quot;]
            A3[&quot;padding (4 bytes)&quot;]
            A4[&quot;[0] = 0 (1 byte)&quot;]
            A5[&quot;[1] = 0 (1 byte)&quot;]
            A6[&quot;... (4094 bytes)&quot;]
            A7[&quot;[4095] = 0 (1 byte)&quot;]
        end
        subgraph MT[&quot;Byte[] MethodTable (0x00A000)&quot;]
            direction LR
            M1[&quot;BaseSize: 16&quot;]
            M2[&quot;ComponentSize: 1&quot;]
            M3[&quot;ElementType: System.Byte&quot;]
            M4[&quot;Rank: 1&quot;]
            M5[&quot;IsArray: true&quot;]
        end
    end
    U -.-&gt;|&quot;holds the heap address&quot;| Arr
    A1 -.-&gt;|&quot;holds the MethodTable address&quot;| MT
&lt;&#x2F;pre&gt;
&lt;p&gt;MethodTable* (8 bytes): This is the &lt;strong&gt;heap address&lt;&#x2F;strong&gt; of the MethodTable
belonging to the &lt;code&gt;Byte[]&lt;&#x2F;code&gt; type. It&#x27;s a memory address like &lt;code&gt;0x00A000&lt;&#x2F;code&gt;.
CLR reads this address to learn what type the object is, the size of its
elements, its boundaries, and its behavior (methods). Each type has one
MethodTable, and all objects of that type share the same MethodTable.&lt;&#x2F;p&gt;
&lt;p&gt;Notice: the &lt;code&gt;buffer&lt;&#x2F;code&gt; variable on the stack holds the value &lt;code&gt;0x00E100&lt;&#x2F;code&gt;.
This value is the &lt;strong&gt;starting address of the byte[] object on the heap.&lt;&#x2F;strong&gt;
In other words, &lt;code&gt;buffer&lt;&#x2F;code&gt; is the &quot;address card&quot; for that 4096-byte region on
the heap. When you hold this reference, CLR can do the following:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;1. Learn the type:&lt;&#x2F;strong&gt; &lt;code&gt;buffer.GetType()&lt;&#x2F;code&gt; → first goes to the address
0x00E100 that &lt;code&gt;buffer&lt;&#x2F;code&gt; holds, reads the 8-byte MethodTable* value (0x00A000)
there. 0x00A000 is the address of the Byte[] MethodTable. From that
MethodTable, it retrieves &lt;code&gt;IsArray = true&lt;&#x2F;code&gt;, &lt;code&gt;ElementType = System.Byte&lt;&#x2F;code&gt;,
&lt;code&gt;Rank = 1&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;2. Learn the length:&lt;&#x2F;strong&gt; &lt;code&gt;buffer.Length&lt;&#x2F;code&gt; → reads the 4-byte &lt;code&gt;Length&lt;&#x2F;code&gt; value
at offset 8 from 0x00E100. It&#x27;s 4096.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;3. Access an element:&lt;&#x2F;strong&gt; When you write &lt;code&gt;buffer[150]&lt;&#x2F;code&gt;, CLR takes three
controlled steps:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;1. Bounds check:    if (150 &amp;lt; 0 || 150 &amp;gt;= 4096) → throw IndexOutOfRangeException&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;2. Address calc:    target = 0x00E100 + 16 (header) + (150 × 1) (componentSize)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                    = 0x00E100 + 16 + 150&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                    = 0x00E196&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;3. Read&#x2F;Write:      *(byte*)0x00E196&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The formula breakdown:&lt;&#x2F;p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Part&lt;&#x2F;th&gt;&lt;th&gt;Value&lt;&#x2F;th&gt;&lt;th&gt;Source&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Base address&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;0x00E100&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;buffer&lt;&#x2F;code&gt; reference on the stack&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Header size&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;16 bytes&lt;&#x2F;code&gt; (8 MT + 4 Length + 4 padding)&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;BaseSize&lt;&#x2F;code&gt; in Array MethodTable&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;ComponentSize&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;1&lt;&#x2F;code&gt; (for byte)&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;ComponentSize&lt;&#x2F;code&gt; field in Array MethodTable&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Length&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;4096&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;At offset 8 in the object&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;index&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;150&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;Your code&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;p&gt;MethodTable has two more special fields for arrays:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;ComponentSize&lt;&#x2F;code&gt;:&lt;&#x2F;strong&gt; The size of each element in bytes. &lt;code&gt;1&lt;&#x2F;code&gt; for &lt;code&gt;byte[]&lt;&#x2F;code&gt;,
&lt;code&gt;4&lt;&#x2F;code&gt; for &lt;code&gt;int[]&lt;&#x2F;code&gt;, the struct size for a struct array.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;BaseSize&lt;&#x2F;code&gt;:&lt;&#x2F;strong&gt; The size of the array&#x27;s header (MT + Length + padding).
Actual object size = &lt;code&gt;BaseSize + (Length × ComponentSize)&lt;&#x2F;code&gt;.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;CLR extracts everything it needs for &lt;code&gt;buffer[150]&lt;&#x2F;code&gt; from the MethodTable +
Length pair. It doesn&#x27;t need a separate &quot;start&#x2F;length&quot; struct because arrays
always start at index 0. &lt;code&gt;start&lt;&#x2F;code&gt; is always 0.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;start-length-in-span-t&quot;&gt;Start&#x2F;Length in Span&amp;lt;T&amp;gt;&lt;&#x2F;h3&gt;
&lt;p&gt;What you described as &quot;a struct type with start and length values&quot; is actually
&lt;code&gt;Span&amp;lt;T&amp;gt;&lt;&#x2F;code&gt;. Span is a view struct that works &lt;strong&gt;on top of&lt;&#x2F;strong&gt; an array:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;csharp&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;byte&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-source&quot;&gt; fullBuffer&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; new&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; byte&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;4096&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;Span&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-typeparameters&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;byte&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-typeparameters&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-source&quot;&gt; slice&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-source&quot;&gt; fullBuffer&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;AsSpan&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;100&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 50&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;  &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; start at byte 100, take 50 bytes&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Span itself is a struct (value type) living on the stack and contains:&lt;&#x2F;p&gt;
&lt;pre class=&quot;mermaid&quot;&gt;
flowchart LR
    subgraph Stack[&quot;Stack&quot;]
        direction TB
        subgraph Span[&quot;Span&amp;lt;byte&amp;gt; slice (stack)&quot;]
            S1[&quot;_reference (8 bytes, holds heap address of element 100)&quot;]
            S2[&quot;_length = 50 (4 bytes)&quot;]
        end
    end
    subgraph Heap[&quot;Heap&quot;]
        direction TB
        subgraph Arr[&quot;byte[] fullBuffer (0x00E100)&quot;]
            direction LR
            MT[&quot;MethodTable* (8)&quot;]
            LEN[&quot;Length = 4096 (4)&quot;]
            PAD[&quot;padding (4)&quot;]
            EL0[&quot;[0]...[99] (100 bytes, header + 0..99)&quot;]
            EL100[&quot;[100] = slice start&quot;]
            EL149[&quot;[149] = slice end&quot;]
            EL150[&quot;[150]...[4095]&quot;]
        end
    end
    S1 -.-&gt;|&quot;skips the header, points directly to element 100&quot;| EL100
&lt;&#x2F;pre&gt;
&lt;p&gt;Span&#x27;s &lt;code&gt;_reference&lt;&#x2F;code&gt; field does not point to the start of the array, but to the
&lt;strong&gt;start of the slice&lt;&#x2F;strong&gt; (with the offset already added). When indexing on a
Span, CLR does:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;1. Slice bounds:     if (index &amp;lt; 0 || index &amp;gt;= 50) → error (Span&amp;#39;s _length)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;2. Actual address:   target = _reference + (index × 1)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;As you can see, Span carries the &quot;start and length&quot; information. But
&lt;code&gt;byte[]&lt;&#x2F;code&gt; itself does not - an array always starts at 0, and its length is
embedded in the object. The start&#x2F;length pair comes into play when a view is
needed.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;where-does-the-element-type-come-from&quot;&gt;Where Does the Element Type Come From?&lt;&#x2F;h3&gt;
&lt;p&gt;When reading &lt;code&gt;buffer[150]&lt;&#x2F;code&gt;, CLR knows the 150th byte is a &lt;code&gt;byte&lt;&#x2F;code&gt; from the
&lt;strong&gt;ComponentSize&lt;&#x2F;strong&gt; value. The &lt;code&gt;Byte[]&lt;&#x2F;code&gt; MethodTable has &lt;code&gt;ComponentSize = 1&lt;&#x2F;code&gt;.
For &lt;code&gt;Int32[]&lt;&#x2F;code&gt; it&#x27;s 4, for &lt;code&gt;Double[]&lt;&#x2F;code&gt; it&#x27;s 8.&lt;&#x2F;p&gt;
&lt;p&gt;What if the element is a reference type? Consider &lt;code&gt;string[]&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;csharp&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;string&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-source&quot;&gt; names&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; new&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; string&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;3&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This array&#x27;s structure on the heap:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;[MethodTable* → String[]]    (8 bytes)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;[Length = 3]                 (4 bytes)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;[padding]                    (4 bytes)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;[names[0] = null]            (8 bytes - string reference)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;[names[1] = null]            (8 bytes - string reference)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;[names[2] = null]            (8 bytes - string reference)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Total: 16 + (3 × 8) = 40 bytes&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Here, &lt;code&gt;ComponentSize = 8&lt;&#x2F;code&gt; (reference size on 64-bit). Each element is a
reference; the string itself is elsewhere on the heap. The array only holds
addresses. When reading &lt;code&gt;names[1]&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;1. Address:   0x00E100 + 16 + (1 × 8) = 0x00E118&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;2. Read:      *(string*)0x00E118 → 0x00F200 (address of the string)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;3. Go to string: read the string object at 0x00F200&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;GC also tracks these references. As long as the &lt;code&gt;string[]&lt;&#x2F;code&gt; array itself is
alive, the strings pointed to by the array&#x27;s references also stay alive (even
if nothing else references them).&lt;&#x2F;p&gt;
&lt;h2 id=&quot;heap-and-methodtable&quot;&gt;Heap and MethodTable&lt;&#x2F;h2&gt;
&lt;p&gt;Think of the heap as a large &lt;strong&gt;plot of land.&lt;&#x2F;strong&gt; When you need space, you
allocate a parcel (allocation); when you&#x27;re done, you clear it (GC collects).
Unlike the stack, there&#x27;s no order here - each parcel is independent.&lt;&#x2F;p&gt;
&lt;p&gt;But the real question is what CLR does behind the scenes when an object is
created on the heap. Let&#x27;s step through what happens the moment you write
&lt;code&gt;new User()&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;what-happens-when-clr-creates-an-object&quot;&gt;What Happens When CLR Creates an Object?&lt;&#x2F;h3&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;csharp&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;var&lt;&#x2F;span&gt;&lt;span class=&quot;z-source&quot;&gt; user&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; new&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; User&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; {&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-readwrite&quot;&gt; Name&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;Erdinc&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-readwrite&quot;&gt; Age&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 30&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; }&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;When this line executes, CLR follows these steps:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Look up type info.&lt;&#x2F;strong&gt; Does CLR know the &lt;code&gt;User&lt;&#x2F;code&gt; type? Where is the
&lt;code&gt;MethodTable&lt;&#x2F;code&gt; for the &lt;code&gt;User&lt;&#x2F;code&gt; class? (If it&#x27;s the first use, the type is
loaded - type load.)&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Allocate memory.&lt;&#x2F;strong&gt; It looks at the &lt;code&gt;BaseSize&lt;&#x2F;code&gt; field in the &lt;code&gt;MethodTable&lt;&#x2F;code&gt;.
This field tells how many bytes an object of type &lt;code&gt;User&lt;&#x2F;code&gt; will occupy on
the heap. That many bytes are allocated from the GC heap.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Write the MethodTable pointer.&lt;&#x2F;strong&gt; The address of the &lt;code&gt;MethodTable&lt;&#x2F;code&gt; is
written to the first 8 bytes (64-bit) or 4 bytes (32-bit) of the allocated
memory. This pointer (TypeHandle) is a critical reference that tells the
runtime which type the object belongs to.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Zero out fields.&lt;&#x2F;strong&gt; The remaining space is zeroed. &lt;code&gt;int&lt;&#x2F;code&gt; → 0, &lt;code&gt;string&lt;&#x2F;code&gt; → null,
&lt;code&gt;bool&lt;&#x2F;code&gt; → false.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Call constructor.&lt;&#x2F;strong&gt; The &lt;code&gt;User&lt;&#x2F;code&gt; constructor runs, writing the actual values
to the fields.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Return the reference.&lt;&#x2F;strong&gt; The &lt;strong&gt;starting address&lt;&#x2F;strong&gt; of this heap-allocated
object is assigned to the &lt;code&gt;var user&lt;&#x2F;code&gt; variable. This address is stored on
the stack.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;Visually:&lt;&#x2F;p&gt;
&lt;pre class=&quot;mermaid&quot;&gt;
flowchart LR
    subgraph Stack[&quot;Stack&quot;]
        direction TB
        U[&quot;user = 0x001A3F (8 bytes)&quot;]
    end

    subgraph Heap[&quot;Heap&quot;]
        direction TB
        subgraph Obj[&quot;User object (address: 0x001A3F)&quot;]
            direction LR
            MT[&quot;MethodTable* (8 bytes)&quot;]
            F1[&quot;Age = 30 (4 bytes)&quot;]
            PAD[&quot;padding (4 bytes)&quot;]
            F2[&quot;Name = 0x00B210 (8 bytes)&quot;]
        end
        subgraph MTBlock[&quot;User MethodTable&quot;]
            direction LR
            MTInfo[&quot;BaseSize: 24
            EEClass*: 0xF0A100
            Parent MethodTable*: System.Object
            Interface count: 0
            Method slots: ...&quot;]
        end
        subgraph Str[&quot;String &#x27;Erdinc&#x27; (0x00B210)&quot;]
            direction LR
            SM[&quot;MethodTable* (System.String)&quot;]
            SL[&quot;Length = 6&quot;]
            SC[&quot;chars: E r d i n c&quot;]
        end
    end

    U -.-&gt;|&quot;points to&quot;| Obj
    MT -.-&gt;|&quot;type info&quot;| MTBlock
    F2 -.-&gt;|&quot;Name points to&quot;| Str
&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;what-is-methodtable-and-what-does-it-do&quot;&gt;What is MethodTable and What Does It Do?&lt;&#x2F;h3&gt;
&lt;p&gt;Every type has a &lt;strong&gt;MethodTable.&lt;&#x2F;strong&gt; CLR creates this table when the type is
first loaded, and all objects of that type share the same MethodTable. Even
if you create a million &lt;code&gt;User&lt;&#x2F;code&gt; objects, there is only &lt;strong&gt;one&lt;&#x2F;strong&gt; &lt;code&gt;User&lt;&#x2F;code&gt;
MethodTable.&lt;&#x2F;p&gt;
&lt;p&gt;MethodTable contents (simplified):&lt;&#x2F;p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Field&lt;&#x2F;th&gt;&lt;th&gt;Description&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;BaseSize&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td&gt;How many bytes an object of this type occupies on the heap. The first field GC uses for allocation.&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;EEClass pointer&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td&gt;Points to the &lt;code&gt;EEClass&lt;&#x2F;code&gt; structure. Field offsets, interface list, property metadata live here.&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Parent MethodTable&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td&gt;The inheritance chain. If &lt;code&gt;User&lt;&#x2F;code&gt;, its parent is &lt;code&gt;System.Object&lt;&#x2F;code&gt;&#x27;s MethodTable.&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Interface count and Interface map&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td&gt;Which interfaces it implements. Used for casts and &lt;code&gt;is&lt;&#x2F;code&gt; checks.&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Method slots&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td&gt;Addresses of virtual methods. Like &lt;code&gt;ToString()&lt;&#x2F;code&gt;, &lt;code&gt;GetHashCode()&lt;&#x2F;code&gt;.&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;p&gt;So when you call &lt;code&gt;user.GetType()&lt;&#x2F;code&gt;, CLR&#x27;s only job is to read the MethodTable
pointer from the first 8 bytes of the object and return the corresponding
&lt;code&gt;Type&lt;&#x2F;code&gt; object to you.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;namespace-and-methodtable-relationship&quot;&gt;Namespace and MethodTable Relationship&lt;&#x2F;h3&gt;
&lt;p&gt;MethodTables are created &lt;strong&gt;per type&lt;&#x2F;strong&gt;, not per namespace. A namespace is a
logical grouping used at compile time. At runtime, there is no such thing as a
&quot;namespace MethodTable.&quot;&lt;&#x2F;p&gt;
&lt;p&gt;A type&#x27;s full runtime identity consists of three parts:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Assembly + Namespace + TypeName&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;For example:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;csharp&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;namespace&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; MyApp&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;Models&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;{&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-modifier&quot;&gt;    public&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt; class&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; User&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; {&lt;&#x2F;span&gt;&lt;span class=&quot;z-source&quot;&gt; ... &lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;}&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;     &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; Identity: MyApp.dll + MyApp.Models + User&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;namespace&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; MyApp&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;DTOs&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;{&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-modifier&quot;&gt;    public&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt; class&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; User&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; {&lt;&#x2F;span&gt;&lt;span class=&quot;z-source&quot;&gt; ... &lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;}&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;     &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; Identity: MyApp.dll + MyApp.DTOs + User&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;These two &lt;code&gt;User&lt;&#x2F;code&gt; classes have the same name but different namespaces.
Therefore, CLR creates &lt;strong&gt;two separate MethodTables&lt;&#x2F;strong&gt; for them. Each has its
own &lt;code&gt;BaseSize&lt;&#x2F;code&gt;, field offsets, and method slots. &lt;code&gt;Models.User&lt;&#x2F;code&gt; and
&lt;code&gt;DTOs.User&lt;&#x2F;code&gt; are completely different types that just happen to share a name.&lt;&#x2F;p&gt;
&lt;p&gt;Namespace information is not stored as a separate field inside the
MethodTable; it&#x27;s held as &lt;strong&gt;part of the type&#x27;s name.&lt;&#x2F;strong&gt; When you call
&lt;code&gt;Type.FullName&lt;&#x2F;code&gt;, you get &lt;code&gt;&quot;MyApp.Models.User&quot;&lt;&#x2F;code&gt;; this string comes from the
type&#x27;s metadata. CLR uses this full name when loading a type:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;1. Does the assembly contain a type called &amp;quot;MyApp.Models.User&amp;quot;?&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;2. If yes, load its MethodTable (or use it if already loaded)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;3. If not, throw TypeLoadException&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;So, the namespace is necessary for type resolution, but the MethodTable itself
is type-based, not namespace-based. 50 classes in the same namespace = 50
separate MethodTables.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;accessing-properties-the-offset-mechanism&quot;&gt;Accessing Properties: The Offset Mechanism&lt;&#x2F;h3&gt;
&lt;p&gt;When you write &lt;code&gt;user.Name&lt;&#x2F;code&gt;, how does CLR know which bytes in the heap object
belong to the &lt;code&gt;Name&lt;&#x2F;code&gt; property? Answer: &lt;strong&gt;field offset.&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;The &lt;code&gt;EEClass&lt;&#x2F;code&gt; holds an offset value for each field:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;User Class EEClass (simplified):&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  Field: Age      → offset: 8   (right after the MethodTable pointer)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  Field: Name     → offset: 16  (Age 4 bytes + padding 4 bytes = 8 bytes later)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  Total size      → 24 bytes    (8 MT + 4 Age + 4 pad + 8 Name)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Why is there padding? The CPU requires &lt;strong&gt;alignment&lt;&#x2F;strong&gt; for faster memory access.
On a 64-bit system, 8-byte references must be at addresses that are multiples
of 8. That&#x27;s why 4 bytes of padding are inserted between &lt;code&gt;Age&lt;&#x2F;code&gt; (4 bytes) and
&lt;code&gt;Name&lt;&#x2F;code&gt; (8 bytes).&lt;&#x2F;p&gt;
&lt;p&gt;CLR uses these offset values to translate the &lt;code&gt;user.Name&lt;&#x2F;code&gt; call into:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;csharp&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; user.Name is actually this:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;string&lt;&#x2F;span&gt;&lt;span class=&quot;z-source&quot;&gt; name&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; *&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;string&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;*&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;byte&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;*&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-readwrite&quot;&gt;user&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; +&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 16&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;  &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; read the reference at byte 16 of the object&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;strong&gt;The advantage of offset access:&lt;&#x2F;strong&gt; There&#x27;s no need to maintain a separate
property address map for each object. All &lt;code&gt;User&lt;&#x2F;code&gt; objects use the same offsets.
Only the base address changes: &lt;code&gt;object_address + Age_offset = Age&#x27;s address&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Note: Since &lt;code&gt;Age&lt;&#x2F;code&gt; is a value type, its value is &lt;strong&gt;inside&lt;&#x2F;strong&gt; the heap object.
But since &lt;code&gt;Name&lt;&#x2F;code&gt; is a reference type, only its &lt;strong&gt;address&lt;&#x2F;strong&gt; is inside the heap
object. The actual string data is at a different heap address.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;offset-in-inheritance&quot;&gt;Offset in Inheritance&lt;&#x2F;h3&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;csharp&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;class&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; Person&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;{&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-modifier&quot;&gt;    public&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; int&lt;&#x2F;span&gt;&lt;span class=&quot;z-source&quot;&gt; Id&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; {&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt; get&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt; set&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;class&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; User&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; :&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; Person&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;{&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-modifier&quot;&gt;    public&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; string&lt;&#x2F;span&gt;&lt;span class=&quot;z-source&quot;&gt; Name&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; {&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt; get&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt; set&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-modifier&quot;&gt;    public&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; int&lt;&#x2F;span&gt;&lt;span class=&quot;z-source&quot;&gt; Age&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; {&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt; get&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt; set&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The &lt;code&gt;User&lt;&#x2F;code&gt; object on the heap:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;[MethodTable* → User]  (8 bytes, at the very start)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;[Id: int]              (offset 8, field inherited from Person)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;[Name: string ref]     (offset 16, 4 bytes Id + 4 padding = 8)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;[Age: int]             (offset 24)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Total: 32 bytes&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The critical point here: parent class fields always come &lt;strong&gt;before&lt;&#x2F;strong&gt; child
class fields. This way, even when you do &lt;code&gt;Person person = user;&lt;&#x2F;code&gt;, &lt;code&gt;person.Id&lt;&#x2F;code&gt;
is read from the same offset. Because the &lt;code&gt;Person&lt;&#x2F;code&gt; MethodTable also knows
&lt;code&gt;Id&lt;&#x2F;code&gt; is at byte 8 - and at byte 8 of the &lt;code&gt;user&lt;&#x2F;code&gt; object, &lt;code&gt;Id&lt;&#x2F;code&gt; really is there.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;value-type-vs-reference-type-summary-table&quot;&gt;Value Type vs Reference Type: Summary Table&lt;&#x2F;h2&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Property&lt;&#x2F;th&gt;&lt;th&gt;Value Type (&lt;code&gt;struct&lt;&#x2F;code&gt;, &lt;code&gt;int&lt;&#x2F;code&gt;, &lt;code&gt;bool&lt;&#x2F;code&gt;)&lt;&#x2F;th&gt;&lt;th&gt;Reference Type (&lt;code&gt;class&lt;&#x2F;code&gt;, &lt;code&gt;string&lt;&#x2F;code&gt;)&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Where it lives&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td&gt;Where its owner lives (stack or inside heap)&lt;&#x2F;td&gt;&lt;td&gt;Always on the heap&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Assignment behavior&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td&gt;Value is copied&lt;&#x2F;td&gt;&lt;td&gt;Reference (address) is copied&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Can be null?&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td&gt;No (yes with &lt;code&gt;int?&lt;&#x2F;code&gt; nullable wrapper)&lt;&#x2F;td&gt;&lt;td&gt;Yes&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Default value&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td&gt;Zeroed state (&lt;code&gt;0&lt;&#x2F;code&gt;, &lt;code&gt;false&lt;&#x2F;code&gt;, &lt;code&gt;default&lt;&#x2F;code&gt;)&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;null&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Inheritance&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td&gt;From &lt;code&gt;System.ValueType&lt;&#x2F;code&gt;, sealed&lt;&#x2F;td&gt;&lt;td&gt;From &lt;code&gt;Object&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;GC impact&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td&gt;Goes away automatically when owner is collected&lt;&#x2F;td&gt;&lt;td&gt;Requires GC collection&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;How it sits in an object&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td&gt;Value is inside the object&lt;&#x2F;td&gt;&lt;td&gt;Address is inside the object&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;h2 id=&quot;the-string-matter&quot;&gt;The String Matter&lt;&#x2F;h2&gt;
&lt;p&gt;String has a special place in this story. It&#x27;s the type that confuses people
the most.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;csharp&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;string&lt;&#x2F;span&gt;&lt;span class=&quot;z-source&quot;&gt; a&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;hello&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;string&lt;&#x2F;span&gt;&lt;span class=&quot;z-source&quot;&gt; b&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-readwrite&quot;&gt; a&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other z-readwrite&quot;&gt;b&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;world&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; a is still &amp;quot;hello&amp;quot;, right? Yes.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;String is a reference type - it lives on the heap. But it&#x27;s &lt;strong&gt;immutable.&lt;&#x2F;strong&gt;
That&#x27;s why the &lt;code&gt;b = &quot;world&quot;&lt;&#x2F;code&gt; assignment above doesn&#x27;t affect &lt;code&gt;a&lt;&#x2F;code&gt; - it doesn&#x27;t
change the content of the string &lt;code&gt;b&lt;&#x2F;code&gt; points to; it assigns the address of a
new string to &lt;code&gt;b&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;In fact, the heap structure of a string also starts with a MethodTable:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;String object (heap):&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  [MethodTable* → System.String]  (8 bytes)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  [Length: int = 5]               (4 bytes)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  [Padding]                       (4 bytes)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  [First Char: &amp;#39;h&amp;#39;]               (2 bytes)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  [Second Char: &amp;#39;e&amp;#39;]              (2 bytes)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  ...&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;code&gt;Length&lt;&#x2F;code&gt; and characters are inside the object, accessible at fixed offsets.
But &lt;strong&gt;you cannot change the content&lt;&#x2F;strong&gt; because CLR doesn&#x27;t know the string is
writable - the object is marked as readonly.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;why-immutable&quot;&gt;Why Immutable?&lt;&#x2F;h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Thread safety.&lt;&#x2F;strong&gt; Multiple threads can read the same string without fear.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Interning.&lt;&#x2F;strong&gt; Strings with the same content are stored once in memory.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Security.&lt;&#x2F;strong&gt; Just because you passed a string as a parameter doesn&#x27;t mean
the method can modify it.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;But this immutability comes at a cost:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;csharp&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;string&lt;&#x2F;span&gt;&lt;span class=&quot;z-source&quot;&gt; result&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;for&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; (&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;int&lt;&#x2F;span&gt;&lt;span class=&quot;z-source&quot;&gt; i&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 0&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-readwrite&quot;&gt; i&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; &amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 10000&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-readwrite&quot;&gt; i&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;++&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;{&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other z-readwrite&quot;&gt;    result&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; +=&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;x&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;   &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; New string object every loop. 10,000 allocations.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This code creates 10,000 new &lt;code&gt;string&lt;&#x2F;code&gt; objects, polluting the heap. The right
way:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;csharp&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;var&lt;&#x2F;span&gt;&lt;span class=&quot;z-source&quot;&gt; sb&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; new&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; StringBuilder&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;for&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; (&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;int&lt;&#x2F;span&gt;&lt;span class=&quot;z-source&quot;&gt; i&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 0&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-readwrite&quot;&gt; i&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; &amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 10000&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-readwrite&quot;&gt; i&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;++&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;{&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-source&quot;&gt;    sb&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;Append&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;x&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;string&lt;&#x2F;span&gt;&lt;span class=&quot;z-source&quot;&gt; result&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-source&quot;&gt; sb&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;ToString&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;  &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; Single allocation.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Even though string is a reference type, it behaves like a value type. The &lt;code&gt;==&lt;&#x2F;code&gt;
operator compares content (not address). Copying is safe because it&#x27;s
immutable. But in memory, it&#x27;s always &lt;strong&gt;on the heap&lt;&#x2F;strong&gt; - it can&#x27;t live on the
stack because its size is unknown at compile time and its lifetime can exceed
method scope.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;string-interning&quot;&gt;String Interning&lt;&#x2F;h3&gt;
&lt;p&gt;CLR keeps frequently used strings in a pool called the &lt;strong&gt;intern pool:&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;csharp&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;string&lt;&#x2F;span&gt;&lt;span class=&quot;z-source&quot;&gt; a&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;dotnet&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;string&lt;&#x2F;span&gt;&lt;span class=&quot;z-source&quot;&gt; b&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;dotnet&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-source&quot;&gt;Console&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;WriteLine&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-source&quot;&gt;Object&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;ReferenceEquals&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-readwrite&quot;&gt;a&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-readwrite&quot;&gt; b&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt; &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; True - same object.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Strings that are constant at compile time are automatically interned. You can
also do it manually at runtime:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;csharp&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;string&lt;&#x2F;span&gt;&lt;span class=&quot;z-source&quot;&gt; c&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; new&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; string&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;new&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; char&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; {&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; &amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;d&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; &amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;o&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; &amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;t&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; &amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;n&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; &amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;e&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; &amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;t&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; }&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;string&lt;&#x2F;span&gt;&lt;span class=&quot;z-source&quot;&gt; d&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; string&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;Intern&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-readwrite&quot;&gt;c&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;So when a method returns a string at runtime, does interning happen
automatically? Two scenarios:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Scenario A: Method returns a compile-time literal - interned.&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;csharp&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;string&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; GetHelloWorld&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;{&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    return&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;Merhaba dunya&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;   &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; compile-time literal&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;string&lt;&#x2F;span&gt;&lt;span class=&quot;z-source&quot;&gt; a&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;Merhaba dunya&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;string&lt;&#x2F;span&gt;&lt;span class=&quot;z-source&quot;&gt; b&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; GetHelloWorld&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-source&quot;&gt;Object&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;ReferenceEquals&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-readwrite&quot;&gt;a&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-readwrite&quot;&gt; b&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt; &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; True - both are the same intern object&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The &lt;code&gt;&quot;Merhaba dunya&quot;&lt;&#x2F;code&gt; literal in &lt;code&gt;GetHelloWorld()&lt;&#x2F;code&gt;&#x27;s body is embedded in the
assembly metadata. When CLR loads the assembly, it also adds it to the intern
pool. When the method is called, it returns the same object from the pool. If
two literals are concatenated with &lt;code&gt;+&lt;&#x2F;code&gt; and the compiler can optimize them into
a single literal at build time, the result is still interned.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Scenario B: Method creates the string at runtime - not interned.&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;csharp&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;string&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; GetHelloWorld&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;{&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;    var&lt;&#x2F;span&gt;&lt;span class=&quot;z-source&quot;&gt; merhaba&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;Merhaba &lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;    var&lt;&#x2F;span&gt;&lt;span class=&quot;z-source&quot;&gt; dunya&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;dunya&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    return&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-readwrite&quot;&gt; merhaba&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; +&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-readwrite&quot;&gt; dunya&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;   &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; runtime concat, new object&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;string&lt;&#x2F;span&gt;&lt;span class=&quot;z-source&quot;&gt; a&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;Merhaba dunya&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;string&lt;&#x2F;span&gt;&lt;span class=&quot;z-source&quot;&gt; b&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; GetHelloWorld&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-source&quot;&gt;Object&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;ReferenceEquals&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-readwrite&quot;&gt;a&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-readwrite&quot;&gt; b&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt; &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; False - b was created at runtime, not in the intern pool&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;string&lt;&#x2F;span&gt;&lt;span class=&quot;z-source&quot;&gt; c&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; string&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;Intern&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-readwrite&quot;&gt;b&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;  &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; Manual intern&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-source&quot;&gt;Object&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;ReferenceEquals&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-readwrite&quot;&gt;a&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-readwrite&quot;&gt; c&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt; &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; True - now it&amp;#39;s the same pool object&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Concatenation with &lt;code&gt;+&lt;&#x2F;code&gt; using variables, &lt;code&gt;StringBuilder.ToString()&lt;&#x2F;code&gt;,
&lt;code&gt;Substring()&lt;&#x2F;code&gt;, &lt;code&gt;ToUpper()&lt;&#x2F;code&gt;, reading from a file, JSON deserialization - all
of these create new heap objects at runtime. Even if the content is the same,
it doesn&#x27;t automatically enter the intern pool. You need to call
&lt;code&gt;string.Intern()&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;In short, the issue is not the string&#x27;s &lt;strong&gt;content&lt;&#x2F;strong&gt;, but &lt;strong&gt;how it was
created.&lt;&#x2F;strong&gt; We&#x27;ll look into this more later.&lt;&#x2F;p&gt;
&lt;p&gt;Interning prevents the same string from occupying heap space multiple times.
But excessive use can bloat GC&#x27;s Gen 2 - because interned strings are
&lt;strong&gt;never&lt;&#x2F;strong&gt; collected by the GC (they live until the AppDomain shuts down).&lt;&#x2F;p&gt;
&lt;h2 id=&quot;summary-stack-heap-methodtable-relationship&quot;&gt;Summary: Stack, Heap, MethodTable Relationship&lt;&#x2F;h2&gt;
&lt;p&gt;The complete journey of a &lt;code&gt;new User()&lt;&#x2F;code&gt; call from start to finish:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;1. CLR looks at the User MethodTable → BaseSize: 24 bytes&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;2. Allocates 24 bytes from the GC heap&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;3. Writes the User MethodTable address to the first 8 bytes of allocated space&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;4. Zeroes the remaining space, calls the constructor&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;5. Assigns the heap address to the user variable on the stack&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;6. user.Name call: reads the string address at offset 16 from the user address&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;When you understand this mechanism, you start understanding most performance
problems too. Every &lt;code&gt;new&lt;&#x2F;code&gt; is an allocation. GC has an &lt;strong&gt;allocation budget&lt;&#x2F;strong&gt;
in its Gen 0 region; creating a new object consumes this budget. When the
budget hits zero, GC is triggered and collects unused objects. So every
allocation brings you one step closer to the next GC cycle. Every unnecessary
allocation is a preventable cost.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;what-s-next&quot;&gt;What&#x27;s Next&lt;&#x2F;h2&gt;
&lt;p&gt;We&#x27;ve covered what stack and heap are, what CLR does when creating objects,
the role of MethodTable, and why string is special. But the real question is:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;&quot;How do we avoid heap allocations when working with strings?&quot;&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;The answer: &lt;code&gt;Span&amp;lt;T&amp;gt;&lt;&#x2F;code&gt;, &lt;code&gt;Memory&amp;lt;T&amp;gt;&lt;&#x2F;code&gt;, &lt;code&gt;stackalloc&lt;&#x2F;code&gt;, and the &lt;code&gt;IDisposable&lt;&#x2F;code&gt;
pattern. In the next part of this series, I&#x27;ll cover these with code examples.
We&#x27;ll also look at GC&#x27;s generation mechanism, why finalizers are dangerous,
and how to catch memory leaks.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;references&quot;&gt;References&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;learn.microsoft.com&#x2F;en-us&#x2F;dotnet&#x2F;standard&#x2F;garbage-collection&#x2F;&quot;&gt;Microsoft Docs - Memory management and garbage collection&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;devblogs.microsoft.com&#x2F;dotnet&#x2F;all-about-span-exploring-a-new-net-mainstay&#x2F;&quot;&gt;Stephen Toub - All About Span: Exploring .NET Memory&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;dotnet&#x2F;runtime&#x2F;blob&#x2F;main&#x2F;src&#x2F;coreclr&#x2F;vm&#x2F;methodtable.h&quot;&gt;.NET Runtime - MethodTable source&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;dotnet&#x2F;runtime&#x2F;blob&#x2F;main&#x2F;docs&#x2F;design&#x2F;coreclr&#x2F;botr&#x2F;object-layout.md&quot;&gt;.NET Runtime - Object layout&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;learn.microsoft.com&#x2F;en-us&#x2F;archive&#x2F;blogs&#x2F;ericlippert&#x2F;the-stack-is-an-implementation-detail-part-one&quot;&gt;Eric Lippert - The Stack Is An Implementation Detail&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;script src=&quot;https:&#x2F;&#x2F;cdn.jsdelivr.net&#x2F;npm&#x2F;mermaid@11&#x2F;dist&#x2F;mermaid.min.js&quot;&gt;&lt;&#x2F;script&gt;
&lt;script&gt;
  mermaid.initialize({
    startOnLoad: true,
    theme: &#x27;neutral&#x27;,
    flowchart: { nodeSpacing: 30, rankSpacing: 40 },
    themeVariables: { fontSize: &#x27;15px&#x27; }
  });
&lt;&#x2F;script&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Welcome</title>
        <published>2026-06-15T00:00:00+00:00</published>
        <updated>2026-06-15T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://erdincyasan.com/en/blog/welcome/"/>
        <id>https://erdincyasan.com/en/blog/welcome/</id>
        
        <content type="html" xml:base="https://erdincyasan.com/en/blog/welcome/">&lt;p&gt;Hi again, I&#x27;m Erdinç Yaşan.&lt;&#x2F;p&gt;
&lt;p&gt;So far I&#x27;ve built a lot of sites; spun servers up and tore them down. Most
of the time the reason was instability, and partly restlessness, that
constant urge to try something new. In the end I made a different decision: I
came to believe that what really matters is &lt;strong&gt;stability&lt;&#x2F;strong&gt;, and I built this
static site with that intent.&lt;&#x2F;p&gt;
&lt;p&gt;My general goal here is two things: to keep track of the work I do day to
day, and to write up the content and experiments that influenced me, in my
own words.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;writing-in-the-ai-age&quot;&gt;Writing in the AI age&lt;&#x2F;h2&gt;
&lt;p&gt;In this age it&#x27;s hard not to wonder whether a piece of writing was done with
AI or by hand. Let me make this clear up front: the projects here were
thought up by me, but parts may have been written with AI. The writing is
different.&lt;&#x2F;p&gt;
&lt;p&gt;On this personal site there will &lt;strong&gt;never be a post produced directly by
AI.&lt;&#x2F;strong&gt; While creating my content I&#x27;ll lean on AI to weigh different points of
view, but the final word is my own perspective, and I&#x27;m the one who puts it
into writing.&lt;&#x2F;p&gt;
&lt;p&gt;The rule is the same on the code side: if my name is on a commit, I wrote
that code. Not AI.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;editors-change-software-doesn-t-die&quot;&gt;Editors change, software doesn&#x27;t die&lt;&#x2F;h2&gt;
&lt;p&gt;At some point AI will replace editors. In fact, with this revolution editors
have already started shifting shape and terminals have started changing. But
software doesn&#x27;t die.&lt;&#x2F;p&gt;
&lt;p&gt;I have one worry. We used to be taught how to solve errors, how to use
tooltips, the refactoring conveniences inside the editor. We can&#x27;t learn
these anymore. When a problem comes up, we no longer know where to look
first. My observation is this: the old hands don&#x27;t use AI to find the root
cause of a bug.&lt;&#x2F;p&gt;
&lt;p&gt;That&#x27;s exactly why this site will be a record of my intent to keep thinking
with my own head while using AI as a tool. Welcome.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>The 14 Habits of Highly Productive Developers</title>
        <published>2026-06-15T00:00:00+00:00</published>
        <updated>2026-06-15T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://erdincyasan.com/en/library/14-habits-of-highly-productive-developers/"/>
        <id>https://erdincyasan.com/en/library/14-habits-of-highly-productive-developers/</id>
        
        <content type="html" xml:base="https://erdincyasan.com/en/library/14-habits-of-highly-productive-developers/">&lt;p&gt;I read this before building the site, so I do not have my notes here yet. I will add them soon.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Agile for Dummies</title>
        <published>2026-06-15T00:00:00+00:00</published>
        <updated>2026-06-15T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://erdincyasan.com/en/library/agile-for-dummies/"/>
        <id>https://erdincyasan.com/en/library/agile-for-dummies/</id>
        
        <content type="html" xml:base="https://erdincyasan.com/en/library/agile-for-dummies/">&lt;p&gt;I read this before building the site, so I do not have my notes here yet. I will add them soon.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Agile Principles, Patterns, and Practices in C#</title>
        <published>2026-06-15T00:00:00+00:00</published>
        <updated>2026-06-15T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://erdincyasan.com/en/library/agile-ppp-csharp/"/>
        <id>https://erdincyasan.com/en/library/agile-ppp-csharp/</id>
        
        <content type="html" xml:base="https://erdincyasan.com/en/library/agile-ppp-csharp/">&lt;p&gt;I read this before building the site, so I do not have my notes here yet. I will add them soon.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>ASP.NET 8 Best Practices</title>
        <published>2026-06-15T00:00:00+00:00</published>
        <updated>2026-06-15T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://erdincyasan.com/en/library/aspnet-8-best-practices/"/>
        <id>https://erdincyasan.com/en/library/aspnet-8-best-practices/</id>
        
        <content type="html" xml:base="https://erdincyasan.com/en/library/aspnet-8-best-practices/">&lt;p&gt;I read this before building the site, so I do not have my notes here yet. I will add them soon.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Clean Architecture</title>
        <published>2026-06-15T00:00:00+00:00</published>
        <updated>2026-06-15T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://erdincyasan.com/en/library/clean-architecture/"/>
        <id>https://erdincyasan.com/en/library/clean-architecture/</id>
        
        <content type="html" xml:base="https://erdincyasan.com/en/library/clean-architecture/">&lt;p&gt;I read this before building the site, so I do not have my notes here yet. I will add them soon.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Clean Code</title>
        <published>2026-06-15T00:00:00+00:00</published>
        <updated>2026-06-15T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://erdincyasan.com/en/library/clean-code/"/>
        <id>https://erdincyasan.com/en/library/clean-code/</id>
        
        <content type="html" xml:base="https://erdincyasan.com/en/library/clean-code/">&lt;p&gt;I read this before building the site, so I do not have my notes here yet. I will add them soon.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>C# 12 and .NET 8: Modern Cross-Platform Development Fundamentals</title>
        <published>2026-06-15T00:00:00+00:00</published>
        <updated>2026-06-15T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://erdincyasan.com/en/library/csharp-12-and-net-8/"/>
        <id>https://erdincyasan.com/en/library/csharp-12-and-net-8/</id>
        
        <content type="html" xml:base="https://erdincyasan.com/en/library/csharp-12-and-net-8/">&lt;p&gt;I read this before building the site, so I do not have my notes here yet. I will add them soon.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Domain-Driven Design</title>
        <published>2026-06-15T00:00:00+00:00</published>
        <updated>2026-06-15T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://erdincyasan.com/en/library/domain-driven-design/"/>
        <id>https://erdincyasan.com/en/library/domain-driven-design/</id>
        
        <content type="html" xml:base="https://erdincyasan.com/en/library/domain-driven-design/">&lt;p&gt;I read this before building the site, so I do not have my notes here yet. I will add them soon.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>C# 10 in a Nutshell: The Definitive Reference</title>
        <published>2026-06-15T00:00:00+00:00</published>
        <updated>2026-06-15T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://erdincyasan.com/en/library/dotnet-in-a-nutshell/"/>
        <id>https://erdincyasan.com/en/library/dotnet-in-a-nutshell/</id>
        
        <content type="html" xml:base="https://erdincyasan.com/en/library/dotnet-in-a-nutshell/">&lt;p&gt;I read this before building the site, so I do not have my notes here yet. I will add them soon.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Hands-On Domain-Driven Design with .NET Core</title>
        <published>2026-06-15T00:00:00+00:00</published>
        <updated>2026-06-15T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://erdincyasan.com/en/library/hands-on-ddd-with-net-core/"/>
        <id>https://erdincyasan.com/en/library/hands-on-ddd-with-net-core/</id>
        
        <content type="html" xml:base="https://erdincyasan.com/en/library/hands-on-ddd-with-net-core/">&lt;p&gt;I read this before building the site, so I do not have my notes here yet. I will add them soon.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Implementing Domain-Driven Design</title>
        <published>2026-06-15T00:00:00+00:00</published>
        <updated>2026-06-15T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://erdincyasan.com/en/library/implementing-domain-driven-design/"/>
        <id>https://erdincyasan.com/en/library/implementing-domain-driven-design/</id>
        
        <content type="html" xml:base="https://erdincyasan.com/en/library/implementing-domain-driven-design/">&lt;p&gt;I read this before building the site, so I do not have my notes here yet. I will add them soon.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>The Rust Programming Language</title>
        <published>2026-06-15T00:00:00+00:00</published>
        <updated>2026-06-15T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://erdincyasan.com/en/library/the-rust-programming-language/"/>
        <id>https://erdincyasan.com/en/library/the-rust-programming-language/</id>
        
        <content type="html" xml:base="https://erdincyasan.com/en/library/the-rust-programming-language/">&lt;p&gt;I read this before building the site, so I do not have my notes here yet. I will add them soon.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Ultimate ASP.NET Core Web API (Code Maze)</title>
        <published>2026-06-15T00:00:00+00:00</published>
        <updated>2026-06-15T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://erdincyasan.com/en/library/ultimate-aspnet-core-web-api/"/>
        <id>https://erdincyasan.com/en/library/ultimate-aspnet-core-web-api/</id>
        
        <content type="html" xml:base="https://erdincyasan.com/en/library/ultimate-aspnet-core-web-api/">&lt;p&gt;I read this before building the site, so I do not have my notes here yet. I will add them soon.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Designing Data-Intensive Applications</title>
        <published>2026-06-10T00:00:00+00:00</published>
        <updated>2026-06-10T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://erdincyasan.com/en/library/designing-data-intensive-applications/"/>
        <id>https://erdincyasan.com/en/library/designing-data-intensive-applications/</id>
        
        <content type="html" xml:base="https://erdincyasan.com/en/library/designing-data-intensive-applications/">&lt;p&gt;I read this before building the site, so I do not have my notes here yet. I will add them soon.&lt;&#x2F;p&gt;
</content>
        
    </entry>
</feed>
