<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Varun&apos;s Musings</title><description>A random man, on a journey of seeking knowledge</description><link>https://snare.dev</link><item><title>Year of Varun #21 - A Retrospective</title><link>https://snare.dev/musings/21-retrospective</link><guid isPermaLink="true">https://snare.dev/musings/21-retrospective</guid><description>A retrospective on turning 22</description><pubDate>Sat, 19 Apr 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I’m 22 today. Happy one year closer to death&lt;sup&gt;&lt;a href=&quot;#user-content-fn-oneyearcloser&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; for me! I’m already
a fifth of the way through my 20s, that’s wild to even think about.&lt;/p&gt;
&lt;p&gt;I’ve wanted to start doing a
&lt;a href=&quot;https://www.scrum.org/resources/what-is-a-sprint-retrospective&quot;&gt;retrospective&lt;/a&gt;
(retro) on life each year, almost like a New Year’s post, but on my birthday.
Kinda cliche, but it’s gonna be less “New Year’s Resolution” and more personal
reflection.&lt;/p&gt;
&lt;p&gt;My 21st year of life has somehow been both uneventful and eventful at the same
time. Let’s look back on what’s been going on thus far.&lt;/p&gt;
&lt;h2&gt;Deconversion&lt;/h2&gt;
&lt;p&gt;This one’s probably the most important event to me, personally.&lt;/p&gt;
&lt;p&gt;I formally renounced Christianity from my life on July 7, 2025, when I left my
church. I had already been atheist for at least a year up to that point, but
remained in the church out of fear that I would lose my job, since it was based
on building a
&lt;a href=&quot;https://www.saltandlight.community&quot;&gt;faith-based platform for connection&lt;/a&gt;. I
didn’t agree with the objectives while I worked there, but I silently
participated and just kept my mouth shut, so I could continue paying for school;
it just became unbearable after a while.&lt;/p&gt;
&lt;p&gt;Interestingly enough, almost no one has contacted me from there since then sans
a few friends that I had already told the news to. It’s almost irritating (but
also a little liberating) to realize that no one really cared, since it means I
can cleanly put that era in the past. But it also means I was used; I
volunteered almost every single week, you would think someone would notice. But
nah. That was the irritating part; being a simple asset to them, and not a
human&lt;sup&gt;&lt;a href=&quot;#user-content-fn-hr&quot;&gt;2&lt;/a&gt;&lt;/sup&gt; worth talking to outside of “serving”.&lt;/p&gt;
&lt;p&gt;Sometimes I really do think about the times that I used to play drums with
people that I loved talking to, and all the things that I believed. It only gets
more nostalgic every time I drive by the place, since I haven’t really done many
drum gigs since then. But more on that later.&lt;/p&gt;
&lt;p&gt;You can read my &lt;a href=&quot;/musings/the-unchristian&quot;&gt;deconversion post&lt;/a&gt; about it if you’d
like to know about why I renounced my faith.&lt;/p&gt;
&lt;h2&gt;Burnout/Job Hell&lt;/h2&gt;
&lt;p&gt;I went through two large periods of
&lt;a href=&quot;https://en.wikipedia.org/wiki/Occupational_burnout&quot;&gt;burnout&lt;/a&gt; last year. It
wasn’t fun at all; I’m sure most people that have gone through burnout, if not
all, would agree with me here. My first one was right around this time last year
from April-May 2024 at the end of my Spring 2024 semester, and I had another one
that lasted much longer from November 2024-February 2025.&lt;/p&gt;
&lt;p&gt;Burnout is hellish, especially when they last for extended periods of time like
mine. I didn’t feel like getting out of bed, like eating, like doing &lt;em&gt;anything&lt;/em&gt;
at all, even though I knew I needed to. Tickets and fulfilling work tasks? Down
the drain. Projects for school? Down the drain as well! Especially when
&lt;a href=&quot;https://health.clevelandclinic.org/adhd-paralysis&quot;&gt;ADHD paralysis&lt;/a&gt; hits, and
you’re just sitting in bed, feeling like you’re enslaved by the grips of YouTube
and Reddit and Hacker News on your phone, and you want to throw your phone
across the room to stop. It’s genuinely claustrophobic at times, and it feels
like nothing can help but escapism through various means&lt;sup&gt;&lt;a href=&quot;#user-content-fn-escapism&quot;&gt;3&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;It doesn’t really help that I ended up basically quitting my church-based
startup job at the beginning of August 2024. It was not working out financially
anyway, and the faith issues I mentioned earlier only made it
inevitable&lt;sup&gt;&lt;a href=&quot;#user-content-fn-seedling&quot;&gt;4&lt;/a&gt;&lt;/sup&gt;. After that, I went through an unemployment stint until
November 2024, and briefly worked at another startup until I lost my job yet
again in January 2025. I have not been able to find a job since then (as of the
time of this writing) because the job market is that hellish. Crazy, right? I
like to think I’m a pretty competent person, but not being able to hold jobs
because of burnout gives me massive bouts of
&lt;a href=&quot;https://en.wikipedia.org/wiki/Impostor_syndrome&quot;&gt;impostor syndrome&lt;/a&gt;, and some
days I feel like genuinely committing suicide because of it. But I digress; good
times will come some day, right?&lt;/p&gt;
&lt;h2&gt;New Projects, New Friends&lt;/h2&gt;
&lt;p&gt;Not all has been bad, though.&lt;/p&gt;
&lt;p&gt;I’ve made a ton of new friends, mostly at
&lt;a href=&quot;https://sfsu.edu&quot;&gt;San Francisco State University&lt;/a&gt; (SFSU) through various
classes and at their Computer Science Lab. While I was
funemployed&lt;sup&gt;&lt;a href=&quot;#user-content-fn-funemployed&quot;&gt;5&lt;/a&gt;&lt;/sup&gt;, I managed to carry an entire semester’s worth of
people on my back, running an underground tutoring service for an operating
systems principles course that I was also taking at the same time in Fall 2025.
I made almost $1,000 too! By far one of the best classes I’ve taken in a
university setting, thanks to a great
&lt;a href=&quot;https://cs.sfsu.edu/people/robert-bierman&quot;&gt;professor&lt;/a&gt;. I also started really
talking to people working on various projects like a
&lt;a href=&quot;https://github.com/engine3d-dev/TheAtlasEngine&quot;&gt;game engine from scratch&lt;/a&gt;,
among other cool things. And I’m building a car right now with some of them for
my embedded Linux class! Exciting stuff. And I’m not afraid of working on large
projects that seem out of my pay grade anymore, especially after watching this
great
&lt;a href=&quot;https://www.youtube.com/playlist?list=PLP29wDx6QmW4Mw8mgvP87Zk33LRcKA9bl&quot;&gt;low-level source dive YouTube series&lt;/a&gt;,
so that’s a plus.&lt;/p&gt;
&lt;p&gt;I’ve been doing open source contributions as well. I’ve continued to work on my
&lt;a href=&quot;https://github.com/nix-community/nixos-cli&quot;&gt;NixOS CLI&lt;/a&gt;, and am almost done with
a full rewrite of it from Zig to Go that I’ve been doing solely while riding
&lt;a href=&quot;https://en.wikipedia.org/wiki/Bay_Area_Rapid_Transit&quot;&gt;BART&lt;/a&gt; to/from university,
so I’m not done yet. But I will be soon! I’ve also slowly been increasing my
contributions to various Nix ecosystems such as &lt;code&gt;nixpkgs&lt;/code&gt; and &lt;code&gt;home-manager&lt;/code&gt;. My
goal is to become a committer to these places in the future, and I would also
like to participate in &lt;a href=&quot;https://github.com/ngi-nix/summer-of-nix&quot;&gt;Summer of Nix&lt;/a&gt;
this year, if possible.&lt;/p&gt;
&lt;p&gt;Basically, all I’ve been doing is just improving the open source world, and
honing my own skills while I’m at it! It’s really enlightening, and I hope that
others can read from it.&lt;/p&gt;
&lt;h2&gt;Graduation&lt;/h2&gt;
&lt;p&gt;I’m a month out from graduating with a bachelors in computer science from SFSU.
It’s surreal; I don’t plan on pursuing a master’s so this is gonna be my last
month in school. While I’ve been working at various startups my entire time
throughout college, I can’t imagine not going to school anymore.&lt;/p&gt;
&lt;p&gt;Like, where the hell am I gonna make new friends? I sure as hell can’t do it
from my computer, I tried making friends online during COVID and it didn’t
fucking work at all.&lt;/p&gt;
&lt;p&gt;What’s gonna happen to my old friends that I’ve been making? I don’t want to
lose them because we’re not in the same place anymore.&lt;/p&gt;
&lt;p&gt;Will I have time to start making a life for myself? Because hell if I want to
move out, have you seen the fucking rent elsewhere? The Bay Area is a goddamn
expensive place to live in, and I’m still looking for work! I’ll stay with my
parents, thank you very much.&lt;/p&gt;
&lt;p&gt;I plan on flying to India for a time, to see my family there. My grandparents
and cousins are all over there, and I’ve never been able to see them for more
than a month at a time. I’d like to change that. But what comes after that?&lt;/p&gt;
&lt;p&gt;There’s a ton of questions I’m gonna have to answer, and rather soon. Because I
definitely can’t deal with two more years of school, that’s for sure. But I’ll
be sad when it happens.&lt;/p&gt;
&lt;h2&gt;What’s Next?&lt;/h2&gt;
&lt;p&gt;Well first off, I want a job, as I mentioned prior. It sucks being funemployed.
Here’s my
&lt;a href=&quot;https://raw.githubusercontent.com/water-sucks/resume/main/resume.pdf&quot;&gt;master resume&lt;/a&gt;,
in case you’re reading this and you’re hiring. Plus, I would need money for all
the things I’m about to mention next.&lt;/p&gt;
&lt;p&gt;I mentioned earlier that I don’t play drums as much. That’s true, unfortunately.
So it seems, that:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;I’ve lost the plot.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I wanted to play drums for a living, but now I’m just stuck trying to make a
career in tech. This sucks, and it genuinely makes me wonder what I’m doing it
all for in the first place if I’m not gonna practice the one thing I’ve enjoyed
my entire life.&lt;/p&gt;
&lt;p&gt;It all went wrong when I left the church. I had already not been practicing, but
that was because I was in a death spiral of using my church gigs as practice
time, since I ended up playing so-called
&lt;a href=&quot;https://en.wikipedia.org/wiki/Contemporary_Christian_music&quot;&gt;“contemporary Christian music”&lt;/a&gt;
that I fucking LOATHED&lt;sup&gt;&lt;a href=&quot;#user-content-fn-loathe&quot;&gt;6&lt;/a&gt;&lt;/sup&gt;. When I left the church, my practice came to a
screeching halt.&lt;/p&gt;
&lt;p&gt;I plan on doing better in this regard. Even though I’m busy now, I will have to
force the time back in. It won’t be fun at first. I’ll be throwing my sticks at
the wall, wondering why I don’t have the same speed, the same skill. But it’ll
all be worth it in the end, I hope. Let’s see in next year’s retro if this is
the case.&lt;/p&gt;
&lt;p&gt;On a different note, one of the newest aspirations I’ve had recently is because
of the 2024 US elections. I no longer want to stand back and see it all burn
down while not lifting a finger to stop it. I’ll either see this through, or die
trying.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;I want to get into politics.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;I don’t know how, or where I can get started, but I will, eventually. As much as
I hate electoral politics, I see no other options for my political ideas to
start reaching a wider mainstream. And the populace is hungry for fresh ideas,
because the LORD knows that we have not had any in centuries, it feels like.&lt;/p&gt;
&lt;p&gt;There’s a lot more I can say about this, and I’ll probably write a post about it
too. But I’ll leave at that for now. The TL;DR: I have dreams, and I want to
ensure that I can fulfill them reliably. Seeing how fucked the current state of
politics in the US is, I fear that this may not be possible unless I do
something about it. This requires &lt;strong&gt;candor&lt;/strong&gt;. Revolutionary thought, even. Let’s
go where no one has dared gone before. Think new thoughts that no one has ever
thought. It’s all possible, we just need the time to see it through.&lt;/p&gt;
&lt;p&gt;Here’s to a new year, and new aspirations.&lt;/p&gt;
&lt;section&gt;&lt;h2&gt;Footnotes&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;I do this as a joke with my friends when I say happy birthday to them. Or
“happy one year closer to the apocalypse!” A way to kill the mood, sure, but
at least it’s different and turns some heads, right? &lt;a href=&quot;#user-content-fnref-oneyearcloser&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Like “Human Resources” at a company. It seems uncanny to me that people
would even refer to other humans as “resources”, but that’s precisely what
it felt like at the church. I don’t think anyone likes being “used”, but a
lot of religious people definitely are being used in a sense; that included
me at the time. &lt;a href=&quot;#user-content-fnref-hr&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Oftentimes, I would smoke weed or have a little drink before working on
things for an extended period of time. It would help me to not overthink or
have anxiety about things, but I recognize it’s an extremely bad habit. I’m
remaining sober and off drugs for the time being, so I am not addicted or
anything. I just want to re-evaluate their place in my life after a break. &lt;a href=&quot;#user-content-fnref-escapism&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Turns out that the company that I was working for previously has rebooted
into some other faith tech thing called &lt;a href=&quot;https://seeedling.so&quot;&gt;Seedling&lt;/a&gt;.
Well, even though I can probably go back, I am not interested in this sort
of thing in the least, and I have no desire to work on things I don’t agree
with.
&lt;a href=&quot;https://www.forbes.com/sites/quora/2018/12/04/the-science-behind-prayer-and-the-placebo-effect/&quot;&gt;Prayer is placebo&lt;/a&gt;,
as far as I’m concerned. &lt;a href=&quot;#user-content-fnref-seedling&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Yes, I call it funemployed instead of unemployed. I get to work on myself,
and I also get to make things! Gotta make the most of having no obligations
to some capitalist scum, right? &lt;a href=&quot;#user-content-fnref-funemployed&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Yeah, I hate playing this style of music. It’s so generic, South Park even
&lt;a href=&quot;https://en.wikipedia.org/wiki/Christian_Rock_Hard&quot;&gt;made an episode about it&lt;/a&gt;.
On the other hand, Loathe is an amazing band, listen to
&lt;a href=&quot;https://open.spotify.com/album/6nUUV3haj8ug8okTmOyIU2&quot;&gt;I Let It In And It Took Everything&lt;/a&gt;
and be enlightened. &lt;a href=&quot;#user-content-fnref-loathe&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;</content:encoded></item><item><title>Hello, World!</title><link>https://snare.dev/musings/hello-world</link><guid isPermaLink="true">https://snare.dev/musings/hello-world</guid><description>Are things working?</description><pubDate>Mon, 01 Apr 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;This is mostly a test to see if things are working. If things aren’t working,
something is horribly wrong, so send me a message! Of course, I want people to
read what I’m saying, right?&lt;/p&gt;
&lt;p&gt;I love using Markdown for posts, but more often than not, it has its
shortcomings as a language. I often use
&lt;a href=&quot;https://github.com/nvim-neorg/neorg&quot;&gt;neorg&lt;/a&gt; inside &lt;code&gt;neovim&lt;/code&gt; to write my posts,
and then convert them to Markdown after that. It has great support for a lot of
things, but it’s still beta-quality software. However, if you use &lt;code&gt;neovim&lt;/code&gt;, it’s
worth a try.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Code Blocks&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;   Damn&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; &quot;you use Nix?&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;   Yeah&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; &quot;I use Nix&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;   &quot;That&apos;s&quot;&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; &quot;so cool!&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; std&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; @import&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;std&quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; print&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; std&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;debug&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;print&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; testing&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; std&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;testing&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;pub&lt;/span&gt;&lt;span&gt; fn&lt;/span&gt;&lt;span&gt; main&lt;/span&gt;&lt;span&gt;() &lt;/span&gt;&lt;span&gt;!&lt;/span&gt;&lt;span&gt;void&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    print&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;Whoa, you use Zig?&lt;/span&gt;&lt;span&gt;\n&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;{})&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;test&lt;/span&gt;&lt;span&gt; &quot;1 + 1 = 2?&quot;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    const&lt;/span&gt;&lt;span&gt; actual&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; 1&lt;/span&gt;&lt;span&gt; +&lt;/span&gt;&lt;span&gt; 1&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    testing&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;expectEqual&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;actual&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; eww&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; () &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; void&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; () &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  console&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;log&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;Eww, TypeScript.&apos;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  console&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;log&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;At least it&apos;s not JavaScript.&quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Thanks&lt;/h2&gt;
&lt;p&gt;Thanks for reading this! Why did you read it though? Are you seriously that
bored?&lt;/p&gt;
&lt;p&gt;Do something better with yourself, haha.&lt;/p&gt;

&lt;img src=&quot;/_astro/yoshi-ded.ZGhSNoMf_ZJGCPz.webp&quot; alt=&quot;Yoshi falling down slope&quot; loading=&quot;lazy&quot; width=&quot;320&quot; height=&quot;240&quot; /&gt;
&lt;p&gt;Fin.&lt;/p&gt;</content:encoded></item><item><title>SourceHut: Git Done Right?</title><link>https://snare.dev/musings/sourcehut-reflections</link><guid isPermaLink="true">https://snare.dev/musings/sourcehut-reflections</guid><description>My personal review of SourceHut after using it for a few months</description><pubDate>Thu, 25 Dec 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;God, I’m sick of GitHub.&lt;/p&gt;
&lt;p&gt;Where did it all go wrong?&lt;/p&gt;
&lt;p&gt;I started coding professionally in 2020, and learned how to use Git for the
first time the year before. As many new GitHub users think at first, I thought
GitHub was the &lt;em&gt;only&lt;/em&gt; way to use Git, and never even considered the fact that
there are alternatives.&lt;/p&gt;
&lt;p&gt;In September 2025, I had enough of this, and moved all my repositories to
&lt;a href=&quot;https://sourcehut.org&quot;&gt;SourceHut&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;But why would one even use an alternative Git hosting service?&lt;/p&gt;
&lt;h2&gt;Why I Switched&lt;/h2&gt;
&lt;p&gt;Consider 2020-2022 as my Git honeymoon. I used to create a GitHub repo for
EVERYTHING. Hell, I still do. My CS homework? The very first thing I did at the
start of each semester was &lt;code&gt;gh repo create&lt;/code&gt; for each class. Like a madman, I
even tried to use GitHub for versioning my music compositions at one
point&lt;sup&gt;&lt;a href=&quot;#user-content-fn-music-git&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;!&lt;/p&gt;
&lt;p&gt;However, this honeymoon would only last a couple of years.&lt;/p&gt;
&lt;h3&gt;AI Bullshit&lt;/h3&gt;
&lt;p&gt;The first time I had started to realize that “maybe GitHub isn’t a good
platform” was 2023, when &lt;a href=&quot;https://github.com/features/copilot&quot;&gt;GitHub Copilot&lt;/a&gt;
usage started to increase and the hype creeped into my daily life.&lt;/p&gt;
&lt;p&gt;I had never heard of Copilot until that point, and was not interested in most AI
tools whatsoever. However, as more people in my life started to use it and
professors told us that we would get extra credit for using these AI
autocomplete plugins, I gave in. I signed up and integrated it into Neovim for a
while&lt;sup&gt;&lt;a href=&quot;#user-content-fn-neovim-experience&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;The more I looked into and used AI autocomplete tools (not just Copilot, I used
Codeium/Windsurf at one point), the more appalled I became. And it wasn’t just
because the code completion wasn’t good. Even the way that they sourced data was
deeply unsettling for me. My research into this yielded the following questions
that I could not find adequate answers for:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Does training on GPL/other copyleft code make all generated code
&lt;a href=&quot;https://en.wikipedia.org/wiki/Derivative_work&quot;&gt;derivative works&lt;/a&gt;, or what I
like to call &lt;strong&gt;fruit of the copyleft tree&lt;/strong&gt;&lt;sup&gt;&lt;a href=&quot;#user-content-fn-copyleft-tree&quot;&gt;3&lt;/a&gt;&lt;/sup&gt;?&lt;/li&gt;
&lt;li&gt;Have authors/owners of code truly consented to letting their code be used for
AI training? And no, IMO,
&lt;a href=&quot;https://www.digitalfluency.guide/data-ethics/the-informed-part-of-informed-consent&quot;&gt;&lt;em&gt;a simple EULA is not enough for &lt;strong&gt;true&lt;/strong&gt; informed consent&lt;/em&gt;&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Since code quality on GitHub can be rather questionable, is there any
solution to Copilot potentially introducing vulnerabilities and other
poor-quality code to a project?&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;These are just a few questions of many others in the legal gray areas GitHub is
trotting in currently with all of their AI offerings. They only are digging
themselves into deeper shit, considering that they likely use private
repositories to train their models with
&lt;a href=&quot;https://github.com/orgs/community/discussions/171080&quot;&gt;without admitting so publicly&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Interesting unethical practices, to say the least.&lt;/p&gt;
&lt;p&gt;Fun fact, companies used to take these concerns extremely seriously. Even large
companies like Apple has
&lt;a href=&quot;https://techcrunch.com/2023/05/19/apple-reportedly-limits-internal-use-of-ai-powered-tools-like-chatgpt-and-GitHub-copilot&quot;&gt;restricted use of ChatGPT and Copilot&lt;/a&gt;
in the past for the same reasons. Yet GitHub keeps peddling AI as a magic tool
in many of its products and shoving Copilot into everywhere on the GitHub
interface, including
&lt;a href=&quot;https://docs.github.com/en/copilot/how-tos/use-copilot-agents/request-a-code-review/use-code-review&quot;&gt;pull request reviews&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;These aren’t the only problems with GitHub though. Far from it.&lt;/p&gt;
&lt;h3&gt;Work Gamification&lt;/h3&gt;
&lt;p&gt;Every company has to figure out some way of keeping retention numbers high,
right?&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Yes&lt;/em&gt;, including companies where more engagement with their software can &lt;em&gt;drive
down&lt;/em&gt; the quality of the content on it&lt;sup&gt;&lt;a href=&quot;#user-content-fn-engagement-quality&quot;&gt;4&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;Surprisingly, user retention methods can apply to things OUTSIDE the content
creation space (i.e. YouTube, TikTok, the like). And GitHub has figured out a
way to make this apply to the entire software development life cycle…&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Behold: the contribution graph!&lt;/strong&gt;.&lt;/p&gt;

&lt;figure&gt; &lt;img src=&quot;/_astro/varun-contribution-graph-2025._xjY2WLx_2967c8.webp&quot; srcset=&quot;/_astro/varun-contribution-graph-2025._xjY2WLx_tTos2.webp 640w, /_astro/varun-contribution-graph-2025._xjY2WLx_SOR5G.webp 750w, /_astro/varun-contribution-graph-2025._xjY2WLx_ZOwVqN.webp 828w&quot; alt=&quot;My GitHub contribution graph for 2025&quot; loading=&quot;lazy&quot; width=&quot;866&quot; height=&quot;619&quot; /&gt; &lt;u&gt; &lt;i&gt; &lt;figcaption&gt;My GitHub contribution graph for 2025&lt;/figcaption&gt; &lt;/i&gt; &lt;/u&gt; &lt;/figure&gt;
&lt;p&gt;Christ, I used to be OBSESSED with this piece of shit. And I still am sometimes,
but nowhere near the extent to which I was&lt;sup&gt;&lt;a href=&quot;#user-content-fn-cgraph&quot;&gt;5&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;Let me explain further.&lt;/p&gt;
&lt;p&gt;GitHub incentivizes you to use Git in the way that they have intended. And
frankly, that means using every single resource on their platform. Filing
issues, making pull requests, and commits that land on default branches all
result in so-called “contributions”. And
&lt;a href=&quot;https://news.ycombinator.com/item?id=11404482&quot;&gt;WAY too much emphasis is placed on this stupid green graph&lt;/a&gt;
by some people, including prospective employers. That’s just bonkers to me now.&lt;/p&gt;
&lt;p&gt;This causes people to either
&lt;a href=&quot;https://github.com/gelstudios/gitfiti&quot;&gt;spoof commit history&lt;/a&gt;, create small
commits solely for getting darker squares on the graph, and even make
&lt;a href=&quot;https://navendu.me/posts/ai-generated-spam-prs/&quot;&gt;AI-generated spammy pull requests&lt;/a&gt;&lt;sup&gt;&lt;a href=&quot;#user-content-fn-worst-contrib-ever&quot;&gt;6&lt;/a&gt;&lt;/sup&gt;
for the resume’s sake. It makes maintainers’ lives hell, decreases code quality,
and all for what? A green graph?&lt;/p&gt;

&lt;figure&gt; &lt;img src=&quot;/_astro/green-graphs-and-ham.DF9JKkxw_1TMypC.webp&quot; srcset=&quot;/_astro/green-graphs-and-ham.DF9JKkxw_1jowAk.webp 640w, /_astro/green-graphs-and-ham.DF9JKkxw_jp2Pu.webp 750w, /_astro/green-graphs-and-ham.DF9JKkxw_Z1p7qOg.webp 828w, /_astro/green-graphs-and-ham.DF9JKkxw_Z29s1nT.webp 1080w, /_astro/green-graphs-and-ham.DF9JKkxw_1TMypC.webp 1280w&quot; alt=&quot;Dr. Seuss Green Eggs and Ham photoshop with GitHub graph&quot; loading=&quot;lazy&quot; width=&quot;1280&quot; height=&quot;720&quot; /&gt; &lt;u&gt; &lt;i&gt; &lt;figcaption&gt;I do not like green graphs and ham, Sam I am.&lt;/figcaption&gt; &lt;/i&gt; &lt;/u&gt; &lt;/figure&gt;
&lt;p&gt;This is definitely a smaller problem than the others, but it’s still something
that has annoyed me ever since I noticed it in my own behavior. Something had to
change.&lt;/p&gt;
&lt;h3&gt;Pull Requests: The Antichrist’s Git Workflow&lt;/h3&gt;
&lt;p&gt;Git branches originally worked best with local workflows.&lt;/p&gt;
&lt;p&gt;GitHub changed that, and did so radically. No, radical does not necessarily mean
good in this case.&lt;/p&gt;
&lt;p&gt;They made branches the primary means of communication between developers, rather
than the commits themselves (I’ll call commits “&lt;em&gt;patches&lt;/em&gt;” and a series of them
a “&lt;em&gt;patchset&lt;/em&gt;” later on). I didn’t know when I first started using Git at first,
but this was not the original way Git was designed to be used.&lt;/p&gt;
&lt;p&gt;Linus Torvalds, the creator of Git himself, has
&lt;a href=&quot;https://github.com/torvalds/linux/pull/17#issuecomment-5654674&quot;&gt;historically LAID into GitHub&lt;/a&gt;
for this branch-based contribution model, and they’ve simply ignored him and
continued on with their ways&lt;sup&gt;&lt;a href=&quot;#user-content-fn-linus-god&quot;&gt;7&lt;/a&gt;&lt;/sup&gt;. Opinionated is good and all, but not
when the opinions are just &lt;em&gt;bad&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;GitHub’s pull request UI is breathtakingly terrible once you start to see its
flaws. First off: &lt;strong&gt;diff soup&lt;/strong&gt;. Diff soup has genuinely made my life hell in
many instances at work and in open source as well.&lt;/p&gt;
&lt;p&gt;What is diff soup, you may ask? I define diff soup as large pull requests that
make it extremely hard to find the context and/or history of a given patchset
and the corresponding reviews/its evolution over time.&lt;/p&gt;
&lt;p&gt;Let’s do a small case study.&lt;/p&gt;
&lt;p&gt;Take a large pull request such as
&lt;a href=&quot;https://github.com/NixOS/nix/pull/8699&quot;&gt;this one that introduces a C API to the Nix package manager&lt;/a&gt;.
It went for more than a YEAR before getting merged, with hundreds of review
comments in a flat thread. If we’re talking code smells, this is a fucking
sewer.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Observation I&lt;/strong&gt;: It’s basically impossible to find the thread of conversation,
since many comments apply to code that simply does not exist anymore. Fun fact,
GitHub oftentimes simply does not display some review comments when their
corresponding diffs disappear. That’s because the whole diff is displayed as it
was in its most recent form. You can’t view what the repository was like before,
unless you stack your commits on top of previous ones. So if you want previous
review comments, you basically have to say your farewells to rebasing.&lt;/p&gt;
&lt;p&gt;Sure, there are “outdated” comments, but those get applied to different comments
somewhat inconsistently and it’s irritating.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Observation II&lt;/strong&gt;: Speaking of the actual diff view, some diffs can be MASSIVE.
The GitHub UI is very limited in what it can show, and you can’t exactly slice
up a diff and talk about suggestions outside of the changed code that may have
been affected. So the code review process itself is also limited in that way.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Observation III&lt;/strong&gt;: The very notion of “resolving” comments is overrated in its
current form. They’re just a way to hide comments (both “outdated” AND
“up-to-date” comments; “resolved” and “outdated” are too similar, to be frank).
Just look at the PR and how many resolved comments there are; how many more are
actually related to the up-to-date code on that branch anymore?&lt;/p&gt;
&lt;p&gt;Also, pet peeve of mine: for some reason, an author can resolve comments without
actually acknowledging or applying the reviewer’s requested changes. They really
shouldn’t be allowed to do that; the original commenter should mark it as
resolved after having the appropriate discussion or verifying the requested
changes have been made.&lt;/p&gt;
&lt;p&gt;Oh, and don’t get me STARTED on the terms GitHub uses for merging strategies for
pull requests.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Create a merge commit&lt;/strong&gt; :: this is the pull request merging strategy GitHub
incentivizes, and these merge commits are ugly as FUCK.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Squash and merge&lt;/strong&gt; :: this turns the whole branch diff into a single commit
and fast-forwards the target branch with the new squashed commit. NO, it does
NOT create a merge commit, as the name seems to suggest.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Rebase and merge&lt;/strong&gt; :: God FORBID we use the right fucking terms for Git
operations. THESE ARE CONTRADICTORY TERMS! IT’S JUST REBASING ON TOP OF THE
TARGET BRANCH, YOU FUCKS!&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;As you can see from my sailor’s mouth, this is a subject that will ALWAYS cause
me to crash out when it comes up.&lt;/p&gt;
&lt;p&gt;As if we couldn’t have more problems with the pull request workflow, GitHub has
&lt;a href=&quot;https://github.com/orgs/community/discussions/10410&quot;&gt;no interest in preserving GPG signatures&lt;/a&gt;
through the latter two strategies. In order for you to keep GPG signatures, you
HAVE to use merge commits. And we already know how ugly those end up looking in
the subsequent history&lt;sup&gt;&lt;a href=&quot;#user-content-fn-merging-without-ui&quot;&gt;8&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;They put in all this work for showing GPG signatures alongside code, and they
don’t even support it properly when merging a pull request into mainline
branches? Laughably negligent and lazy on GitHub’s behalf.&lt;/p&gt;
&lt;p&gt;And no, &lt;code&gt;gh pr&lt;/code&gt; does not make using GitHub pull requests easier. If anything,
it’s just a shitty replica of the web UI in CLI form.&lt;/p&gt;
&lt;p&gt;The flawed pull request flow is ultimately what caused me to get so fed up with
GitHub as time went on. I was curious about how projects like the Linux Kernel
Mailing List (LKML) and PostgreSQL handle contributions, since they’ve gone on
for decades without needing GitHub whatsoever. Not to mention Git was literally
DESIGNED for the LKML, so I wanted to see how Git was originally meant to be
used.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Enter &lt;a href=&quot;https://sourcehut.org&quot;&gt;SourceHut&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;
&lt;h2&gt;Getting Started&lt;/h2&gt;
&lt;p&gt;I saw SourceHut for the first time when looking at Zig, sometime in mid-2023. I
noticed that some of their CI infrastructure ran on &lt;code&gt;builds.sr.ht&lt;/code&gt; (specifically
FreeBSD), and was curious about what SourceHut offered.&lt;/p&gt;
&lt;p&gt;One of the very first things I saw was that it was created by Drew DeVault, and
that got me very interested. While the dude is very polarizing, I tend to like a
lot of his takes on software, and I also love using Wayland (using
&lt;a href=&quot;https://codeberg.org/river/river&quot;&gt;&lt;code&gt;riverwm&lt;/code&gt;&lt;/a&gt; at time of writing).&lt;/p&gt;
&lt;p&gt;I actually ended up purchasing a subscription to support his efforts in July
2023, but never used the platform until September 2025. That’s when I finally
snapped, and GitHub had given me enough trouble. Diff soup was one thing, but I
had finally come to the realization that until GitHub takes its head out of its
own ass with regards to AI, I had no business of keeping my primary means of
development there. Plus, I really badly wanted to learn how to use Git with
email, and I figure diving into the deep end headfirst is the best way to do
so&lt;sup&gt;&lt;a href=&quot;#user-content-fn-deep-end-nix&quot;&gt;9&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;h3&gt;Hold On, Why Not GitLab/Forgejo/Gitea Or Something Else?&lt;/h3&gt;
&lt;p&gt;Some of the problems I have with these platforms are similar to the one with
GitHub, minus the AI bullshit I suppose.&lt;/p&gt;
&lt;p&gt;For them, it’s not exactly the project/entities behind them as much as they copy
the GitHub model of pull requests that I have started to dislike.&lt;/p&gt;
&lt;p&gt;Feel free to &lt;a href=&quot;mailto:varun@snare.dev&quot;&gt;send me an email&lt;/a&gt; for further discussion.&lt;/p&gt;
&lt;h3&gt;Porting Repositories&lt;/h3&gt;
&lt;p&gt;I’m not gonna lie, porting repositories was literally seamless. After adding my
SSH keys (both my &lt;code&gt;gpg-ssh-agent&lt;/code&gt; key and a machine-specific one), I just ran:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;$ git remote set-url origin git@git.sr.ht:~watersucks/&amp;lt;repo-name&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;in each one of my repos and pushed them up. SourceHut automatically created a
private repository for each of these, and then I toggled their visibility to
“public” and voila! My repos were ported over.&lt;/p&gt;
&lt;p&gt;Not so fast, what about discoverability?? Great question.&lt;/p&gt;
&lt;h3&gt;GitHub Mirroring&lt;/h3&gt;
&lt;p&gt;GitHub is popular because everyone uses it. Now, I don’t have to necessarily USE
the GitHub facilities, but I can optimize my own projects for both drive-by
GitHub contributors AND SourceHut users by mirroring my repository to GitHub on
every push.&lt;/p&gt;
&lt;p&gt;Here comes &lt;code&gt;builds.sr.ht&lt;/code&gt; to save the day!&lt;/p&gt;
&lt;p&gt;I created a (mostly) identical build manifest in each repository at
&lt;code&gt;.builds/mirror.yml&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;image&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; alpine/edge&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;secrets&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  - &lt;/span&gt;&lt;span&gt;&amp;lt;GITHUB_TOKEN_SECRET&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;sources&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  - &lt;/span&gt;&lt;span&gt;https://git.sr.ht/~watersucks/&amp;lt;repo-name&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;tasks&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  - &lt;/span&gt;&lt;span&gt;write-ssh-config&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; |&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      cat &amp;gt;&amp;gt; ~/.ssh/config &amp;lt;&amp;lt; EOF&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      Host github.com&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        IdentityFile ~/.ssh/srht_github_mirror&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        IdentitiesOnly yes&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        BatchMode yes&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        StrictHostKeyChecking no&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      EOF&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  - &lt;/span&gt;&lt;span&gt;mirror&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; |&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      cd ~/&amp;lt;repo&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      git remote add github git@github.com:water-sucks/&amp;lt;repo&amp;gt;.git&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      git push --mirror github&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;triggers&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  - &lt;/span&gt;&lt;span&gt;action&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; email&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    condition&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; failure&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    to&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &apos;Varun Narravula &amp;lt;varun@snare.dev&amp;gt;&apos;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Once I pushed it up, SourceHut automatically ran this manifest and synced the
repository to GitHub on every push. I put up a notice in the READMEs and on the
GitHub repository description that the primary sources were at SourceHut, but
that I would still accept pull requests and issues, and mirror them over into
SourceHut when applicable.&lt;/p&gt;
&lt;p&gt;Now, there are limitations to these build manifests, which I’ll go over later.
But so far, this move was going pretty well.&lt;/p&gt;
&lt;h2&gt;Patch-Based Email Workflow&lt;/h2&gt;
&lt;p&gt;Now comes the hard part: figuring out the email-based workflow.&lt;/p&gt;
&lt;p&gt;I had to create mailing lists for each project already, and followed the
recommended layout of having three lists: &lt;code&gt;&amp;lt;repo&amp;gt;-devel&lt;/code&gt;, used for
development/sending patches, &lt;code&gt;&amp;lt;repo&amp;gt;-announce&lt;/code&gt; for announcing releases/breaking
changes and other such things, and &lt;code&gt;&amp;lt;repo&amp;gt;-discuss&lt;/code&gt; for general discussion.&lt;/p&gt;
&lt;p&gt;Now, I didn’t expect much engagement at all, and that’s fine. Most people are
simply not going to know how to even interact with SourceHut properly, since the
documentation is sparse&lt;sup&gt;&lt;a href=&quot;#user-content-fn-sparse-docs&quot;&gt;10&lt;/a&gt;&lt;/sup&gt; compared to GitHub. But at the very least,
I had to set something up in order to respond to these things/create patches
myself for other projects on the platform.&lt;/p&gt;
&lt;p&gt;To that point, I found &lt;a href=&quot;https://aerc-mail.org/&quot;&gt;&lt;code&gt;aerc&lt;/code&gt;&lt;/a&gt;, Drew DeVault’s own
email client&lt;sup&gt;&lt;a href=&quot;#user-content-fn-aerc-maintainer&quot;&gt;11&lt;/a&gt;&lt;/sup&gt;. It seemed really cool to manage email through
the terminal, so I picked it up, added it to my
&lt;a href=&quot;https://github.com/nix-community/home-manager&quot;&gt;home-manager&lt;/a&gt; configuration, and
watched a metric shit-ton of videos on how to do email-based workflows.&lt;/p&gt;
&lt;p&gt;Basically, the idea is this when submitting patches:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Do development on a local branch, make your commits.&lt;/li&gt;
&lt;li&gt;Once your commits are ready, run &lt;code&gt;git format-patch&lt;/code&gt; on the patchset.&lt;/li&gt;
&lt;li&gt;Send the patchset off with &lt;code&gt;git send-email&lt;/code&gt; to the proper mailing list,
optionally with a cover letter.&lt;/li&gt;
&lt;li&gt;If changes are requested, then send another version of that same patchset
after applying the requested fixes. &lt;code&gt;git send-email&lt;/code&gt; and &lt;code&gt;git format-patch&lt;/code&gt;
support versioning patchsets like this, and even implement tooling around
diffing between patchsets in the form of &lt;code&gt;git range-diff&lt;/code&gt;. I’ll talk about my
workflow around this in a later post, so stay tuned.&lt;/li&gt;
&lt;li&gt;Otherwise, wait for the patch to get accepted and for a reply!&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Branches in this flow are for local development organization only. This is a
COMPLETE mindfuck compared to the GitHub flow, and took me a minute to get used
to.&lt;/p&gt;
&lt;p&gt;For my own projects, branch organization was still great, but all my commits
simply ended up on &lt;code&gt;main&lt;/code&gt;, since I was the trusted core maintainer. In GitHub
land, I would not configure this sort of access, even to my own main branch. I’d
always make pull requests to main and self-merge instead. I’m not gonna lie,
this made much more sense than the GitHub way.&lt;/p&gt;
&lt;p&gt;When you are accepting patches from a list, the flow goes like this:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Open &lt;code&gt;aerc&lt;/code&gt; to find the patch email(s).&lt;/li&gt;
&lt;li&gt;Check out the main development (trunk) branch.&lt;/li&gt;
&lt;li&gt;Pipe the contents of the email(s) to &lt;code&gt;git am&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Make sure shit works.&lt;/li&gt;
&lt;li&gt;If good, then rebase it into your trunk and push it up, and reply back with a
“Thanks!” and the new, merged commit ref.&lt;/li&gt;
&lt;li&gt;Otherwise, slice up the diffs, and review them by simply &lt;em&gt;editing&lt;/em&gt; the patch
itself inline.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You wanna know something funny, dear reader?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://lists.sr.ht/~sircmpwn/sr.ht-dev/%3C20251214054157.3149363-1-varun@snare.dev%3E&quot;&gt;My first contributed patch on SourceHut was to SourceHut itself.&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;At least one that wasn’t to my own project.&lt;/p&gt;
&lt;p&gt;I ended up fixing a bug with the issue tracker &lt;code&gt;todo.sr.ht&lt;/code&gt; itself that I found
when attempting to resolve one of my issues automatically through email. This
was super annoying at the time, but it does highlight the fact that SourceHut is
&lt;em&gt;not&lt;/em&gt; perfect.&lt;/p&gt;
&lt;h2&gt;Hear Ye, Hear Ye! What Problems Have Arisen, My Dear SourceHut?&lt;/h2&gt;
&lt;p&gt;Let’s talk about some other problems.&lt;/p&gt;
&lt;p&gt;Fixing bugs with the platform itself is one thing. I like using alpha software
and contributing fixes back, but this can interfere with my own project
development. I am not employed by SourceHut, so any bugs with it that I must fix
must come out of my own time.&lt;/p&gt;
&lt;h3&gt;GitHub’s Monopoly&lt;/h3&gt;
&lt;p&gt;I’ve mentioned this earlier, but I am still somewhat stuck in the GitHub
ecosystem for multiple reasons.&lt;/p&gt;
&lt;p&gt;First off, at the time of writing, I still use Nix and have many projects in the
NixOS space. I maintain
&lt;a href=&quot;https://github.com/nix-community/nixos-cli&quot;&gt;&lt;code&gt;nixos-cli&lt;/code&gt;&lt;/a&gt;, and including a
project in &lt;code&gt;nix-community&lt;/code&gt; for visibility requires me to use GitHub for the
upstream. I could develop it on SourceHut personally, but mirroring a repo like
this seems like an unreasonable waste of SourceHut compute when I’m gonna
contribute it to the GitHub upstream anyway.&lt;/p&gt;
&lt;p&gt;Additionally, the Neovim plugin ecosystem often assumes GitHub as a default when
using &lt;code&gt;owner/repo&lt;/code&gt; style syntax for including plugins. My own
&lt;a href=&quot;https://sr.ht/~watersucks/darkrose.nvim&quot;&gt;&lt;code&gt;darkrose.nvim&lt;/code&gt;&lt;/a&gt; colorscheme is
mirrored to GitHub for precisely this reason, to make it easier to use.&lt;/p&gt;
&lt;p&gt;Also, my workplace (&lt;a href=&quot;https://pangolin.net&quot;&gt;Pangolin&lt;/a&gt;) heavily uses GitHub for
its open source hosting and for free GitHub Actions compute. If I want my
paycheck, GitHub it is when I’m on my 9-5.&lt;/p&gt;
&lt;h3&gt;builds.sr.ht Usability Issues&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;builds.sr.ht&lt;/code&gt;, while it is remarkable for what it offers, is missing some
features that I sorely miss from GitHub Actions, such as cronjobs (i.e. for my
weekly NixOS configuration flake updates)&lt;sup&gt;&lt;a href=&quot;#user-content-fn-nixed-updates&quot;&gt;12&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;It’s also somewhat limiting to only be able to run 4 jobs on every push, but I
understand this limitation; build compute cannot be free forever, think of the
trees!&lt;/p&gt;
&lt;p&gt;One more gripe with &lt;code&gt;builds.sr.ht&lt;/code&gt;: for public projects, where do I send build
failures? Does it happen automatically with CI? Mailing list CI is just very
confusing, and I don’t get who receives the notifications. I don’t want everyone
in a &lt;code&gt;&amp;lt;repo&amp;gt;-devel&lt;/code&gt; list to receive notice of my own stupid mistakes, and I
don’t want to see contributors’ CI failures either. Triggers aren’t very
flexible at the moment, and I don’t know how to expand them properly. If someone
knows how, &lt;a href=&quot;mailto:varun@snare.dev&quot;&gt;send me an email&lt;/a&gt;, I’d appreciate it.&lt;/p&gt;
&lt;h3&gt;External Usability&lt;/h3&gt;
&lt;p&gt;This is always gonna be a fundamental problem with SourceHut, in my opinion.&lt;/p&gt;
&lt;p&gt;The thing is, people have been so conditioned by GitHub to &lt;em&gt;expect&lt;/em&gt; the
contribution process to be streamlined for them. That’s because GitHub is
HEAVILY optimized for drive-by contributions. Any stupid person can make a pull
request without explaining the purpose of it properly. The very nature of
sending patches over email is intimidating for many Git users, and will tend to
invite more experienced developers rather than noob ones.&lt;/p&gt;
&lt;p&gt;The analogy I make: since GitHub is optimized for drive-by contributions,
&lt;em&gt;SourceHut simply wants you to park your damn car and to be more persistent for
a change&lt;/em&gt;. It’s a good attitude to have, and one that I share.&lt;/p&gt;
&lt;p&gt;However, this drives down contributions massively. Even in my own GitHub
repositories, I see people that fork them, have changes in separate branches,
but have not made pull requests despite me saying that it is okay.&lt;/p&gt;
&lt;p&gt;Some days, I fear that because of this barrier to entry, SourceHut may never
gain popularity and be stuck in a forever cycle of this. But perhaps that is for
the best; who knows?&lt;/p&gt;
&lt;p&gt;I still do get quality contributions over email, just less of them than I would
have otherwise received in the form of a GitHub pull request. And that’s fine
for now.&lt;/p&gt;
&lt;h3&gt;What The Future Holds&lt;/h3&gt;
&lt;p&gt;In the future, I may have figured out my problems and gripes with SourceHut, and
may write a future post about that as well. Stay tuned!&lt;/p&gt;
&lt;p&gt;Fin.&lt;/p&gt;
&lt;section&gt;&lt;h2&gt;Footnotes&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Yeah, don’t do this. The file limits are rather low for GitHub and even with
Git LFS, the actual size of the repo explodes and you won’t be able to host
it eventually. Not to mention who cares about how music projects evolve?
Most people just want the finished product, not shitty half-finished demos. &lt;a href=&quot;#user-content-fnref-music-git&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;I actually gave up using it. The signal-to-noise ratio was laughably low for
completions, and even when I set Codeium off by default, I found that I
would never really enable it. And not because I forgot it existed; I just
didn’t care much for the output even when I enabled it. &lt;a href=&quot;#user-content-fnref-neovim-experience&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;“Fruit of the copyleft tree” is a term I came up with to refer to code that
should be available on request due to GPL/other copyleft licensing. It’s a
play on
“&lt;a href=&quot;https://en.wikipedia.org/wiki/Fruit_of_the_poisonous_tree&quot;&gt;fruit of the poisonous tree&lt;/a&gt;”
in legal cases. I really like this phrase, since I came up with it while
stoned. &lt;a href=&quot;#user-content-fnref-copyleft-tree&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;My belief is that as the size of a platform grows, so does the amount of
slop on it. With ALL of these big tech platforms, user retention tactics
often end up assisting in the creation of more frequent trash content and
end up tanking the signal-to-noise ratio for good content. Content in this
case doesn’t only mean videos/other media, it also means project activity,
like code contributions, bug reports, and documentation updates. &lt;a href=&quot;#user-content-fnref-engagement-quality&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;I actually bought a domain, &lt;a href=&quot;https://cgraph.dev&quot;&gt;cgraph.dev&lt;/a&gt;, which I was
gonna make a small platform-agnostic contribution graph for. However, I
haven’t really worked on this at all, and have been considering just giving
up on it for the same reasons I mention here. &lt;a href=&quot;#user-content-fnref-cgraph&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;If you want to hurt yourself in confusion, see the following
&lt;a href=&quot;https://gitlab.freedesktop.org/mesa/mesa/-/work_items/13736&quot;&gt;AI-generated merge request&lt;/a&gt;
to the Mesa project on GitLab. Some idiot had the absolute fucking GALL to
say they didn’t know what they were doing and were submitting a patch to a
low-level Linux graphics stack ANYWAY. I wish I could throw people off
building sometimes.&lt;/p&gt;
&lt;p&gt;Side note: We should all aspire to be like Timur in that merge request. &lt;a href=&quot;#user-content-fnref-worst-contrib-ever&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;I don’t regard Torvalds as a god. I disagree with him on plenty of things,
but agree with him in this instance about GitHub. Don’t come after me for
citing his ethos. &lt;a href=&quot;#user-content-fnref-linus-god&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Yes, it is possible to merge without the GitHub UI. But then you don’t even
get a way to automatically close the pull request with a “merged” status!
Daniel Stenberg, the &lt;code&gt;curl&lt;/code&gt; maintainer, has mentioned this on his own
&lt;a href=&quot;https://daniel.haxx.se/blog/2020/11/09/this-is-how-i-git/&quot;&gt;blog post on his own Git workflow&lt;/a&gt;,
and you can see how stupid funny it looks by looking at the
&lt;a href=&quot;https://github.com/curl/curl/pulls?q=is%3Apr+is%3Aclosed&quot;&gt;&lt;code&gt;curl&lt;/code&gt; pull request list&lt;/a&gt;,
where they all show up as closed. &lt;a href=&quot;#user-content-fnref-merging-without-ui&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;I did this with NixOS, all the way back in 2020 or so. Ended up wiping my
Void Linux drive by accident from the NixOS installer ISO with &lt;code&gt;dd&lt;/code&gt; and it’s
now my daily driver. So I’m used to this kind of uprooting thing and
learning the hard way :} &lt;a href=&quot;#user-content-fnref-deep-end-nix&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;I seem to have a fondness for cool projects with poor/subpar/sparse docs:
Nix, Zig, and now SourceHut. I have a fucking problem. &lt;a href=&quot;#user-content-fnref-sparse-docs&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;He apparently doesn’t maintain the project anymore, it’s someone else now.
But still, cool fact I guess. &lt;a href=&quot;#user-content-fnref-aerc-maintainer&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;I use &lt;a href=&quot;https://cron-job.org&quot;&gt;cron-job.org&lt;/a&gt; to do this every Saturday, and
all it does is use the SourceHut GraphQL API to send an ad-hoc build
manifest that I have to MANUALLY keep in sync with what’s in the repo.
Genuinely so stupid lol. &lt;a href=&quot;#user-content-fnref-nixed-updates&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;</content:encoded></item><item><title>realpath() Considered Harmful</title><link>https://snare.dev/musings/stop-using-realpath</link><guid isPermaLink="true">https://snare.dev/musings/stop-using-realpath</guid><description>Why realpath() is a terrible function</description><pubDate>Wed, 09 Apr 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;Synopsis&lt;/h2&gt;
&lt;p&gt;Public service announcement for all programmers: &lt;strong&gt;stop using &lt;code&gt;realpath()&lt;/code&gt;&lt;/strong&gt;.
The C function &lt;code&gt;realpath(3)&lt;/code&gt; included in C standard library on most Unix-like
operating systems, whatever equivalent there is for your programming language,
just don’t use it in code in general&lt;sup&gt;&lt;a href=&quot;#user-content-fn-scripts&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;But why?&lt;/p&gt;
&lt;h2&gt;Part I :: Where Did This Begin?&lt;/h2&gt;
&lt;p&gt;I stated down a rabbit hole of research on &lt;code&gt;realpath()&lt;/code&gt; and its various
incarnations when I ran into a bizarre problem while working on my reimagined
&lt;a href=&quot;https://github.com/nix-community/nixos-cli&quot;&gt;NixOS CLI utility&lt;/a&gt;. This is a
rewrite of various assorted scripts throughout the NixOS ecosystem in
Zig&lt;sup&gt;&lt;a href=&quot;#user-content-fn-rewrite&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;, and these scripts often relied on the usage of absolute paths in
order to function. I didn’t question this until I found out that my invocations
of &lt;code&gt;std.fs.realpath()&lt;/code&gt; were causing an instance of
&lt;a href=&quot;https://ziglang.org/documentation/0.14.0/#toc-Illegal-Behavior&quot;&gt;detectable illegal behavior&lt;/a&gt;&lt;sup&gt;&lt;a href=&quot;#user-content-fn-dib&quot;&gt;3&lt;/a&gt;&lt;/sup&gt;
that only occurred in &lt;code&gt;ReleaseSafe&lt;/code&gt; mode, but not in &lt;code&gt;Debug&lt;/code&gt; mode during
development.&lt;/p&gt;
&lt;p&gt;Interestingly, I have never been able to create a minimally reproducible example
of this since then; I assume it has been fixed in some fashion. However, that
doesn’t mean that its usage is not harmful. In fact, Zig is strongly considering
outright &lt;a href=&quot;https://github.com/ziglang/zig/issues/19353&quot;&gt;removing this function&lt;/a&gt;
from its standard library&lt;sup&gt;&lt;a href=&quot;#user-content-fn-inspiration&quot;&gt;4&lt;/a&gt;&lt;/sup&gt;. So, let’s research from what depths of
hell &lt;code&gt;realpath()&lt;/code&gt; came from.&lt;/p&gt;
&lt;h2&gt;Part II :: Abstraction Does Not Mean Portability&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;realpath()&lt;/code&gt; is not a portable construct in most places. The system &lt;code&gt;realpath()&lt;/code&gt;
will have vastly different behavior depending on how a language standard library
implements it.&lt;/p&gt;
&lt;h3&gt;&lt;code&gt;glibc&lt;/code&gt; Implementation&lt;/h3&gt;
&lt;p&gt;Let’s investigate how
&lt;a href=&quot;https://sourceware.org/git/?p=glibc.git;a=blob;f=stdlib/canonicalize.c;h=dca6fca5fb76cc51767364821c3ecf0ab534d725;hb=HEAD#l189&quot;&gt;&lt;code&gt;glibc&lt;/code&gt; implements it&lt;/a&gt;.
This is the implementation details, slightly editorialized from the &lt;code&gt;glibc&lt;/code&gt;
source code&lt;sup&gt;&lt;a href=&quot;#user-content-fn-editorialized&quot;&gt;5&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;// Return the canonical absolute name of file `name`.&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;// A canonical absolute name:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;//   - Must be an absolute path.&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;//   - Cannot contain any &quot;.&quot; nor &quot;..&quot; components&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;//   - Does not contain any repeated filename separators (&quot;/&quot;) or symbolic&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;//     links (symlinks)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;//   - All filename components must exist.&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;// Implementation details:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;//   - If `resolved` is NULL, the result is malloc()&apos;ed. Otherwise, if the&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;//     canonical name is PATH_MAX chars or more, return NULL with `errno` set&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;//     to ENAMETOOLONG.&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;//   - If the name fits in fewer than PATH_MAX chars, return the name in&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;//     `resolved`.&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;//   - If the name cannot be resolved and `resolved` is non-NULL, it contains the&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;//     name of the first component that cannot be resolved.&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;//   - If the name can be resolved, `resolved` holds the same value as the value&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;//     returned.&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;char&lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt; realpath&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; char&lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt; name&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; char&lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt; resolved&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Already getting into quite a lot of platform-specific details. But hold on;
Linux has multiple &lt;code&gt;libc&lt;/code&gt; implementations! What about those?&lt;/p&gt;
&lt;h3&gt;&lt;code&gt;musl&lt;/code&gt; Implementation&lt;/h3&gt;
&lt;p&gt;On the same system, we have &lt;code&gt;musl&lt;/code&gt;. How does &lt;code&gt;musl&lt;/code&gt; implement the same type of
functionality? The source code is
&lt;a href=&quot;https://git.musl-libc.org/cgit/musl/tree/src/misc/realpath.c#n14&quot;&gt;here&lt;/a&gt;, but
this is the summary of the assumptions made and results returned:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;// Resolve a canonical name from a filename.&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;// A canonical absolute name:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;//   - Must be an absolute path.&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;//   - Cannot contain any `&quot;.&quot;` nor `&quot;..&quot;` components&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;//   - Does not contain any repeated filename separators (`&quot;/&quot;`) or symbolic&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;//     links&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;//   - Not all filename components and ancestor directories need to exist; only&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;//     symlinks need to be resolved to existing places.&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;char&lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt; realpath&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; char&lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt; restrict&lt;/span&gt;&lt;span&gt; filename&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; char&lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt; restrict&lt;/span&gt;&lt;span&gt; solved&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Do we see any differences here? Well, yeah! Apparently, two different &lt;code&gt;libc&lt;/code&gt;
implementations on the SAME platform will determine if filename components exist
differently! So &lt;code&gt;realpath()&lt;/code&gt; now doesn’t have an invariant decreeing that
filenames need to exist if they can be resolved by &lt;code&gt;realpath()&lt;/code&gt;. Great, now try
compiling the same application that relies on this supposed &lt;code&gt;glibc&lt;/code&gt; “invariant”
for Alpine Linux, and watch as you get &lt;code&gt;&quot;error: file not exists&quot;&lt;/code&gt; when your
application should have manually been ensuring their existence after resolution
anyway. Amazing! &lt;strong&gt;&lt;em&gt;Not.&lt;/em&gt;&lt;/strong&gt; That is insanity that can only be resolved by a
night out drinking your sorrows away at 3:30 in the morning once you run into it
in a Docker container randomly.&lt;/p&gt;
&lt;h3&gt;Other Language Standard Libraries&lt;/h3&gt;
&lt;p&gt;How do other languages do it then, you may ask? Often times in the WORST fucking
ways possible.&lt;/p&gt;
&lt;p&gt;Take Zig. As new a language as it is, the way that it implements &lt;code&gt;realpath()&lt;/code&gt; on
POSIX systems is just &lt;em&gt;insane&lt;/em&gt;. Here’s the
&lt;a href=&quot;https://github.com/ziglang/zig/blob/ddcf6fcdf3452a4a7d55a27e7112e794447120b6/lib/std/os.zig#L104&quot;&gt;source&lt;/a&gt;,
for reference, yet again.&lt;/p&gt;
&lt;p&gt;I don’t even want to give this the time of day, to be frank. It’s genuinely that
bad. Here’s a snippet from the relevant code, with portions omitted for brevity.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;switch&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;native_os&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    .&lt;/span&gt;&lt;span&gt;windows&lt;/span&gt;&lt;span&gt; =&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        var&lt;/span&gt;&lt;span&gt; wide_buf&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; [&lt;/span&gt;&lt;span&gt;windows&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;PATH_MAX_WIDE&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;u16&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; undefined&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        const&lt;/span&gt;&lt;span&gt; wide_slice&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; try&lt;/span&gt;&lt;span&gt; windows&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;GetFinalPathNameByHandle(&lt;/span&gt;&lt;span&gt;fd&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;{}, &lt;/span&gt;&lt;span&gt;wide_buf&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;..&lt;/span&gt;&lt;span&gt;])&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        const&lt;/span&gt;&lt;span&gt; end_index&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; std&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;unicode&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;wtf16LeToWtf8&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;out_buffer&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;wide_slice&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        return&lt;/span&gt;&lt;span&gt; out_buffer&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;..&lt;/span&gt;&lt;span&gt;end_index&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    },&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    .&lt;/span&gt;&lt;span&gt;macos&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;ios&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;watchos&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;tvos&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;visionos&lt;/span&gt;&lt;span&gt; =&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        // On macOS, we can use F.GETPATH fcntl command to query the OS for&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        // the path to the file descriptor.&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        @memset&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;out_buffer&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;..&lt;/span&gt;&lt;span&gt;max_path_bytes&lt;/span&gt;&lt;span&gt;], &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        switch&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;posix&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;errno&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;posix&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;system&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;fcntl&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;fd&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;posix&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;F&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;GETPATH&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;out_buffer&lt;/span&gt;&lt;span&gt;))) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            // ...&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        // ...&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        const&lt;/span&gt;&lt;span&gt; len&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; mem&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;indexOfScalar&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;u8&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;out_buffer&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;..&lt;/span&gt;&lt;span&gt;], &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;orelse&lt;/span&gt;&lt;span&gt; max_path_bytes&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        return&lt;/span&gt;&lt;span&gt; out_buffer&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;..&lt;/span&gt;&lt;span&gt;len&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    },&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    .&lt;/span&gt;&lt;span&gt;linux&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;serenity&lt;/span&gt;&lt;span&gt; =&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        var&lt;/span&gt;&lt;span&gt; procfs_buf&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; [&lt;/span&gt;&lt;span&gt;&quot;/proc/self/fd/-2147483648&lt;/span&gt;&lt;span&gt;\x00&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;len&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;u8&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; undefined&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        const&lt;/span&gt;&lt;span&gt; proc_path&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; std&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;fmt&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;bufPrintZ&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;procfs_buf&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;..&lt;/span&gt;&lt;span&gt;], &lt;/span&gt;&lt;span&gt;&quot;/proc/self/fd/{d}&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;fd&lt;/span&gt;&lt;span&gt;}) &lt;/span&gt;&lt;span&gt;catch&lt;/span&gt;&lt;span&gt; unreachable&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        const&lt;/span&gt;&lt;span&gt; target&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; posix&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;readlinkZ&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;proc_path&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;out_buffer&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;catch&lt;/span&gt;&lt;span&gt; |&lt;/span&gt;&lt;span&gt;err&lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            // ...&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        }&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        return&lt;/span&gt;&lt;span&gt; target&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    },&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    .&lt;/span&gt;&lt;span&gt;solaris&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;illumos&lt;/span&gt;&lt;span&gt; =&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        var&lt;/span&gt;&lt;span&gt; procfs_buf&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; [&lt;/span&gt;&lt;span&gt;&quot;/proc/self/path/-2147483648&lt;/span&gt;&lt;span&gt;\x00&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;len&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;u8&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; undefined&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        const&lt;/span&gt;&lt;span&gt; proc_path&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; std&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;fmt&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;bufPrintZ&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;procfs_buf&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;..&lt;/span&gt;&lt;span&gt;], &lt;/span&gt;&lt;span&gt;&quot;/proc/self/path/{d}&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;fd&lt;/span&gt;&lt;span&gt;}) &lt;/span&gt;&lt;span&gt;catch&lt;/span&gt;&lt;span&gt; unreachable&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        const&lt;/span&gt;&lt;span&gt; target&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; posix&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;readlinkZ&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;proc_path&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;out_buffer&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;catch&lt;/span&gt;&lt;span&gt; |&lt;/span&gt;&lt;span&gt;err&lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;span&gt; switch&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;err&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            // ...&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        }&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        return&lt;/span&gt;&lt;span&gt; target&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    },&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    .&lt;/span&gt;&lt;span&gt;freebsd&lt;/span&gt;&lt;span&gt; =&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        var&lt;/span&gt;&lt;span&gt; kfile&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; std&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;c&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;kinfo_file&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; undefined&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        kfile&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;structsize&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; std&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;c&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;KINFO_FILE_SIZE&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        switch&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;posix&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;errno&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;std&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;c&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;fcntl&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;fd&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;std&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;c&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;F&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;KINFO&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;@intFromPtr&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&amp;amp;&lt;/span&gt;&lt;span&gt;kfile&lt;/span&gt;&lt;span&gt;)))) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            // ...&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        // ...&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    },&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    .&lt;/span&gt;&lt;span&gt;dragonfly&lt;/span&gt;&lt;span&gt; =&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        switch&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;posix&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;errno&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;std&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;c&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;fcntl&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;fd&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;posix&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;F&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;GETPATH&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;out_buffer&lt;/span&gt;&lt;span&gt;))) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;          // ...&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        // ...&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    },&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    .&lt;/span&gt;&lt;span&gt;netbsd&lt;/span&gt;&lt;span&gt; =&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        @memset&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;out_buffer&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;..&lt;/span&gt;&lt;span&gt;max_path_bytes&lt;/span&gt;&lt;span&gt;], &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        switch&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;posix&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;errno&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;std&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;c&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;fcntl&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;fd&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;posix&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;F&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;GETPATH&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;out_buffer&lt;/span&gt;&lt;span&gt;))) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            // ...&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        // ...&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    },&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    else&lt;/span&gt;&lt;span&gt; =&amp;gt;&lt;/span&gt;&lt;span&gt; unreachable&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Can you tell what’s wrong? The TL;DR: it just gives up and &lt;strong&gt;relies on extremely
platform-specific ways to retrieve an absolute file path given a file
descriptor&lt;/strong&gt; (&lt;code&gt;fd&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;&lt;em&gt;What the fuck???&lt;/em&gt; Who thought of this monstrosity? Apparently we just don’t
care that file descriptors don’t necessarily refer to file paths on disk
anymore. Apparently shared memory and &lt;code&gt;pipe()&lt;/code&gt; don’t exist either. Jesus Christ.
Indeed, they recognize the error of their ways with the following comment:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;/// Calling this function is usually a bug.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Even &lt;em&gt;they&lt;/em&gt; know that this shit is stupid as hell! I mean, just look at the
implementation for Linux, SerenityOS, and Solaris. They construct a link to
&lt;code&gt;/proc/self&lt;/code&gt; for the &lt;code&gt;fd&lt;/code&gt; in question, and they evaluate the symlinks from
there. Genuinely disgusting, since it also doesn’t necessarily take into account
higher process &lt;code&gt;ulimit&lt;/code&gt; values for file descriptors that can overflow the
buffer. But oh well, who cares? I like my buffer overflows!&lt;/p&gt;
&lt;p&gt;At least the other POSIX-compliant OSes use &lt;code&gt;fnctl()&lt;/code&gt; macros properly, but that
will always depend on if the underlying OS &lt;code&gt;libc&lt;/code&gt; will even expose these
operations. At least for those OSes, their &lt;code&gt;libc&lt;/code&gt; is the only way to access
these types of things, so maybe it’s a little more consistent, but hello??&lt;/p&gt;
&lt;p&gt;And not to mention, fun fact, this functionality simply DOESN’T EXIST on some
platforms; OpenBSD is one of them. “&lt;em&gt;Who uses OpenBSD?&lt;/em&gt;”, you may ask. &lt;strong&gt;That’s
not the point!&lt;/strong&gt; The point is that the fucking functionality isn’t available on
all platforms, so basically, when you don’t have a path, and just have the &lt;code&gt;fd&lt;/code&gt;,
you’re shit outta luck.&lt;/p&gt;
&lt;p&gt;So, here’s the real question: &lt;strong&gt;&lt;em&gt;why does &lt;code&gt;realpath()&lt;/code&gt; even exist????&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;Part III :: Rationale&lt;/h2&gt;
&lt;p&gt;There are two primary use cases that I can see for &lt;code&gt;realpath()&lt;/code&gt; being a
potentially useful function, at least from looking around on Google at links
like &lt;a href=&quot;https://stackoverflow.com/questions/28992329/why-use-realpath&quot;&gt;this&lt;/a&gt;:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;To ensure the existence of a path and that it is resolvable (i.e. no symlink
loops)&lt;/li&gt;
&lt;li&gt;Displaying the full file path for a given relative path, for clarity in
logging and/or displaying.&lt;/li&gt;
&lt;li&gt;To prevent
&lt;a href=&quot;https://owasp.org/www-community/attacks/Path_Traversal&quot;&gt;path traversal attacks&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Your software relies on an assumption that the &lt;code&gt;cwd&lt;/code&gt; is unreliable or can
change in certain instances (i.e. going up directories to find the root of a
Git repository or a Nix flake).&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;All of these are potentially useful, but also…they can be achieved in a
variety of ways that don’t need to rely on the underlying operating system for
any assumptions or assumed invariants.&lt;/p&gt;
&lt;h2&gt;Part IV :: What Should I Use, Then?&lt;/h2&gt;
&lt;p&gt;OK you big baby, you want &lt;code&gt;realpath()&lt;/code&gt; and you’re throwing a big fucking tantrum
about it. Cry harder.&lt;/p&gt;
&lt;p&gt;But really, what should one use in place of &lt;code&gt;realpath()&lt;/code&gt; then?&lt;/p&gt;
&lt;p&gt;To be quite frank, &lt;code&gt;realpath()&lt;/code&gt; usage can be avoided entirely in most instances.
How, you may ask? &lt;strong&gt;Just maintain the reference to the filename in the first
place, first off&lt;/strong&gt;. If you are using a language like Zig or Python and are
trying to call &lt;code&gt;realpath()&lt;/code&gt; on some sort of &lt;code&gt;Path&lt;/code&gt;-like data structure, you had
to have been given a path to the file to call &lt;code&gt;realpath()&lt;/code&gt; on it in these
instances. So, why don’t you just use that reference? Don’t even think about
reifying a filename given the file descriptor, or I will personally find your
address and annoy you to hell about it until you damn well stop. That filename
will have enough context for you to construct the real path to the file in a
manual fashion, if you so please.&lt;/p&gt;
&lt;p&gt;As far as I can tell from reading standard library code from various other
languages (including the prior &lt;code&gt;libc&lt;/code&gt; examples), the general way to look for
canonical paths on &lt;em&gt;any&lt;/em&gt; given system given some path is to do the following
steps:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Resolve any &lt;code&gt;..&lt;/code&gt; and &lt;code&gt;.&lt;/code&gt; links.&lt;/li&gt;
&lt;li&gt;Evaluate any symlinks (and resolve their &lt;code&gt;.&lt;/code&gt; and &lt;code&gt;..&lt;/code&gt; references as well) if
needed.&lt;/li&gt;
&lt;li&gt;Continue doing this until a file path that is not a symlink is reached (it
may not necessarily exist, do not assume this is the case ever).&lt;/li&gt;
&lt;li&gt;Append the current &lt;code&gt;cwd&lt;/code&gt; to the path if relative, or leave it alone if it is
absolute.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This approach is much more portable. You can use a stack to resolve &lt;code&gt;.&lt;/code&gt; and &lt;code&gt;..&lt;/code&gt;
references and checking for the presence of a prefixed &lt;code&gt;..&lt;/code&gt; after to prevent
said path traversal attacks can absolve the usage of &lt;code&gt;realpath()&lt;/code&gt; entirely in
most cases. This is especially true when the &lt;code&gt;cwd&lt;/code&gt; of a process doesn’t need to
change at all.&lt;/p&gt;
&lt;p&gt;There is one important caveat though; this is indeed another reason that
&lt;code&gt;realpath()&lt;/code&gt; functionality just shouldn’t exist. It’s step #2: since most
&lt;code&gt;realpath()&lt;/code&gt; implementations work by evaluating symlinks until a canonical path
is realized, what happens if the symlink changes in the middle of evaluation?
Simple. &lt;strong&gt;You’re fucked.&lt;/strong&gt; You’ll end up with behavior that can vary depending
on how the symlink was changed: at worst, you may end up getting a path that you
will assume doesn’t exist when it does, and end up overwriting information
irreversibly. Now granted, it may not be as fatal as that, but the point is
&lt;code&gt;realpath()&lt;/code&gt; is extremely susceptible to race conditions due to underlying
filesystem changes. If you’re confident enough that symlink changes won’t
happen, that’s on you. But this should not be something that is encouraged,
&lt;em&gt;especially&lt;/em&gt; not by a language standard library.&lt;/p&gt;
&lt;p&gt;So, based on that, who will write this code, you may ask?&lt;/p&gt;
&lt;p&gt;&lt;em&gt;You&lt;/em&gt;. You’re smart. You can write this cross-platform path resolution code on
your own if you need it. Needing to assume absolute paths is a fairly uncommon
scenario, and to be frank, IMO it should not be a standard library function in
general because people will end up reaching for it and run into these
OS-specific footguns. &lt;code&gt;realpath()&lt;/code&gt; itself is more an operating system-specific
construct, and should be treated as such.&lt;/p&gt;
&lt;p&gt;I would write this code for an example, but to be honest, I’m lazy. And I also
try &lt;em&gt;not&lt;/em&gt; to rely on &lt;code&gt;realpath()&lt;/code&gt;-like functionality anymore, and just enforce
absolute paths whenever I need them, and manually evaluate symlinks if needed.
Go has a nice, deterministic &lt;code&gt;EvalSymlinks()&lt;/code&gt; function that does this, so that’s
what I’ve been resorting to in most of my Go code, and equivalent functions
elsewhere. Farewell, &lt;code&gt;realpath()&lt;/code&gt;. Rot in hell.&lt;/p&gt;
&lt;section&gt;&lt;h2&gt;Footnotes&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;I am not quite sure about usage of the &lt;code&gt;realpath&lt;/code&gt; command in shell scripts.
Since shell scripts usually tend to not be as cross-platform as programs
written in more general-purpose programming languages, I’ve found that usage
of the &lt;code&gt;realpath&lt;/code&gt; command can be quite useful when running commands ad-hoc.
But I’ll come back to this later and add an extra section about it if I
change my mind. &lt;a href=&quot;#user-content-fnref-scripts&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;I’m rewriting this in Go for many reasons. I’ll probably end up writing a
blog post about this too. &lt;a href=&quot;#user-content-fnref-rewrite&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;“Detectable illegal behavior” is not quite the term that the Zig community
or documentation has solidified on yet, but there is a good amount of
discussion on it in a
&lt;a href=&quot;https://github.com/ziglang/zig/issues/2402&quot;&gt;GitHub issue&lt;/a&gt;. &lt;a href=&quot;#user-content-fnref-dib&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Thanks to &lt;a href=&quot;https://github.com/matklad&quot;&gt;@matklad&lt;/a&gt; on the linked GitHub issue
for providing the inspiration for really writing a post on this topic in
this
&lt;a href=&quot;https://github.com/ziglang/zig/issues/19353#issuecomment-2006795388&quot;&gt;comment&lt;/a&gt;. &lt;a href=&quot;#user-content-fnref-inspiration&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Well, I kinda just took a comment from the source code and I reworded it to
make more sense. &lt;a href=&quot;#user-content-fnref-editorialized&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;</content:encoded></item><item><title>The Book of Neo</title><link>https://snare.dev/musings/the-book-of-neo</link><guid isPermaLink="true">https://snare.dev/musings/the-book-of-neo</guid><description>A Ten Commandments parody for Neovim users</description><pubDate>Fri, 21 Mar 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;Prologue: Assemble, NeoVimmers!&lt;/h2&gt;
&lt;p&gt;Hail, all ye that can see and hear! The &lt;strong&gt;NeoLORD&lt;/strong&gt;&lt;sup&gt;&lt;a href=&quot;#user-content-fn-neolord&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; hath spoken to me
upon &lt;strong&gt;Mount Joy&lt;/strong&gt;&lt;sup&gt;&lt;a href=&quot;#user-content-fn-mountjoy&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;, and hath given me instructions for all to follow,
lest they be slaughtered by proprietors and condemned forever to the unfree
software of their fellow slaves’ toils. The future does not need us&lt;sup&gt;&lt;a href=&quot;#user-content-fn-thefuture&quot;&gt;3&lt;/a&gt;&lt;/sup&gt;;
let us heed these commands written upon this buffer and treat our present days
with the dignity they deserve.&lt;/p&gt;
&lt;h2&gt;The Zen Commandments&lt;/h2&gt;
&lt;p&gt;Thus saith the &lt;strong&gt;NeoLORD&lt;/strong&gt;:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Thou shalt abide by the philosophy of my right hand&lt;sup&gt;&lt;a href=&quot;#user-content-fn-unix&quot;&gt;4&lt;/a&gt;&lt;/sup&gt;: “Thou shalt do
one thing, and do it well.” The &lt;code&gt;$EDITOR&lt;/code&gt; is a craft, created to create. It
is the crafter of crafters. Do not fall into the traps of the cursèd devil
which shall not be named, and do more than is intended. I am the &lt;strong&gt;NeoLORD&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Thou shalt not use editors other than the venerable Neovim; for I the
&lt;strong&gt;NeoLORD&lt;/strong&gt; am a jealous God, and will destroy all that create with no regard
to their true creator. I am the &lt;strong&gt;NeoLORD&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Thou shalt abstain from usage of VimScript in favor of Lua. The venerable
VimScript will be kept alive, since he is still useful, but beware; old
wineskins will break eventually. I am the &lt;strong&gt;NeoLORD&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Thou shalt make the fullest use of the tools that the &lt;strong&gt;NeoLORD&lt;/strong&gt; has
graciously provided to you. Give LSP and Treesitter their due homage. I am
the &lt;strong&gt;NeoLORD&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Honor plugin maintainers with the utmost respect. They are not subject to
your fleeting whims, and are free to do as they please with the fruits of
their labor. Must a conflict arise, thou shalt resolve thy disagreements
through civil discussion. Take care, and wield the mighty fork&lt;sup&gt;&lt;a href=&quot;#user-content-fn-fork&quot;&gt;5&lt;/a&gt;&lt;/sup&gt; with
caution. I am the &lt;strong&gt;NeoLORD&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Beware the temptresses of useless plugins. Do not let them sour your desire
to create. I am the &lt;strong&gt;NeoLORD&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Thou shalt respect Vim and its contributions to the world, and upstream any
applicable patches, for the spirit of Bram shall live forever and ever. I am
the &lt;strong&gt;NeoLORD&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Thou shalt not act in sloth, lest you perish in the bottomless pits of the
lazy Inferno&lt;sup&gt;&lt;a href=&quot;#user-content-fn-inferno&quot;&gt;6&lt;/a&gt;&lt;/sup&gt;. The &lt;strong&gt;NeoLORD&lt;/strong&gt; your God is not a lazy&lt;sup&gt;&lt;a href=&quot;#user-content-fn-lazy&quot;&gt;7&lt;/a&gt;&lt;/sup&gt;God;
thou shalt instead weave thy configurations by hand. For the lazy folk are
unable to partake in the &lt;strong&gt;NeoLORD’s&lt;/strong&gt; wisdom, and their fingers will rot
from confusion. I am the &lt;strong&gt;NeoLORD&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Thou shalt not blaspheme against the spirit of modality, for whoever disowns
the other modes in favor of insert-only mode shall never be forgiven until
they repent of their ways.&lt;/li&gt;
&lt;li&gt;Do not distress over the transgressions of the ignorant, for everyone is a
sinner in the &lt;strong&gt;NeoLORD’s&lt;/strong&gt; presence. Instead, lead them towards repentance
and the narrow gate of &lt;em&gt;change&lt;/em&gt;. I am the &lt;strong&gt;NeoLORD&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;The Simpleton’s Zen Commandments&lt;/h2&gt;
&lt;p&gt;To all Neovim programmers: follow these commands.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Do one thing, and do it well. Don’t be like Emacs.&lt;/li&gt;
&lt;li&gt;Don’t use editors other than Neovim, or else.&lt;/li&gt;
&lt;li&gt;Use Lua rather than VimScript.&lt;/li&gt;
&lt;li&gt;Use the native LSP and Treesitter functionality.&lt;/li&gt;
&lt;li&gt;Don’t be an asshole to plugin maintainers. If you don’t like what they’re
doing, or they don’t do what you want, fork the damn thing and do it yourself
if you must.&lt;/li&gt;
&lt;li&gt;Don’t add more plugins than you need to. It’s easy to fall into config hell.&lt;/li&gt;
&lt;li&gt;Upstream any patches that are in Neovim core to Vim if possible. Both editors
are great. One is just better.&lt;/li&gt;
&lt;li&gt;Don’t use Neovim distributions like LazyVim if you want to learn Neovim
functionality properly. Make your configurations yourself.&lt;/li&gt;
&lt;li&gt;Use the modes to your advantage! Normal mode and visual mode are much more
powerful than you give them credit for.&lt;/li&gt;
&lt;li&gt;Lots of people don’t use Neovim. It’s fine, you didn’t know what Neovim was
for a while either. Spread the good news and tell them to use it too!&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Epilogue&lt;/h2&gt;
&lt;p&gt;This is just a joke that my friends and I wrote in a Discord voice call. Don’t
take it too seriously.&lt;/p&gt;
&lt;p&gt;Credit to &lt;a href=&quot;https://www.linkedin.com/in/zach-howe-016603173/&quot;&gt;Zachary Howe&lt;/a&gt; for
co-authoring some of these commandments with me.&lt;/p&gt;
&lt;p&gt;If you want to see more content like this,
&lt;a href=&quot;https://snare.dev/about&quot;&gt;send me a message&lt;/a&gt;! I’d love to hear more ideas for
niche, satirical content. Maybe the &lt;strong&gt;NeoLORD’s&lt;/strong&gt; commission is in need of
writing.&lt;/p&gt;
&lt;section&gt;&lt;h2&gt;Footnotes&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;The &lt;em&gt;NeoLORD&lt;/em&gt; is T.J. DeVries (aka Teej). Teej, if you’re reading this, I
don’t know why my friends and I made you the &lt;strong&gt;NeoLORD&lt;/strong&gt;. But you were the
funniest to reference. &lt;a href=&quot;#user-content-fnref-neolord&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;A reference to Bill Joy, the creator of &lt;code&gt;vi&lt;/code&gt;. &lt;a href=&quot;#user-content-fnref-mountjoy&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;A reference to
&lt;a href=&quot;https://sites.cc.gatech.edu/computing/nano/documents/Joy%20-%20Why%20the%20Future%20Doesn%27t%20Need%20Us.pdf&quot;&gt;The Future Does Not Need Us&lt;/a&gt;,
a publication, also authored by Bill Joy. &lt;a href=&quot;#user-content-fnref-thefuture&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The &lt;a href=&quot;https://en.wikipedia.org/wiki/Unix_philosophy&quot;&gt;UNIX Philosophy&lt;/a&gt;. &lt;a href=&quot;#user-content-fnref-unix&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Forking plugins and/or maintaining patches for the same plugins is a good
idea when you’re in complete disagreement with a maintainer over something
large and highly technical. Neovim itself is a fork of Vim, so they can be
successful in practice. But just wield caution when doing so. &lt;a href=&quot;#user-content-fnref-fork&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Dante’s &lt;em&gt;Divine Comedy&lt;/em&gt;. Yes, the hell references are definitely good for
this. &lt;a href=&quot;#user-content-fnref-inferno&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;This refers to Neovim distributions such as LazyVim. &lt;a href=&quot;#user-content-fnref-lazy&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;</content:encoded></item><item><title>The Unchristian</title><link>https://snare.dev/musings/the-unchristian</link><guid isPermaLink="true">https://snare.dev/musings/the-unchristian</guid><description>A retelling of why I left the church and am now atheist.</description><pubDate>Sat, 08 Jun 2024 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;Part 0: Hi there.&lt;/h2&gt;
&lt;p&gt;Disclaimer: If you’ve come here from the random depths, hello there. This is one
of my more thought-out pieces that is filled with a considerable amount of
personal details, so if you feel uncomfy, it’s fine to click off. I don’t mind
at all. If you’re still here, I’m glad you want to know more about the
particular struggles I have had with faith, namely of the Christian variety.&lt;/p&gt;
&lt;p&gt;First off, if you’re a Christian, or even simply a person of faith of some sort
other than that, I implore you, &lt;em&gt;please don’t click off this page&lt;/em&gt;. I know
deconstruction is a rather scary word for many, but please give this a try; it
took a lot of effort for me to even gather the courage to speak about this.&lt;/p&gt;
&lt;p&gt;My goal is not to convince you of anything. Instead, I aim to simply tell a
story, or more aptly for this context, a &lt;em&gt;testimony&lt;/em&gt;. &lt;strong&gt;My&lt;/strong&gt; testimony. This
isn’t clickbait, I can promise that, so please, humor me, and feel free to
contact me to talk about it after.&lt;/p&gt;
&lt;p&gt;My journey through faith has been something that has followed me my entire
lifetime. It’s been a wild ride, really, filled with ups and downs that are only
matched by ungodly creations like the
&lt;a href=&quot;https://en.wikipedia.org/wiki/Euthanasia_Coaster&quot;&gt;Euthanasia Coaster&lt;/a&gt;. Well,
that may be hyperbolic, but I digress. Not only has it been wild, it has been a
long time coming, and I think it’s time I talked about it.&lt;/p&gt;
&lt;p&gt;This is a piece about my journey as someone who was raised in the Christian
church. Over a long period of time, I’ve started to question the value of it
all, and why I believe what I believe. This lead me down a painful journey of
deconstruction that was almost 6 years in the making, and eventually led to my
deconversion.&lt;/p&gt;
&lt;h2&gt;Part I: Faith&lt;/h2&gt;
&lt;p&gt;I was raised a Christian by my family. My mother was (and still is) a devout
Christian, having converted when she was living in India along with her older
sister; however, my dad was not. My older sister and I were raised in the
church, and we were exposed to all the doctrines from early age; in fact, my
mother even told me that the first book I learned to read was the Bible (I had a
children’s version of it and would read it all the time; I even have vague
recollections of doing this). I was fascinated by all the stories, and when I
was told they were true and without errors, I believed it wholeheartedly.&lt;/p&gt;
&lt;p&gt;My mother even told me that I myself &lt;em&gt;was&lt;/em&gt; a miracle when I was grown up, that I
was diagnosed with a whole swath of developmental disorders, but most of them
had disappeared by the time I was four years old. I had no reason to doubt that,
especially when I was young; after all, I knew I could kind of interact with
people in a semi-coherent way, even though I still had problems with it.&lt;/p&gt;
&lt;p&gt;From the beginning, I was taught to read my Bible. In fact, it was a fixture of
my daily routine; in the morning, drink some warm water, brush your teeth, and
open your Bible. Read one chapter per day, learn something new, every day. As a
child, I did complain about this routine, but I later on grew to love it. It was
my bread, just as the Lord’s Prayer spoke of; I lived by its words. It could
speak of no wrong in my eyes, it was that foundational. After all, I read the
magical words, “You shall love your neighbor as yourself” (Matthew 22:39), and I
was entranced in this moral direction; it can’t be wrong, can it? With all the
things it gets right, I couldn’t even comprehend the idea that this could have
been wrong; in fact, I never did for most of my life.&lt;/p&gt;
&lt;p&gt;I went to a single non-denominational church for most of my life, and attended
almost every week with my mother and sister. Since I was taught about the faith
from an early age, I was already convinced of the idea that Jesus had died and
resurrected for my sins. In fact, after I understood what a “baptism” was, and
after my sister was baptized, I begged my mother for &lt;em&gt;years&lt;/em&gt; upon years to do
the same. Since I was in elementary school at the time, she simply told me
“You’re not ready yet”; this cycle continued until 2016, when I was in middle
school. I was baptized then, in an auditorium filled with people I knew.&lt;/p&gt;
&lt;p&gt;At my baptism, I was asked a single question that many millions of people had
been asked prior: “Do you believe Jesus Christ died for your sins and was
resurrected from the dead?”&lt;/p&gt;
&lt;p&gt;I replied, “Of course!” &lt;em&gt;What, you think He didn’t?&lt;/em&gt;, I had in my mind. The
auditorium laughed along in approval of my emphatic response. And underneath the
water I went, and came back up to the sound of cheering. It felt magical, as if
I had won the game of life while still being so young. Little did I know that my
response to this question was one that I would laugh at myself for, given some
time.&lt;/p&gt;

&lt;figure&gt; &lt;img src=&quot;/_astro/jesus-baptism.D10uWrDO_1hrWYP.webp&quot; alt=&quot;Illustration of Jesus getting baptized by John&quot; loading=&quot;lazy&quot; width=&quot;500&quot; height=&quot;300&quot; /&gt; &lt;u&gt; &lt;i&gt; &lt;figcaption&gt;I really did feel like Jesus getting baptized by John at that moment&lt;/figcaption&gt; &lt;/i&gt; &lt;/u&gt; &lt;/figure&gt;
&lt;p&gt;Regardless of that, after I came out of the water, I felt as if Jesus Himself
had come down into the water and whispered into my ears, “Well done, my good and
faithful servant!” (Matthew 25:23), and had opened a literal doorway in my
chest. Nothing could replicate that. I was redeemed; a new child had been born
out of the filth. It was time to rejoice in His presence!&lt;/p&gt;
&lt;p&gt;Around this same time, I auditioned for my church’s worship band. I am an avid
drummer, and contemporary Christian worship songs from artists like Hillsong and
Elevation were extremely simple for me to pick up and learn on the drum kit. My
worship pastor took me on, and I started playing music with them almost every
single week. I connected with some people of faith that was almost divine in my
eyes, and I was in awe of their abilities, as well as their disposition for
faith, and aspired to be like them. Needless to say, I was a devout Christian
for most of my young life, and there was no letting go of that, ever.&lt;/p&gt;
&lt;p&gt;Or so I thought.&lt;/p&gt;
&lt;h2&gt;Part II: Doubt&lt;/h2&gt;
&lt;p&gt;Let’s rewind a few years back from here.&lt;/p&gt;
&lt;p&gt;Around my fifth-grade year (2014), a close uncle of mine saw that I was a very
curious person. I had always been since birth, asking questions about anything
and everything, though unsurprisingly I never thought of doing the same thing in
regard to my faith. He gave me a swath of books, all from an author named Ken
Ham. I didn’t think much of this name, but I was addicted to reading, so
naturally, I picked these books up after a while. I learned about the idea that
there were people out there that didn’t have the same perspectives as me, and
that I needed to defend myself against these people; I could defend myself using
“knowledge!”&lt;sup&gt;&lt;a href=&quot;#user-content-fn-knowledge&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; After all, there was a mountain of evidence for God’s
existence, I needed just to “Fear not, for [He] is with me” (Isaiah 41:10).&lt;/p&gt;
&lt;p&gt;I read these books front to back, arming myself with arguments against the
people who dared to question God. &lt;em&gt;How could these people question my faith?&lt;/em&gt;, I
asked myself over and over again. Yet, it didn’t feel right as I was reading
these books. Something was off. But I didn’t think much of it. Having been
taught a literalist interpretation of the Bible, I regarded all types of doubt
about my faith as demonic, and regarded Satan as a real, scary entity that could
easily lead me to a cliff and throw me off into the valley of the shadow of
death (Psalms 23:4).&lt;/p&gt;
&lt;p&gt;Interestingly, this sentiment was not shaken even as I had my first encounter
with an idea from the faith that I disagreed with. I listen to metal (and many
other genres, check out my &lt;a href=&quot;https://www.last.fm/user/water-sucks&quot;&gt;last.fm&lt;/a&gt; for
some recs), and I first encountered it around my sixth grade school year (2014).
When this same close uncle that gave me the Ken Ham books heard about this, he
took me aside privately and started scolding me, telling me that “metal music
was a way for the devil to find his way into my soul,” and that when the devil
does that, his only goal is to “steal, kill, and destroy” (John 10:10). I took
issue with this, as I loved my music, and was even listening to mostly Christian
or Christian-influenced metal bands such as August Burns Red and Underoath
(great bands, by the way; you should listen to them!). However, out of trust for
this uncle, I tried to stop listening to my favorite bands, and transitioned
over to other genres of music.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Spoiler alert&lt;/em&gt;: this transition to other types of music didn’t work; I still
listen to metal today.&lt;/p&gt;

&lt;figure&gt; &lt;img src=&quot;/_astro/brr-brr-deng.Dpnkkq4n_1OweAR.webp&quot; srcset=&quot;/_astro/brr-brr-deng.Dpnkkq4n_Z2vxhsN.webp 640w&quot; alt=&quot;Mudvayne brr brr deng meme&quot; loading=&quot;lazy&quot; width=&quot;400&quot; height=&quot;362&quot; /&gt; &lt;u&gt; &lt;i&gt; &lt;figcaption&gt;Yeah, it was definitely this dude that made me ditch religion.&lt;/figcaption&gt; &lt;/i&gt; &lt;/u&gt; &lt;/figure&gt;
&lt;p&gt;Coincidentally, I discovered many other bands through this process, like Saosin
and other bands that I still listen to today, so maybe this was a blessing in
disguise. Regardless, I was disheartened by this blatant dismissal of my own
feelings around the music I loved to listen to, but my faith was still strong in
spite of that; I just pushed those feelings to the back of my mind and continued
on believing.&lt;/p&gt;
&lt;p&gt;I held fast to a creationist, literalist view of the Bible, and this was not
shaken at all by any teachings about “unbiblical” stuff like evolution that I
was exposed to in my science classes until around my freshman year of high
school (2017). I was having a conversation with one of my best friends on the
phone, and we were just talking about random things. We veered into religion
sometime during this (for context, he was a casually-practicing Hindu), and I
brought up the fact that I believed in creationism and that God created the
Earth in six 24-hour days. He simply laughed at me, saying:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;You seriously believe in that shit, Varun?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I fought back against this, saying I had evidence, and tried to bring up what I
had learned from the apologetics books I was given. This was my first active
application of those concepts; I had to know what I was talking about, right? I
left that conversation thinking I had won my case, but that first little seed of
doubt was planted in my heart right then and there, and I was not aware at &lt;em&gt;all&lt;/em&gt;
of what havoc it would end up wreaking on my life.&lt;/p&gt;
&lt;p&gt;A few weeks later, I still found myself mildly shaken up at this conversation. I
decided to go on Google and just start going down rabbit holes, searching for
anything related to what we were talking about. I started with searches like:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;“Why is evolution true?”&lt;/li&gt;
&lt;li&gt;“Why is creationism true?”&lt;/li&gt;
&lt;li&gt;“Why is evolution false?”&lt;/li&gt;
&lt;li&gt;“Why is creationism false?”&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;But this eventually turned into more of the following searches:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;“Contradictions in the Bible”&lt;/li&gt;
&lt;li&gt;“Refutations of contradictions in the Bible”&lt;/li&gt;
&lt;li&gt;“Why is the Bible true?”&lt;/li&gt;
&lt;li&gt;“How was the Bible made?”&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Cut me some slack, I don’t remember the &lt;em&gt;exact&lt;/em&gt; keywords. I don’t think
15-year-old me would have understood what “refutation” meant, but I digress. You
get the idea, hopefully. I would go down these rabbit holes for hours a day,
just reading, absorbing whatever information I could find. But that hole still
remained in my heart, and I couldn’t understand why this was the case.&lt;/p&gt;
&lt;p&gt;I didn’t know it at the time, but these were the beginnings of &lt;strong&gt;doubt&lt;/strong&gt;, and it
was something I had feared for a long time in this realm. I had always regarded
hell as a literal entity, and was scared shitless of it; after all, I was just a
kid. What would happen if I doubted, and that led me to commit the worst of all
sins, the &lt;a href=&quot;https://en.wikipedia.org/wiki/Eternal_sin&quot;&gt;unpardonable sin&lt;/a&gt; (Luke
12:10)? So I kept reading my Bible, and kept trying to be devout. I struggled
heavily with my mental health throughout all of my high school years, and these
seeds of doubt were one of the primary contributors towards it. I also struggled
with suicidal ideation, and tried to publically commit suicide when I was around
16 years old, which eventually led to me being placed on medications. This
didn’t exactly help the case for my doubt.&lt;/p&gt;
&lt;p&gt;On a side note: I still struggle with depression and suicidal ideation today,
although it has been quite a while since I’ve acted on those urges. I’ve learned
some strategies to deal with it through therapy and other forms of support,
which is nice.&lt;/p&gt;
&lt;p&gt;I kept reading my Bible, which was an annotated version from a pastor named
David Jeremiah that I was gifted. While I wouldn’t mark it up, I kept its
additional commentary in the margins close to my heart. Many times passed where
I would read it front to back; One year, I attended
&lt;a href=&quot;https://alpha.org/about/&quot;&gt;Alpha&lt;/a&gt;, a course targeted at Christians and
unbelievers alike to teach the fundamentals of the Christian faith. I was in a
group with my campus lead pastor as the leader of it, and we were discussing
quite often the core tenets of the Christian faith. &lt;em&gt;Anything to quell the very
doubt that would send me to the abyss&lt;/em&gt;, I thought.&lt;/p&gt;

&lt;figure&gt; &lt;img src=&quot;/_astro/david-jeremiah-bible.DDh9FbCO_ZYhVAV.webp&quot; srcset=&quot;/_astro/david-jeremiah-bible.DDh9FbCO_1UjOGm.webp 640w, /_astro/david-jeremiah-bible.DDh9FbCO_Z1JiY1U.webp 750w, /_astro/david-jeremiah-bible.DDh9FbCO_Z2pEMjI.webp 828w, /_astro/david-jeremiah-bible.DDh9FbCO_1FbyQ9.webp 1080w, /_astro/david-jeremiah-bible.DDh9FbCO_Z1iUkxc.webp 1280w&quot; alt=&quot;My personal Bible, with commentary from David Jeremiah&quot; loading=&quot;lazy&quot; width=&quot;300&quot; height=&quot;400&quot; /&gt; &lt;u&gt; &lt;i&gt; &lt;figcaption&gt;My personal Bible, with commentary from David Jeremiah&lt;/figcaption&gt; &lt;/i&gt; &lt;/u&gt; &lt;/figure&gt;
&lt;p&gt;My beliefs bled over to my high school environment as well, also in particularly
unlikable ways. While I was in marching band, I developed a budding friendship
with someone from the color guard that also happened to attend the same church
that I did. We talked a lot, and ended up vibing so much that I even developed
some slight feelings for her. She eventually told me that she was lesbian, and I
was mortified. I couldn’t even &lt;em&gt;comprehend&lt;/em&gt; how someone could attend church and
simultaneously be part of the LGBTQ+ community. I was taught that these people
are to be regarded as sinners. We ended up going back and forth about this
multiple times, and eventually I let my true feelings at the time slip: the
age-old adage of “Love the sin, hate the sinner.” I wouldn’t know at the time
(much less why it would be the case), but she was hurt by that, and our
friendship wilted slowly.&lt;/p&gt;
&lt;p&gt;This nagged me for a while. I didn’t know what I did to hurt her, but I was
still under the impression that people like her just wanted to live a life of
sin. This contributed more to the doubts in my mind. Would Jesus seriously
condone what I did to her? I, myself, was also a sinner, yet I cast the first
stone. I forced myself to look at different perspectives, the same way I forced
myself to look at the theory of evolution online. That’s all I could find solace
in: learning.&lt;/p&gt;
&lt;p&gt;The thoughts, experiences, and consequences as a result of these could never
leave me alone. I was constantly thinking about them, and the impact that they
had on my life, and as this happened, I finally realized I needed to do one
thing, more than anything else: I needed to dig into my foundations to see what
was buried in it, and if it was worth believing.&lt;/p&gt;
&lt;h2&gt;Part III: Deconstruction&lt;/h2&gt;

&lt;figure&gt; &lt;img src=&quot;/_astro/scrambled-road.IP4O9VcF_AoJU2.webp&quot; alt=&quot;Man standing at beginning of scrambled road&quot; loading=&quot;lazy&quot; width=&quot;419&quot; height=&quot;450&quot; /&gt;  &lt;/figure&gt;
&lt;p&gt;So begins the journey of the often ill-fated term,
&lt;a href=&quot;https://en.wikipedia.org/wiki/Faith_deconstruction&quot;&gt;“deconstruction”&lt;/a&gt;. I was
frankly terrified of this concept at first; in fact, I didn’t even know what it
truly meant or what the word for the process even was until a year into the
process of starting it. I simply knew that I wanted reassurance that what I
believed in what was correct, and that I could apply that truth to my life.&lt;/p&gt;
&lt;p&gt;At the time, I was on Reddit a considerable amount, and often used it as a
central forum of resources I could gather from. Subreddits like
&lt;a href=&quot;https://reddit.com/r/OpenChristian&quot;&gt;r/OpenChristian&lt;/a&gt;,
&lt;a href=&quot;https://reddit.com/r/Exvangelical&quot;&gt;r/Exvangelical&lt;/a&gt;, among others caught my eye,
and I made posts there regularly about different topics, like how I had stopped
seeing the “magic” in contemporary Christian music and other worship music after
having played it live for so long&lt;sup&gt;&lt;a href=&quot;#user-content-fn-ccm&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;, and how I could have better beliefs in
certain topics while still having it reconcile with the Bible that I had always
known.&lt;/p&gt;
&lt;p&gt;One day, I was helping my church build out a new stage for worship, and the
conversation among us shifted to the topic of evolution. When this came up, I
finally mentioned that I really wanted to know how I could reconcile the Bible’s
claims and the reality of certain scientific concepts like evolution. When I
said that, my worship pastor looked at me, not in disapproval, but with a face
of happiness. I was confused; wasn’t I doubting something that shouldn’t be
doubted? He told me this:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Doubting and asking is good for your faith, Varun.
God is more than willing to answer your questions.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;That wasn’t the exact quote, but that was the essence I took away from the
conversation we had. He recommended me a book to read, namely
&lt;a href=&quot;https://books.google.com/books/about/Irresistible.html?id=3oEoDwAAQBAJ&quot;&gt;&lt;em&gt;Irresistible&lt;/em&gt;&lt;/a&gt;,
by Andy Stanley, a pastor. I Googled it, and to my initial dismay, most of the
searches that came up were write-ups by conservative and fundamentalist
organizations like the Gospel Coalition that warned me about the heretical
beliefs that are supposedly espoused by Stanley in this book. That made me more
intrigued than ever; I knew about infighting between Christians, but had never
seen such vehement dismissal of certain ones before. The
&lt;a href=&quot;https://en.wikipedia.org/wiki/Streisand_effect&quot;&gt;Streisand effect&lt;/a&gt; had taken me
over, and it made me that much more excited to read this. So I bought the
book&lt;sup&gt;&lt;a href=&quot;#user-content-fn-irresistible&quot;&gt;3&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;When I finished reading, I noticed a sense of catharsis that was hanging over my
mind. It was freeing, almost as if I could think for myself regarding my own
faith for the very first time. However, I was almost angry at my church, at the
surrounding Christians that told me there was only &lt;em&gt;one&lt;/em&gt; way in which I could
follow my God. The teachings of Jesus were infinitely more important for me to
follow than any Old Testament teachings; after all, the carpet with all the
uncleanliness has been rolled up into heaven (Acts 10:9-16)! I felt deceived
with this newfound ability to think. &lt;em&gt;Knowledge was a curse&lt;/em&gt;, I thought; &lt;em&gt;that’s
the way ignorance is bliss, right&lt;/em&gt;? This anger did eventually settle, and the
only feeling I was left with was peace. My faith was changed forever, but for
the better.&lt;/p&gt;

&lt;figure&gt; &lt;img src=&quot;/_astro/man-standing-in-front-of-god.Dvd_Jueg_1kRMyD.webp&quot; alt=&quot;Man standing in front of God&quot; loading=&quot;lazy&quot; width=&quot;512&quot; height=&quot;512&quot; /&gt; &lt;u&gt; &lt;i&gt; &lt;figcaption&gt;My interrogation of God was commencing.&lt;/figcaption&gt; &lt;/i&gt; &lt;/u&gt; &lt;/figure&gt;
&lt;p&gt;After reading this book, I let go of the idea of Biblical inerrancy. I saw it
for how limiting it was for my own knowledge, and even for my own faith. If the
Bible was only inspired by God, but written by humans, the mistakes I saw from
before actually make sense! I now understood Christianity through the lens of
humanity, not just through the words of the Bible. Instead of reading the Bible
in an &lt;a href=&quot;https://en.wikipedia.org/wiki/Eisegesis&quot;&gt;&lt;em&gt;eisegetical&lt;/em&gt;&lt;/a&gt; manner, I now
sought to read it in an &lt;a href=&quot;https://en.wikipedia.org/wiki/Exegesis&quot;&gt;&lt;em&gt;exegetical&lt;/em&gt;&lt;/a&gt;
manner&lt;sup&gt;&lt;a href=&quot;#user-content-fn-exegesis&quot;&gt;4&lt;/a&gt;&lt;/sup&gt;. As for scientific concepts like evolution, I finally saw the
evidence for them far outweighed the beliefs I had in the creationism and
anti-scientific, fundamentalist variants of faith that I had once espoused. In
summary, I was now a progressive Christian who deeply sympathized with the
tenets of
&lt;a href=&quot;https://en.wikipedia.org/wiki/Liberation_theology&quot;&gt;liberation theology&lt;/a&gt;. I even
contacted the friend I had offended previously, and profusely thanked them for
dealing with my own naivety with such a kind heart. I was ecstatic about all
these developments; it meant a new era for my faith.&lt;/p&gt;
&lt;p&gt;This did not spell the end of my deconstruction process, though. It was only the
beginning.&lt;/p&gt;
&lt;p&gt;At one point, I had to re-evaluate my perspectives on hell. I could not
fundamentally reconcile this with the idea of a
&lt;a href=&quot;https://en.wiktionary.org/wiki/tri-omni&quot;&gt;tri-omni&lt;/a&gt; God, and eventually I found
the rationalizations for hell quite sickening and incoherent. There was no way
in hell (pun intended) I could justify a God being simultaneously omnibenevolent
and willing to judge an inferior creation of His to an infinite punishment for
the finite crimes over less than 120 years, at maximum. After all, He will take
care of us with more diligence so than He ever will for the simple sparrow
(Matthew 6:25-34), no? Through more digging, I found the ideas of Christian
universalism, and read about St. Augustine and his ideas surrounding the
doctrine. I was again ecstatic to find that there were others out there that had
the same ideas as I, and was convinced of the goodness of God once more. My mind
was pliable, and ready to learn more and more.&lt;/p&gt;
&lt;p&gt;The COVID pandemic only served to give me more time to ruminate about these
ideas. With the many hours I had just lying around, I took the time to just
research everything I possibly could. I found out about the Apocrypha, along
with other non-canonical books such as the
&lt;a href=&quot;https://en.wikipedia.org/wiki/Gospel_of_Thomas&quot;&gt;Gospel of Thomas&lt;/a&gt; and other
&lt;a href=&quot;https://en.wikipedia.org/wiki/Nag_Hammadi_library&quot;&gt;Gnostic texts&lt;/a&gt;. My
perspectives on faith were changing by the minute.&lt;/p&gt;
&lt;p&gt;Around this time (c. 2020-2021), I graduated from high school, and I started to
dig heavily into philosophy and other related subjects. I took a philosophy
class at my community college, and it was radically eye-opening to the different
epistemological perspectives that existed through the times, including those of
non-Christian ones that I had not really familiarized myself with yet. I took
the time to learn about
&lt;a href=&quot;https://en.wikipedia.org/wiki/S%C3%B8ren_Kierkegaard&quot;&gt;Søren Kierkegaard&lt;/a&gt;,
&lt;a href=&quot;https://en.wikipedia.org/wiki/Albert_Camus&quot;&gt;Albert Camus&lt;/a&gt;,
&lt;a href=&quot;https://en.wikipedia.org/wiki/Friedrich_Nietzsche&quot;&gt;Friedrich Nietzsche&lt;/a&gt;, and
the likes of other existentialist philosophers, and held the reasoning of
&lt;a href=&quot;https://en.wikipedia.org/wiki/Ren%C3%A9_Descartes&quot;&gt;Descartes&lt;/a&gt; for why there was
a good God dear to my heart, since it seemed like a bulletproof defense of the
concept. However, the walls were falling down all around me as I was exposed to
every single one of these ideas, and they were all so compelling in their
explanatory power and prowess, without the ontological baggage of the Bible as a
whole. I was at a crossroads&lt;sup&gt;&lt;a href=&quot;#user-content-fn-crossroads&quot;&gt;5&lt;/a&gt;&lt;/sup&gt; for my beliefs, without even knowing
it.&lt;/p&gt;
&lt;h2&gt;Part IV: End of an Era&lt;/h2&gt;
&lt;p&gt;I didn’t know it at the time, but I had subscribed to a
&lt;a href=&quot;https://en.wikipedia.org/wiki/God_of_the_gaps&quot;&gt;“God of the gaps”&lt;/a&gt;-like
theology&lt;sup&gt;&lt;a href=&quot;#user-content-fn-gotg&quot;&gt;6&lt;/a&gt;&lt;/sup&gt;, and kept this way of thinking throughout my time at community
college after I had graduated from high school. I couldn’t let go of the idea,
even I knew in the back of my head this was a flawed presupposition to have.
However, there was a big development that I had noticed over the times that my
beliefs had changed. I found myself less convinced of some concepts that were in
the Bible; there were parts of it that I had once accepted as being irrelevant
to the current message that now seemed irreconcilable with the fundamental
assumptions about the God I worshiped that I had:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;If there is a benevolent God, why is there so much evil that comes to those
who do not deserve it (i.e. children)?&lt;/li&gt;
&lt;li&gt;If there is an omniscient and/or omnipotent God, how can he change His mind?
Does that not make Him non-omniscient and/or non-omnipotent?&lt;/li&gt;
&lt;li&gt;Where is God, and why won’t he talk to me, despite my cries out to Him?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;As you can tell, these were the fundamental questions of old; the problem of
&lt;a href=&quot;https://en.wikipedia.org/wiki/Problem_of_evil&quot;&gt;teleological evil&lt;/a&gt;, the
&lt;a href=&quot;https://en.wikipedia.org/wiki/Epicurean_paradox&quot;&gt;Epicurean paradox&lt;/a&gt;, the
problem of
&lt;a href=&quot;https://en.wikipedia.org/wiki/Argument_from_nonbelief&quot;&gt;divine hiddenness&lt;/a&gt;, and
the like. They were now questions that seemed completely unanswerable with even
the most convoluted of theodicies. I was wrapping my head over and out in
cognitive dissonance about the very fundamentals of the things that I believed.
Sixth-grade me would have been terrified; however, I now had the desire to
question even that which I was not to question. The “God of the gaps” theology I
had at the time was still there in my mind, but that gap was shrinking, and it
was shrinking &lt;em&gt;fast&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;What was also fundamentally different was that I couldn’t even bring myself to
talk about God as if I believed in Him anymore. I would keep my faith status
private, and when people asked me about it, I would just say it was a private
matter, without elaborating further. I still went to church (and in fact was
volunteering in some capacity almost every single week, whether it was playing
drums, or running camera equipment, or something else). It was a little
draining, since I didn’t exactly listen to the preaching, and didn’t share many
of the same beliefs as I used to; after all, it was still a non-denominational
church with relatively conservative beliefs. For almost a full year between 2022
and 2023, I kept going to church like this with no further
problems&lt;sup&gt;&lt;a href=&quot;#user-content-fn-kept-going&quot;&gt;7&lt;/a&gt;&lt;/sup&gt;. I called myself an agnostic privately, saying that I was
unable to truly know if there was a God or not since that idea was
unfalsifiable; however, I wanted for it to be true, so that I didn’t have to
lose my faith of old. There was one person at the same church I was going to
that I confided this in, who himself had a similar view.&lt;/p&gt;
&lt;p&gt;The principal problem that I initially failed to see was that the “God of the
gaps”-like theology that I held was a &lt;em&gt;black box&lt;/em&gt;; it stifled the desire to
learn. I would be held back by the assumption that something is because of God,
without the desire to go learn why that was regardless of God. The phrase that
captures this the most is likely the tired, old adage that many Christians and
atheists alike have heard at some point in their lives:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;God works in mysterious ways.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;figure&gt; &lt;img src=&quot;/_astro/man-staring-at-wall.D3DSHqEx_ZSi6dG.webp&quot; alt=&quot;Man staring at a brick wall&quot; loading=&quot;lazy&quot; width=&quot;498&quot; height=&quot;347&quot; /&gt;  &lt;/figure&gt;
&lt;p&gt;This phrase was straight-up painful to hear after a while; it was as mentally
painful as running straight into a brick wall. The sheer NUMBER of times I’ve
heard this phrase is astounding, and it’s such a damn cop-out. How can you
simultaneously claim to know God’s intentions through the Bible, and only resort
to this adage when you can’t explain something within the framework that you
have adopted? It’s as if the person who lets this phrase slip has a fear of the
unknown. It &lt;em&gt;has&lt;/em&gt; to work in some way because of &lt;em&gt;my&lt;/em&gt; convictions. God
absolutely had some sort of intention or causation behind this; we just didn’t
know it yet, and we will once we do meet Him.&lt;/p&gt;
&lt;p&gt;I had trouble squaring this thought with myself, but I simply just did for years
upon end while I was still a Christian. I did this at all phases of my faith
journey; it didn’t matter when I was a full-on young-earth creationist and
fundamentalist, or when I was someone who leaned more towards an agnostic
Christian perspective later on. Anything that I didn’t know a reason for was
sent to a black box of fundamental assumptions I had about the nature of the
world, never to be seen again, for God simply had a reason that I could not
fathom, either for my own good or because I was not powerful enough to know; I
did this for many of the questions I raised above. For a while, that is.&lt;/p&gt;
&lt;p&gt;Until I couldn’t.&lt;/p&gt;
&lt;p&gt;Divine hiddenness was the problem that pushed me over the edge. I prayed
constantly in my head, even when I was agnostic. I just wanted what I considered
to be a quite low expectation of an omnipotent being: a convincing explanation
or experience that would unambiguously show me that this God that I had believed
in for almost my entire life had a real presence. If you’ve ever heard the quote
from William Lane Craig, of
&lt;a href=&quot;https://www.reasonablefaith.org/writings/question-answer/raising-and-lowering-the-epistemic-bar&quot;&gt;“If there’s one’s chance in a million that Christianity is true, it’s worth believing,”&lt;/a&gt;
that was exactly what I had in mind. I prayed for signs, held on to my
confirmation biases, held on to what my mother had told me when I was young:
that I myself was a miracle in God’s eyes. I did it &lt;em&gt;all&lt;/em&gt;. You would think that
as an all-powerful being, He would be able to do this without overriding free
will or whatever the next theodicy will like to claim; to say that this God
cannot do something was simply a poverty of imagination on behalf of the
apologist making the claim&lt;sup&gt;&lt;a href=&quot;#user-content-fn-poi&quot;&gt;8&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;But He never did, and that only made me incredibly sad. In my times of weakness,
in my times when I wanted to throw myself off a bridge, or commit suicide in
other ways, He was the defining example of silence. No response for my earnest
efforts for proof in my darkest moments, when I was about to succumb to the
worst act in regard to existence; in any other realm, I would have abandoned
that idea for good, but why was I not doing that in this instance, where this
God was so simultaneously incontrovertible in my head, yet effectively
nonexistent?&lt;/p&gt;
&lt;p&gt;I don’t remember when this happened, but I consciously came to the realization I
was just uncomfortable with the words, &lt;em&gt;“I don’t know”&lt;/em&gt;. I can say this phrase
for so many other fields; the scientific realm, as a developer, as a drummer,
composer, you name it. The unknowable is just that: &lt;em&gt;unknowable&lt;/em&gt;. There’s
nothing you can do; every proposition that is unknowable is in a superposition
of sorts, but what effect can it have on me if that superposition can’t collapse
into something knowable? Simply put:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;You don’t have to know everything.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Being able to say that about things is magical. It’s lifting an epistemological
load that I had to bear for years trying to justify what I couldn’t through
convoluted chains of theodicies. I started getting content on my YouTube feed
from absolutely phenomenal creators such as
&lt;a href=&quot;https://www.youtube.com/channel/UC6BLQ4Po0euDOZq9gUgkWAQ&quot;&gt;Forrest Valkai&lt;/a&gt; who
changed my perspective radically on what it meant to even know something, and
the theodicies started fading into the distance; I now found solace in the great
unknown.&lt;/p&gt;
&lt;p&gt;And so concluded my journey. I couldn’t answer the theological questions I put
forth precisely because they looked like contradictions. And
well…&lt;a href=&quot;https://en.wikipedia.org/wiki/Duck_test&quot;&gt;if it looks like a duck, walks like one, and quacks like one&lt;/a&gt;,
then what else could it really be? If I truly wanted to decrease the amount of
assumptions I held about the world, I had to let go of this untenable divine
solution to a problem that we humans have created. There was no concrete day
when this became the perspective that I became convinced of; it was just a
thought I had in the back of my mind at first around the beginning of 2022, but
it became stronger in its convincing power gradually. And as you can tell by the
nature of this post, it prevailed.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;I no longer believed in God&lt;/strong&gt; (2023).
&lt;a href=&quot;https://en.wikipedia.org/wiki/God_is_dead&quot;&gt;God was dead to me, and I had killed him with my own hands&lt;/a&gt;.
I had
&lt;a href=&quot;http://4umi.com/nietzsche/zarathustra/46&quot;&gt;ripped him out of my throat&lt;/a&gt;&lt;sup&gt;&lt;a href=&quot;#user-content-fn-throat&quot;&gt;9&lt;/a&gt;&lt;/sup&gt;
after a 6-year battle with my own thoughts, from the end of 2017 to the end
of 2023.&lt;sup&gt;&lt;a href=&quot;#user-content-fn-caps&quot;&gt;10&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;There’s an important note to consider at the end of this process. This was not a
choice that I consciously made. I simply was less and less convinced of the
reality of the Christian god over time. I do not believe that you can choose
your beliefs. Ironic, but my belief in this concept is most succinctly captured
by this quote:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;A man can do what he wills, but he cannot will what he wills.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;→ &lt;em&gt;Arthur Schopenhauer&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;Part V: What Now?&lt;/h2&gt;
&lt;p&gt;Where do I go from here?&lt;/p&gt;
&lt;p&gt;It’s not enough to say that I’ve stopped believing in the Christian god. This
way of living had influenced my life for 20 years at this point, so how was I
going to approach my newfound lack of faith that I had finally come to terms
with in my own head?&lt;/p&gt;
&lt;p&gt;That’s a hard question. &lt;em&gt;I still don’t know&lt;/em&gt;&lt;sup&gt;&lt;a href=&quot;#user-content-fn-idk&quot;&gt;11&lt;/a&gt;&lt;/sup&gt;, to be honest. I wish I had a
simple answer, but there are quite a few circumstances I need to take into
account.&lt;/p&gt;
&lt;p&gt;For one, I’m already outing myself as an atheist on here. Since I live in the
United States, and have many family members and friends that are quite devout
Christians, this is more of a big deal than if I were to do this being raised in
a primarily atheist environment like Germany or something like that. I’m sure
that many of my relatives, including my mother and my sister, will be rather
disappointed, and some may cry in response to reading this post. And for that,
I’m truly sorry. But in truth, it’s better for both them and I to face reality
than for me to keep pretending. It’s just kicking the can down the road. Maybe
some of them will come to me and tell me to listen to their favorite apologists,
or call me a fool for not believing (Psalms 14:1). However, it’s almost trivial
to reconcile any contradictions in the Bible, or to come up with some sort of
theodicy in response to a posed problem. The question is if it is tenable to
hold this fragile stack of reconciliations and theodicies together, and I don’t
think it is anymore. Feel free to &lt;a href=&quot;/about&quot;&gt;converse with me&lt;/a&gt; if we want to have
a productive conversation about this though, I’m always open to talk about faith
even if it is not something I have anymore, and I will never, &lt;em&gt;ever&lt;/em&gt; berate
someone for being a believer like the atheist caricature depicts.&lt;/p&gt;
&lt;p&gt;I am still playing in churches as a drummer, and still attend my first church
almost &lt;em&gt;every single week&lt;/em&gt;. Part of this is to keep up a ruse to my Christian
loved ones, friends, and coworkers that nothing is wrong with my faith, but I
will also be losing some relationships with people that I would prefer not to
lose&lt;sup&gt;&lt;a href=&quot;#user-content-fn-networking&quot;&gt;12&lt;/a&gt;&lt;/sup&gt;. I have come to terms with the fact that I will not be playing
drums at churches anymore if I were to leave this church I attend, but I haven’t
come to terms yet with potentially losing some of my friends that I still want
to keep in touch with. Presumably, once I do come to terms with this, I will
leave. I’ve already told myself that I want to leave at the end of the month
twice in these past two months; perhaps the third time will be the charm. I will
update this page once I end up leaving the church, but if you don’t see a notice
below this paragraph telling you about this, I’m still going to church almost
every Sunday because I don’t have the balls to end the relationship.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;UPDATE&lt;/strong&gt;: I have indeed left the church, on July 7, 2024. It took almost three
months for me to pull myself together enough to do it. I guess I grew the pair
of balls that I needed :}&lt;/p&gt;
&lt;p&gt;Additionally, I’m working at a company right now whose express goals are to help
churches, and most of my coworkers are all of Christian faith. I could
potentially be losing out on relationships I’ve made at work, and I do have to
keep in mind that I could be losing my job as well. So if you’re reading this,
and you’re working at the same place as me, &lt;strong&gt;please don’t be alarmed by this
post&lt;/strong&gt;. I am still willing to work with anyone regardless of their faith and
goals, provided I still see potential. My lack of faith should not hold you all
back from being able to work with me to achieve your goals. If it does, then so
be it, and I am sorry that this is the way it had to end.&lt;/p&gt;
&lt;p&gt;There are many more external consequences for losing faith, but this is already
long enough as it is. I also have to become comfortable with myself losing
faith; after all, it took almost a year before I could come face to face with
the thought that I had lost it in the first place. What are the frameworks that
I will be using to continue living? Many religious people will often claim that
as an atheist, I will have
&lt;a href=&quot;https://en.wikipedia.org/wiki/Argument_from_morality&quot;&gt;no moral framework&lt;/a&gt;, or
that I will have no
&lt;a href=&quot;https://en.wikipedia.org/wiki/Meaning_of_life#Christianity&quot;&gt;purpose in life&lt;/a&gt;,
or other such claims. Unfortunately for them, religion does not have a monopoly
on morals or meaning. How will I live my life then, you may ask? Great question.&lt;/p&gt;
&lt;p&gt;As for morals, I’ve adopted a much more flexible look at them. Morals are an
extremely nuanced subject; after all, we make moral decisions based on a large
variety of factors, but they are still reasons for deeming something morally
good or bad. I’m not going to deny it; emotions and rationality have everything
to do with morality, and embracing that reality is only going to be a good
thing. Doubting my own faculties for rationality is also a good exercise that I
will continue to do. After all, the only thing I know is that
&lt;a href=&quot;https://en.wikipedia.org/wiki/I_know_that_I_know_nothing&quot;&gt;I know nothing&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;There are still some things I take away from my former faith. I am a huge fan of
the idea that you should love your neighbor as yourself. However, now I don’t
need to burden myself with the excess baggage of religious duties, faux
humility&lt;sup&gt;&lt;a href=&quot;#user-content-fn-humility&quot;&gt;13&lt;/a&gt;&lt;/sup&gt; in the whole “I am a sinner and not worthy” manner, and the
historical and current atrocities done in the name of God; I can simply take the
parts that are relevant and apply them to my life, just as we do with any other
principle or situation we see in life. No need for special cases anymore and
twisting my mind into a pretzel as to why God would allow slavery (Leviticus
25:44-46) or the killing of innocent children (Genesis 7:21-23, Numbers,
31:1-18, and many more), while forbidding genuine love (Leviticus 18:22,
20:13)&lt;sup&gt;&lt;a href=&quot;#user-content-fn-lgbtq&quot;&gt;14&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;The second step I need to take is also going to be a lifelong process. You may
have seen the little animated quote on the front page of this little place I
write in:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;I seek to be free from the concept of meaning, to carve my own reality.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;But what does it mean to be free from meaning? I have yet to discover this, but
as far as I know, I have abandoned the idea that there is any meaning that is
prescribed to me by a divine being, or by the universe, or some other
inconceivable thing. Instead, I want to free myself of the chains that society
have placed upon me, that I must do something of “worth” in order for my life to
have meaning. As for what that entails, &lt;em&gt;I don’t know&lt;/em&gt;; I am simply willing to
find out. I want to embrace my almost-lost inquisitiveness, the one I used to
have as a child without any remorse, and let it take me on a journey.&lt;/p&gt;
&lt;p&gt;“&lt;em&gt;I don’t know&lt;/em&gt;” is indeed a beautiful statement. It’s not a cry to find out an
answer, but only a gentle affirmation of the truth as it stands within a given
context. I will be leaning back on this cornerstone for as long as I live.&lt;/p&gt;
&lt;p&gt;I leave you with a quote that has influenced me through this entire journey:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Think about it, think! It ain’t illegal yet. Think!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;→ &lt;em&gt;Eddie Griffin&lt;/em&gt;&lt;/p&gt;
&lt;section&gt;&lt;h2&gt;Footnotes&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;This wasn’t knowledge in the traditional sense of the word, but rather a
strict regurgitation of arguments. I don’t think that most apologia is in
good taste, and serves as post-hoc rationalization for pre-existing belief,
rather than the primary reason for coming to faith. &lt;a href=&quot;#user-content-fnref-knowledge&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;I really used to like playing contemporary Christian music and other worship
music on stage. But after a while, I grew to hate the simplicity of it. It
was just boring and felt uncreative. The “magic” I refer to here is the idea
of feeling “the Holy Spirit” while listening to this music. The truth is, I
felt much more emotion and much more alive listening to
&lt;a href=&quot;https://www.youtube.com/watch?v=jh5iinG-CtE&quot;&gt;Pulse/Surreal&lt;/a&gt; by Lantlôs than
I ever did to Christian music I played on stage, because I saw through the
veil of how formulaic and uninspired it was. I could go on for days about
this, to be honest. &lt;a href=&quot;#user-content-fnref-ccm&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;My sister bought the book for me. If you’re reading this, thanks dude. That
was $15 that changed my life. &lt;a href=&quot;#user-content-fnref-irresistible&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;It’s hard to draw the lines between what constitutes each one of these,
which only adds to the cognitive turmoil I had. &lt;a href=&quot;#user-content-fnref-exegesis&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Ironically, the first church I went to was named Crossroads. Guess I really
had it coming. &lt;a href=&quot;#user-content-fnref-crossroads&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;In reality, “God of the gaps” is more a subset of the
&lt;a href=&quot;https://en.wikipedia.org/wiki/Argument_from_ignorance&quot;&gt;argument from ignorance&lt;/a&gt;
logical fallacy. &lt;a href=&quot;#user-content-fnref-gotg&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;I don’t quite know why I kept going despite disagreeing with the faith. I
was maybe interested in how people on the other side thought, and in how I
myself used to think. Perhaps I was just trying to look through a window to
the past. I’m not sure, even as of the time of writing. &lt;a href=&quot;#user-content-fnref-kept-going&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;I mean this with full intention. Anyone saying that God does not have the
power to do something, while simultaneously claiming said God is omnipotent
simply does not know what they are talking about, or are loading their words
with some other hidden connotations. I’d like to be proven wrong. &lt;a href=&quot;#user-content-fnref-poi&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;I am a huge fan of Nietzsche’s story &lt;em&gt;The Vision And The Enigma&lt;/em&gt;. There’s a
depiction of a shepherd ripping a snake out of his throat, where the snake
represented the
&lt;a href=&quot;https://en.wikipedia.org/wiki/Eternal_return#Nietzsche&apos;s_formulation&quot;&gt;eternal recurrence&lt;/a&gt;
that the shepherd was scared of. I’m alluding to that here, since Nietzsche
is one of the philosophers that most resonated with me post-deconversion.
The prior “I killed him” reference is also from his writings. &lt;a href=&quot;#user-content-fnref-throat&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Notice the change in capitalization. Just a little Easter egg for you :}. &lt;a href=&quot;#user-content-fnref-caps&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;I said “I don’t know”. That was the joke. Whoosh. &lt;a href=&quot;#user-content-fnref-idk&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Networking is already hard for software engineers, and I got my most recent
connections through churches. Needless today, it’ll only become harder if I
lose my existing connections. I would love to work for you if you are
hiring, here’s my
&lt;a href=&quot;https://raw.githubusercontent.com/water-sucks/resume/main/resume.pdf&quot;&gt;résumé&lt;/a&gt;
just in case! &lt;a href=&quot;#user-content-fnref-networking&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;If you have to express your humility in any way vocally that explicitly
denigrates your own lack of worth or your own failings, it’s not humility
anymore. It’s self-hate. Loving your own neighbor as yourself requires you
to love yourself first; ironic coming from a dude who struggles with
self-esteem issues, but that only makes it more poignant a point. &lt;a href=&quot;#user-content-fnref-humility&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;I support the LGBTQ+ community. As an
&lt;a href=&quot;https://en.wikipedia.org/wiki/Asexuality&quot;&gt;asexual&lt;/a&gt; dude, it’s hard not to. &lt;a href=&quot;#user-content-fnref-lgbtq&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;</content:encoded></item><item><title>Zig Comptime Is Absurdly Cool</title><link>https://snare.dev/musings/zig-comptime-reflection</link><guid isPermaLink="true">https://snare.dev/musings/zig-comptime-reflection</guid><description>A little snippet of raving over Zig&apos;s metaprogramming capabilities</description><pubDate>Tue, 13 Aug 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;strong&gt;NOTE&lt;/strong&gt;: If you’re on a mobile browser, I recommend reading this in horizontal
mode or on a laptop for a better experience.&lt;/p&gt;
&lt;h2&gt;Part 0 :: Synopsis&lt;/h2&gt;
&lt;p&gt;You may have heard of &lt;a href=&quot;https://ziglang.org&quot;&gt;Zig&lt;/a&gt;, a new (-ish?) systems
programming language. It markets itself as being extremely simple, and its
mission is to create a language where there is one obvious way to do things.
Quite possibly the antithesis to C++ in its pursuit of simplicity.&lt;/p&gt;
&lt;p&gt;I’ve been using it to write my own
&lt;a href=&quot;https://github.com/nix-community/nixos-cli&quot;&gt;CLI replacement for NixOS tools&lt;/a&gt;,
named &lt;code&gt;nixos&lt;/code&gt; (boring, perhaps, but gets the job done). It’s been a pleasure
thus far, despite the lack of an extant ecosystem for dependencies like Rust or
Go. It’s forced me to solve problems that I would have otherwise reached out to
dependencies for, and overall is making me better at software. I’ll probably
save the personal glazing for another post, though.&lt;/p&gt;
&lt;h2&gt;Part I :: &lt;code&gt;comptime&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;Compile-time semantics can be pretty hard to grok. More often than not, most
languages that have some form of a strong type system will force you to learn
the intricacies and limitations of said type system.&lt;/p&gt;
&lt;p&gt;This is one of the biggest features that Zig has to offer: clear compile-time
semantics without the usage of macros, code gen, or a weird in-between language
like C++ templates. I use the word “language” liberally here; if we define
language as a signal-based mechanism for communication, then C++ templates can
quite possibly be the poorest way of expressing this concept&lt;sup&gt;&lt;a href=&quot;#user-content-fn-templates&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;. But I
digress.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;comptime&lt;/code&gt; in Zig is just a way of running normal Zig code at compile time.
There’s no other language to understand, no preprocessor to fight with, just Zig
code that runs when you run &lt;code&gt;zig build&lt;/code&gt;. Basically, anywhere that requires a
value be known at compile time (array sizes, types, etc.), you can pass a
&lt;code&gt;comptime&lt;/code&gt; value, and it should just work. This makes it extremely simple to do
things like use types as normal values, reflection, generics, and many more
features that are available only in higher-level programming languages or in
lower-level languages with macros. Additionally, it’s much easier to read and
write since LSP autocomplete exists already (it’s normal Zig code, after all).&lt;/p&gt;
&lt;p&gt;Take for example the following piece of code:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;pub&lt;/span&gt;&lt;span&gt; fn&lt;/span&gt;&lt;span&gt; runtimeOnlyDiv&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;comptime&lt;/span&gt;&lt;span&gt; T&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; type&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; T&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;b&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; T&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;T&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  if&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;T&lt;/span&gt;&lt;span&gt; ==&lt;/span&gt;&lt;span&gt; comptime_int&lt;/span&gt;&lt;span&gt; or&lt;/span&gt;&lt;span&gt; T&lt;/span&gt;&lt;span&gt; ==&lt;/span&gt;&lt;span&gt; comptime_float&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    @compileError&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;you big dumb dumb, don&apos;t use comptime values in this function!&quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  return&lt;/span&gt;&lt;span&gt; a&lt;/span&gt;&lt;span&gt; /&lt;/span&gt;&lt;span&gt; b&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This allows one to divide numbers with this function, while restricting
&lt;code&gt;comptime&lt;/code&gt; types from being passed to the function. While being quite
superfluous (like what the fuck would you even use this for?), this demonstrates
the fact that types are values at compile-time, and can thus be operated on like
any other value within that context. In fact, this is exactly how the
&lt;code&gt;ArrayList&lt;/code&gt; data structure is implemented!&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;pub&lt;/span&gt;&lt;span&gt; fn&lt;/span&gt;&lt;span&gt; ArrayList(&lt;/span&gt;&lt;span&gt;comptime&lt;/span&gt;&lt;span&gt; T&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; type&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  return&lt;/span&gt;&lt;span&gt; ArrayListAligned(&lt;/span&gt;&lt;span&gt;T&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A function that returns a type! How can this be? Well, an &lt;code&gt;ArrayList&lt;/code&gt; is just a
struct under the hood with the given type as its field. This function can be
placed anywhere a type is expected, since it returns a type, and Zig will
evaluate this so that it fills in the correct type. This is called
&lt;a href=&quot;https://en.wikipedia.org/wiki/Monomorphization&quot;&gt;monomorphization&lt;/a&gt;, and is more
or less what C++ and Rust do with their templates and traits, but expressed as a
simple function! I vastly prefer this form of generics over C++ and Rust since
it reads much simpler to me. I don’t have to rip my brains out to understand it.&lt;/p&gt;
&lt;p&gt;A trippy result of this is that even parameter types can be expressions that
generate types!&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;pub&lt;/span&gt;&lt;span&gt; fn&lt;/span&gt;&lt;span&gt; set&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;self&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; Self&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;comptime&lt;/span&gt;&lt;span&gt; T&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; ValueType&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;context&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; NixContext&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;value&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;switch&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;T&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  .&lt;/span&gt;&lt;span&gt;int&lt;/span&gt;&lt;span&gt; =&amp;gt;&lt;/span&gt;&lt;span&gt; i64&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  .&lt;/span&gt;&lt;span&gt;float&lt;/span&gt;&lt;span&gt; =&amp;gt;&lt;/span&gt;&lt;span&gt; f64&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  .&lt;/span&gt;&lt;span&gt;bool&lt;/span&gt;&lt;span&gt; =&amp;gt;&lt;/span&gt;&lt;span&gt; bool&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  .&lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;path&lt;/span&gt;&lt;span&gt; =&amp;gt;&lt;/span&gt;&lt;span&gt; [&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; u8&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  .&lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;span&gt; =&amp;gt;&lt;/span&gt;&lt;span&gt; @TypeOf&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;span&gt;),&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  .&lt;/span&gt;&lt;span&gt;list&lt;/span&gt;&lt;span&gt; =&amp;gt;&lt;/span&gt;&lt;span&gt; Value&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  else&lt;/span&gt;&lt;span&gt; =&amp;gt;&lt;/span&gt;&lt;span&gt; @compileError&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;type &apos;&quot;&lt;/span&gt;&lt;span&gt; ++&lt;/span&gt;&lt;span&gt; @tagName&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;T&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;++&lt;/span&gt;&lt;span&gt; &quot;&apos; cannot be used for the set method&quot;&lt;/span&gt;&lt;span&gt;),&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;})) &lt;/span&gt;&lt;span&gt;!&lt;/span&gt;&lt;span&gt;void&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; err&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; switch&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;T&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    .&lt;/span&gt;&lt;span&gt;int&lt;/span&gt;&lt;span&gt; =&amp;gt;&lt;/span&gt;&lt;span&gt; libnix&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;nix_set_int&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;context&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;context&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;self&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;value&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;value&lt;/span&gt;&lt;span&gt;),&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    .&lt;/span&gt;&lt;span&gt;float&lt;/span&gt;&lt;span&gt; =&amp;gt;&lt;/span&gt;&lt;span&gt; libnix&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;nix_set_float&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;context&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;context&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;self&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;value&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;value&lt;/span&gt;&lt;span&gt;),&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    .&lt;/span&gt;&lt;span&gt;bool&lt;/span&gt;&lt;span&gt; =&amp;gt;&lt;/span&gt;&lt;span&gt; libnix&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;nix_set_bool&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;context&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;context&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;self&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;value&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;value&lt;/span&gt;&lt;span&gt;),&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    .&lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;span&gt; =&amp;gt;&lt;/span&gt;&lt;span&gt; libnix&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;nix_set_string&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;context&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;context&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;self&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;value&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;value&lt;/span&gt;&lt;span&gt;),&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    .&lt;/span&gt;&lt;span&gt;path&lt;/span&gt;&lt;span&gt; =&amp;gt;&lt;/span&gt;&lt;span&gt; libnix&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;nix_set_path_string&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;context&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;context&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;self&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;value&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;value&lt;/span&gt;&lt;span&gt;),&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    .&lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;span&gt; =&amp;gt;&lt;/span&gt;&lt;span&gt; libnix&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;nix_set_null&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;context&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;context&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;self&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;value&lt;/span&gt;&lt;span&gt;),&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    else&lt;/span&gt;&lt;span&gt; =&amp;gt;&lt;/span&gt;&lt;span&gt; @panic&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;value cannot be of type &quot;&lt;/span&gt;&lt;span&gt; ++&lt;/span&gt;&lt;span&gt; @typeName&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;@TypeOf&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;value&lt;/span&gt;&lt;span&gt;))),&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  }&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  if&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;err&lt;/span&gt;&lt;span&gt; !=&lt;/span&gt;&lt;span&gt; 0&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; nixError&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;err&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Even if you’ve never read Zig before, this is probably decently easy to read if
you are somewhat proficient in programming.&lt;/p&gt;
&lt;p&gt;Well, where does this really show its beauty? Generics, sure, but what about
anything else? Well, I mentioned &lt;code&gt;comptime&lt;/code&gt; can also be a solution for
compile-time reflection that does not involve macros or code gen.&lt;/p&gt;
&lt;h2&gt;Part II :: The Situation&lt;/h2&gt;
&lt;p&gt;In my &lt;code&gt;nixos&lt;/code&gt; CLI, I have a &lt;code&gt;Config&lt;/code&gt; struct that is filled in from a TOML
configuration file. It looks like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;pub&lt;/span&gt;&lt;span&gt; const&lt;/span&gt;&lt;span&gt; Config&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; struct&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  apply&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; struct&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    imply_impure_with_tag&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; bool&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; false&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    specialisation&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; ?&lt;/span&gt;&lt;span&gt;[]&lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; u8&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; null&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    use_nom&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; bool&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; false&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  } &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; .&lt;/span&gt;&lt;span&gt;{},&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  config_location&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; []&lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; u8&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; &quot;/etc/nixos&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  enter&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; struct&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    mount_resolv_conf&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; bool&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; true&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  } &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; .&lt;/span&gt;&lt;span&gt;{},&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  init&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; struct&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    enable_xserver&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; bool&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; false&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    desktop_config&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; ?&lt;/span&gt;&lt;span&gt;[]&lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; u8&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; null&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    extra_config&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; ?&lt;/span&gt;&lt;span&gt;[]&lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; u8&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; null&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  } &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; .&lt;/span&gt;&lt;span&gt;{},&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  no_confirm&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; bool&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; false&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  use_nvd&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; bool&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; false&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Nothing too crazy, just a struct with some values. I left out some more complex
types for the sake of simplicity.&lt;/p&gt;
&lt;p&gt;A cool feature that I wanted to implement is setting configuration values using
command-line arguments. &lt;code&gt;git&lt;/code&gt; does this, where an invocation like
&lt;code&gt;git -c commit.gpgsign=false commit&lt;/code&gt;, will disable GPG signing for just that one
invocation when committing your code. This enables flexibility for when
misconfigurations happen with your GPG key, and you just want to create an
unsigned commit without changing your configuration.&lt;/p&gt;
&lt;p&gt;However, Git has a ton of options. Just tab through with &lt;code&gt;git -c&lt;/code&gt; in &lt;code&gt;zsh&lt;/code&gt;, and
you get this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt; git&lt;/span&gt;&lt;span&gt; -c&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;zsh:&lt;/span&gt;&lt;span&gt; do&lt;/span&gt;&lt;span&gt; you&lt;/span&gt;&lt;span&gt; wish&lt;/span&gt;&lt;span&gt; to&lt;/span&gt;&lt;span&gt; see&lt;/span&gt;&lt;span&gt; all&lt;/span&gt;&lt;span&gt; 621&lt;/span&gt;&lt;span&gt; possibilities&lt;/span&gt;&lt;span&gt; (69 &lt;/span&gt;&lt;span&gt;lines&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Oh, hell no. &lt;em&gt;There’s no way &lt;code&gt;git&lt;/code&gt; parsed those options manually&lt;/em&gt;, I thought to
myself. That 69 is just the number of top-level &lt;code&gt;git&lt;/code&gt; options! The number of
options in my &lt;code&gt;Config&lt;/code&gt; struct seemed like they would only grow and get more
nested. I don’t want to keep going back to that every time a setting is added!&lt;/p&gt;
&lt;p&gt;I thought about how to solve this for a while. Then, all of a sudden, a
lightbulb went off in my head. I could just use compile-time reflection to
traverse the struct fields given a path.&lt;/p&gt;
&lt;p&gt;The result:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;fn&lt;/span&gt;&lt;span&gt; setFieldValue&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;comptime&lt;/span&gt;&lt;span&gt; T&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; type&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;path&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; []&lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; u8&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;value&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; []&lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; u8&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;ptr&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; *&lt;/span&gt;&lt;span&gt;T&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;!&lt;/span&gt;&lt;span&gt;void&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  switch&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;@typeInfo&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;T&lt;/span&gt;&lt;span&gt;)) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    .&lt;/span&gt;&lt;span&gt;Struct&lt;/span&gt;&lt;span&gt; =&amp;gt;&lt;/span&gt;&lt;span&gt; |&lt;/span&gt;&lt;span&gt;structInfo&lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      inline&lt;/span&gt;&lt;span&gt; for&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;structInfo&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;fields&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;span&gt;field&lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        if&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;std&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;mem&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;startsWith&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;u8&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;path&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;field&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;)) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;          const&lt;/span&gt;&lt;span&gt; rest&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; path&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;field&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;len&lt;/span&gt;&lt;span&gt;..&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;          if&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;rest&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;len&lt;/span&gt;&lt;span&gt; ==&lt;/span&gt;&lt;span&gt; 0&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            if&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;field&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt; ==&lt;/span&gt;&lt;span&gt; bool&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;              const&lt;/span&gt;&lt;span&gt; parsed&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; blk&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                if&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;mem&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;eql&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;u8&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;value&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;true&quot;&lt;/span&gt;&lt;span&gt;)) &lt;/span&gt;&lt;span&gt;break&lt;/span&gt;&lt;span&gt; :&lt;/span&gt;&lt;span&gt;blk&lt;/span&gt;&lt;span&gt; true&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                if&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;mem&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;eql&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;u8&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;value&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;false&quot;&lt;/span&gt;&lt;span&gt;)) &lt;/span&gt;&lt;span&gt;break&lt;/span&gt;&lt;span&gt; :&lt;/span&gt;&lt;span&gt;blk&lt;/span&gt;&lt;span&gt; false&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                return&lt;/span&gt;&lt;span&gt; error&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;InvalidBoolean&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;              }&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;              @field&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;ptr&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;field&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; parsed&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            } &lt;/span&gt;&lt;span&gt;else&lt;/span&gt;&lt;span&gt; if&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;field&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt; ==&lt;/span&gt;&lt;span&gt; []&lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; u8&lt;/span&gt;&lt;span&gt; or&lt;/span&gt;&lt;span&gt; field&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt; ==&lt;/span&gt;&lt;span&gt; ?&lt;/span&gt;&lt;span&gt;[]&lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; u8&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;              if&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;field&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt; ==&lt;/span&gt;&lt;span&gt; ?&lt;/span&gt;&lt;span&gt;[]&lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; u8&lt;/span&gt;&lt;span&gt; and&lt;/span&gt;&lt;span&gt; value&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;len&lt;/span&gt;&lt;span&gt; ==&lt;/span&gt;&lt;span&gt; 0&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                @field&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;ptr&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;field&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; null&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;              } &lt;/span&gt;&lt;span&gt;else&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                @field&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;ptr&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;field&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; value&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;              }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;              return&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            } &lt;/span&gt;&lt;span&gt;else&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;              return&lt;/span&gt;&lt;span&gt; error&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;UnsupportedType&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            return&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;          } &lt;/span&gt;&lt;span&gt;else&lt;/span&gt;&lt;span&gt; if&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;rest&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;] &lt;/span&gt;&lt;span&gt;==&lt;/span&gt;&lt;span&gt; &apos;.&apos;&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            return&lt;/span&gt;&lt;span&gt; setFieldValue&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;field&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;rest&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;..&lt;/span&gt;&lt;span&gt;], &lt;/span&gt;&lt;span&gt;value&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&amp;amp;&lt;/span&gt;&lt;span&gt;@field&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;ptr&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;field&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;))&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;          }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      } &lt;/span&gt;&lt;span&gt;else&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        return&lt;/span&gt;&lt;span&gt; error&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;NoPathExists&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    },&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    else&lt;/span&gt;&lt;span&gt; =&amp;gt;&lt;/span&gt;&lt;span&gt; return&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you don’t understand this, don’t worry! I’ll walk you through this.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;fn&lt;/span&gt;&lt;span&gt; setFieldValue&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;comptime&lt;/span&gt;&lt;span&gt; T&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; type&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;path&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; []&lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; u8&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;value&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; []&lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; u8&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;ptr&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; *&lt;/span&gt;&lt;span&gt;T&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;!&lt;/span&gt;&lt;span&gt;void&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;These are the parameters:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;T&lt;/code&gt; :: a type (the type of the struct we want to modify)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;path&lt;/code&gt; :: a path to a potentially nested field in the struct, separated by
dots (example: &lt;code&gt;apply.use_nom&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;value&lt;/code&gt; :: the string value from the command line&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ptr&lt;/code&gt;: a pointer to the struct value to modify&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Cool, right?&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;switch&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;@typeInfo&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;T&lt;/span&gt;&lt;span&gt;)) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  .&lt;/span&gt;&lt;span&gt;Struct&lt;/span&gt;&lt;span&gt; =&amp;gt;&lt;/span&gt;&lt;span&gt; |&lt;/span&gt;&lt;span&gt;structInfo&lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    inline&lt;/span&gt;&lt;span&gt; for&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;structInfo&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;fields&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;span&gt;field&lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      // ...&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  },&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  else&lt;/span&gt;&lt;span&gt; =&amp;gt;&lt;/span&gt;&lt;span&gt; return&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This might look weird at first. This just checks if the type provided is a
struct, and then starts looping over the struct’s fields. Each field has certain
metadata associated with it: a name, the type (which is itself a &lt;code&gt;comptime&lt;/code&gt;
value of &lt;code&gt;type&lt;/code&gt;, and other things) Notice how this is an &lt;code&gt;inline&lt;/code&gt; for loop; in
&lt;code&gt;comptime&lt;/code&gt;, loops are unrolled, since evaluation is strict inside &lt;code&gt;comptime&lt;/code&gt;
boundaries, and must terminate. After all, the compiler must strictly evaluate
all of these values for them to make sense; type information cannot be available
in the resulting binary.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;else =&amp;gt; return&lt;/code&gt; makes sure to not do anything if the type is not a struct.
This may be confusing (shouldn’t this be a compile error, right?), but I will
come back to this later.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;std&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;mem&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;startsWith&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;u8&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;path&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;field&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;)) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; rest&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; path&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;field&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;len&lt;/span&gt;&lt;span&gt;..&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  if&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;rest&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;len&lt;/span&gt;&lt;span&gt; ==&lt;/span&gt;&lt;span&gt; 0&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    // this is a valid config setting, parse and set the value&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    return&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  } &lt;/span&gt;&lt;span&gt;else&lt;/span&gt;&lt;span&gt; if&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;rest&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;] &lt;/span&gt;&lt;span&gt;==&lt;/span&gt;&lt;span&gt; &apos;.&apos;&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    return&lt;/span&gt;&lt;span&gt; setFieldValue&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;field&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;rest&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;..&lt;/span&gt;&lt;span&gt;], &lt;/span&gt;&lt;span&gt;value&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&amp;amp;&lt;/span&gt;&lt;span&gt;@field&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;ptr&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;field&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;))&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;} &lt;/span&gt;&lt;span&gt;else&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  return&lt;/span&gt;&lt;span&gt; error&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;NoPathExists&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is the real meat; if we have a field that does match the start of the path
passed in, then we check if there is more. If there is more (aka when there is a
dot right after the field name), then we &lt;em&gt;recurse&lt;/em&gt; into the field where that
exists. And if there is no dot in that first index, then we just continue on and
no field matching that will be found. If there is no more (we have reached the
field we need to modify), then we have reached the field that we want to modify
and can access it with &lt;code&gt;@field(ptr, field.name)&lt;/code&gt; in that block with the comment,
and execution can end once we set the value.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;else =&amp;gt; return&lt;/code&gt; in the &lt;code&gt;switch&lt;/code&gt; case is required due to this recursion.
Since the compiler will always hit a case where the nested field is not a struct
in order to terminate evaluation, we make sure to not emit a &lt;code&gt;@compileError()&lt;/code&gt;
in the default case.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;field&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt; ==&lt;/span&gt;&lt;span&gt; bool&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; parsed&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; blk&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    if&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;mem&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;eql&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;u8&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;value&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;true&quot;&lt;/span&gt;&lt;span&gt;)) &lt;/span&gt;&lt;span&gt;break&lt;/span&gt;&lt;span&gt; :&lt;/span&gt;&lt;span&gt;blk&lt;/span&gt;&lt;span&gt; true&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    if&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;mem&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;eql&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;u8&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;value&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;false&quot;&lt;/span&gt;&lt;span&gt;)) &lt;/span&gt;&lt;span&gt;break&lt;/span&gt;&lt;span&gt; :&lt;/span&gt;&lt;span&gt;blk&lt;/span&gt;&lt;span&gt; false&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    return&lt;/span&gt;&lt;span&gt; error&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;InvalidBoolean&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  }&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  @field&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;ptr&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;field&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; parsed&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;} &lt;/span&gt;&lt;span&gt;else&lt;/span&gt;&lt;span&gt; if&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;field&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt; ==&lt;/span&gt;&lt;span&gt; []&lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; u8&lt;/span&gt;&lt;span&gt; or&lt;/span&gt;&lt;span&gt; field&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt; ==&lt;/span&gt;&lt;span&gt; ?&lt;/span&gt;&lt;span&gt;[]&lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; u8&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  if&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;field&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt; ==&lt;/span&gt;&lt;span&gt; ?&lt;/span&gt;&lt;span&gt;[]&lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; u8&lt;/span&gt;&lt;span&gt; and&lt;/span&gt;&lt;span&gt; value&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;len&lt;/span&gt;&lt;span&gt; ==&lt;/span&gt;&lt;span&gt; 0&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    @field&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;ptr&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;field&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; null&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  } &lt;/span&gt;&lt;span&gt;else&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    @field&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;ptr&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;field&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; value&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  return&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;} &lt;/span&gt;&lt;span&gt;else&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  return&lt;/span&gt;&lt;span&gt; error&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;UnsupportedType&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This final snippet runs if the path to the field exists. It checks the field
type, validates it if needed, and sets the value based on that. Pretty
self-explanatory code when you read it; it trivially parses booleans for &lt;code&gt;bool&lt;/code&gt;
fields, or shoves the value into a string (&lt;code&gt;[]const u8&lt;/code&gt;) slice, accounting for
potential empty values by treating them as &lt;code&gt;null&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;This took an hour to implement. I now had dynamic field-setting code that I
probably won’t have to touch for a good long while as I add more configuration
options, all because of Zig’s &lt;code&gt;comptime&lt;/code&gt;. At most, I’ll just need to add more
types to check for like integers or dates, if they crop up. Amazing!&lt;/p&gt;
&lt;h2&gt;Part III :: Conclusion&lt;/h2&gt;
&lt;p&gt;What is the point of this post, besides glazing over a programming language like
every other Rust fanboy out there
(&lt;a href=&quot;https://transitiontech.ca/random/RIIR&quot;&gt;IYKYK&lt;/a&gt;)? The point is that I’m learning
new things every day, and this is one of those things that just blew my mind
away, even though I’ve been using Zig for around a year to this point.&lt;/p&gt;
&lt;p&gt;No matter what, there’s always new stuff to learn. And Zig is fun to use. Go try
it out, and keep doing fun shit!&lt;/p&gt;
&lt;section&gt;&lt;h2&gt;Footnotes&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;I don’t like C++ very much, as you can tell by the tone of this post. It’s
just not fun for me to read or write. I’ve only used it for university
projects thus far, and I don’t really want to fan the flames of the language
wars, but it’s just my opinion. Don’t take it personally. Nix is written in
C++, after all, and I love Nix. It is what it is. &lt;a href=&quot;#user-content-fnref-templates&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;</content:encoded></item><item><title>Freecoding - An Alternative To Vibe Coding</title><link>https://snare.dev/musings/freecoding</link><guid isPermaLink="true">https://snare.dev/musings/freecoding</guid><description>A manifesto against vibe coding, and for an alternative</description><pubDate>Fri, 20 Jun 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Knowledge is power. Especially in an age where knowledge is being outsourced to
entities that we don’t even &lt;em&gt;know&lt;/em&gt; much about yet.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.forbes.com/sites/jackkelly/2024/10/15/the-globalization-and-offshoring-of-us-jobs-have-hit-americans-hard&quot;&gt;A very American mentality&lt;/a&gt;,
to say the least.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;What the fuck are you talking about, Varun?&lt;/em&gt;, you may ask. Great question.&lt;/p&gt;
&lt;p&gt;I’m talking about large language models (LLMs) and their widespread effects on
society, and especially on that of tech workers. Actually, let’s zoom in on our
target: so-called &lt;strong&gt;“vibe coding”&lt;/strong&gt;. A term that was only coined
&lt;a href=&quot;https://xcancel.com/karpathy/status/1886192184808149383&quot;&gt;four months ago&lt;/a&gt; by a
really damn smart guy, but with the AI bros frothing at the mouth following
close behind. That Twitter thread is genuinely sickening to read. As members of
the post-industrial world, we have taken many steps backwards, convincing
ourselves that LLMs and their sycophants are the sole way forward towards some
Wall-E-esque goal&lt;sup&gt;&lt;a href=&quot;#user-content-fn-wall-e&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;This all sounds the ramblings&lt;sup&gt;&lt;a href=&quot;#user-content-fn-ramblings&quot;&gt;2&lt;/a&gt;&lt;/sup&gt; of a man who’s gone slightly insane,
right? I think I have in this regard, but for damn good reason. Many tech
workers have utilized AI in this way, and justified the terrible tradeoffs that
this brings about by mindlessly quoting “efficiency” and “productivity”; they
are collectively paying the price of not learning for some minimal, short-lived
success.&lt;/p&gt;
&lt;p&gt;I’d imagine the following words have probably been spoken somewhere in a
nondescript office building’s coffee break room in San Francisco:&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Oh my, Jared&lt;sup&gt;&lt;a href=&quot;#user-content-fn-jared&quot;&gt;3&lt;/a&gt;&lt;/sup&gt;, look at the new application that’s on the front page of
Hacker News and Reddit this week! It’s vibe coded, and probably made up of piles
of shit-tier spaghetti code that even an Italian will gawk at. Another
money-maker, successfully come to fruition with just the power of imprecise
thoughts and contrived delusions of grandeur!&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;To be frank, I’m APPALLED at the state of post-LLM programming these days. It’s
genuinely disgusting to me that people will literally give up thinking about
system design and architecture, reduce it all down to the pure essence of “It’s
just computer code!” without regard to how it works. And after they adopt this
insulting attitude, they then spit out complete trash and dress it up like a pig
with lipstick, and then make MILLIONS off their trash, all without due respect
to the sources of their material.&lt;/p&gt;
&lt;p&gt;It’s as if drop-shippers have found their new grift. Like they’ve turned to vibe
coding to make a quick buck off some bullshit B2B SaaS product that’ll die in
the next 2 years after they get their venture capital money and disappear off
the face of the Earth. What a terrible way to live life.&lt;/p&gt;
&lt;p&gt;So, on my never ending quest for knowledge, I decided to move in the opposite
direction: something that I now call &lt;strong&gt;freecoding&lt;/strong&gt; :: like
&lt;a href=&quot;https://en.wikipedia.org/wiki/Free_climbing&quot;&gt;free climbing&lt;/a&gt;, but for coding;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;freecoding&lt;/em&gt; :: an approach to producing computer programs by usage of minimal
tooling needed to get the task done, which often involves the constant
questioning of what is truly “necessary” in a programmer’s workflow&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;And yes, I’m coining this term in direct opposition to vibe coding.&lt;/p&gt;
&lt;p&gt;Let’s learn&lt;sup&gt;&lt;a href=&quot;#user-content-fn-learn&quot;&gt;4&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;h2&gt;A Swing And A Miss&lt;/h2&gt;
&lt;p&gt;My experience with vibe coding was atrocious. Think about vibe coding this way.
Every time I tried it&lt;sup&gt;&lt;a href=&quot;#user-content-fn-trying-vibe-coding&quot;&gt;5&lt;/a&gt;&lt;/sup&gt;, it felt like I was picking up a
large multi-dimensional hammer, swinging it at a nail the size of a few atoms,
and somehow missing. Because that’s basically what it is.&lt;/p&gt;
&lt;p&gt;The very first time I tried to vibe code, it was in April 2025 at a university
&lt;a href=&quot;https://sfhacks.io&quot;&gt;hackathon&lt;/a&gt;; my first one, too. Hackathons are supposedly
the best environment for this sort of “style” of coding, if you can call it
that. The goal? Shit out a proof-of-concept product over a weekend that does
something that would have otherwise taken a much longer time to spec out code
for.&lt;/p&gt;
&lt;p&gt;It went &lt;em&gt;horribly&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;We were building a CLI in Go for a new version control system, and I thought it
would be a cool time to try vibe coding. I didn’t want to scaffold out a whole
web application AND a large CLI boilerplate, since I wanted to show off our
product’s functionality! Focus on the business logic, rather than the routine
boring stuff, right? So I installed a Copilot-like plugin into my Neovim
environment, pulled up a ChatGPT tab, and went off to the races. And from that
point on, I was more dumbfounded than impressed.&lt;/p&gt;
&lt;p&gt;Because &lt;strong&gt;&lt;em&gt;nothing worked at all!!!&lt;/em&gt;&lt;/strong&gt; Well, that’s a bit of an overstatement,
but when things didn’t work, shit hit the fan real fast.&lt;/p&gt;
&lt;p&gt;Unless I (and my hackathon buddies) went into an astounding amount of detail in
order to describe the application requirements, our LLMs would confidently shit
out command scaffolds that were just &lt;em&gt;wrong&lt;/em&gt;. When I tried to lead it down the
path to the actual that requirements I wanted, they would initially agree with
me, as if they were repenting of their sinful ways. But to my dismay, they would
stubbornly continue down the same paths they initially came up with. Like a
fucking donkey, begging for water even as the trough is right in front of him!&lt;/p&gt;
&lt;p&gt;Using the wrong command scaffolding and names? Contriving different ideas for
our version control strategies that were blatantly ripping off Git (which we
were explicitly trying to avoid)? Fucking up styles and not using Tailwind and
resorting to terrible CSS that made no sense? You name it, the LLM would fuck it
up!&lt;/p&gt;
&lt;p&gt;In the times that it was successful, it was always with some hidden cost.
Usually, it was countless minutes of getting frustrated at output that just was
not useful, along with the lack of the euphoria that a programmer usually gets
from, you know, actually doing something!&lt;/p&gt;
&lt;p&gt;At the very least, if I wrote code that did not work with my own hands, I would
learn more about &lt;em&gt;why&lt;/em&gt; the code I wrote was bad, and how to design my systems
better. Instead, I was spending my time chasing bugs that I would have &lt;em&gt;never&lt;/em&gt;
created in the first place if I had just spent the damn time creating the code.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;I was replacing personal growth with laziness, and that’s just not acceptable.
Nor should it be treated as such.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Cue a few little snippets of my ranting over those few cursed days:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;No, I don’t want to pull in random-ass React dependencies just because!
Fucking hell, Gippity&lt;sup&gt;&lt;a href=&quot;#user-content-fn-gippity&quot;&gt;6&lt;/a&gt;&lt;/sup&gt;, do you &lt;em&gt;really&lt;/em&gt; want another
&lt;a href=&quot;https://en.wikipedia.org/wiki/Npm_left-pad_incident&quot;&gt;left-pad&lt;/a&gt; incident or
something?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Huh? Stop trying to combine these two unrelated pieces of code because you
think “don’t repeat yourself” is divine command&lt;sup&gt;&lt;a href=&quot;#user-content-fn-dry&quot;&gt;7&lt;/a&gt;&lt;/sup&gt;!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;What the fuck??? Who thought it was a good idea to keep checking for the HTTP
method being called like 5 different times? Oh right! Copilot DOESN’T KNOW
WHAT THE FUCK IT’S DOING! SINCE WHEN DID IT EVER????&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Not to mention the fact that since LLMs don’t always search the internet for
documentation, we would sometimes get old, deprecated, or removed APIs in our
code that were replaceable by better patterns. It almost felt like my group and
I were at slot machines, just hoping to hit the jackpot by hitting the
“Generate” button again and again and again&lt;sup&gt;&lt;a href=&quot;#user-content-fn-luck&quot;&gt;8&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;Guys, this is not what people meant when they said to
&lt;a href=&quot;https://flatrick.github.io/notes/Programming-by-Wishful-Thinking/&quot;&gt;“program by wishful thinking”&lt;/a&gt;!
We’re supposed to be LEARNING from our mistakes, not just praying for dear life
that the next time we “prompt engineer” our badly-phrased requirements, we get
useful output! Have we heard of the colloquial definition of
insanity&lt;sup&gt;&lt;a href=&quot;#user-content-fn-insanity&quot;&gt;9&lt;/a&gt;&lt;/sup&gt;, people?? That’s exactly what’s happening here! Yet it’s as
if Jesus himself had come down and enacted Isaiah 6:9-10 on just
programmers&lt;sup&gt;&lt;a href=&quot;#user-content-fn-isaiah&quot;&gt;10&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;We must, and &lt;em&gt;can&lt;/em&gt; do better than this.&lt;/p&gt;
&lt;h2&gt;Oh, That’s What The Hammer Is For?&lt;/h2&gt;
&lt;p&gt;So, instead of attempting to get the AI to swing the fucking hammer for us,
let’s, I don’t know, &lt;em&gt;try doing it ourselves!&lt;/em&gt; That was my first thought after
this absolutely horrid experience.&lt;/p&gt;
&lt;p&gt;However, I decided to go one step further. I asked myself this question:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;How did the programmers of old learn the lessons they did?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Oh! They DID IT THE HARD WAY. If they didn’t have the tools they needed, they
did the hard work of researching requirements, figuring out a PLAN OF ACTION,
and just fucking doing it themselves. Hardcore as fuck, and I love it. Because
they had no other choice.&lt;/p&gt;
&lt;p&gt;Old programmers, such as my OS principles
&lt;a href=&quot;https://cs.sfsu.edu/people/robert-bierman&quot;&gt;university professor&lt;/a&gt;, are almost
always remarkably amazing at what they can do, since they understand the
fundamentals of programming from first principles. So I wanted to emulate their
ways of thinking.&lt;/p&gt;
&lt;p&gt;To test this out, I stripped out as many tools as I thought were possible to
strip out of my setup, and to work on a Go application for a few days, to see
how it would go. The project in question,
&lt;a href=&quot;https://github.com/nix-community/nixos-cli&quot;&gt;&lt;code&gt;nixos-cli&lt;/code&gt;&lt;/a&gt;, was rather large, but
organized well, so I thought it would be the perfect way to dip my toes into
this intimidating way of creating code without much assistance.&lt;/p&gt;
&lt;p&gt;The only tools I had on hand? My text editor (Neovim) with a mostly vanilla
configuration (no LSP/autocomplete though, just Telescope and few plugins to
make navigation slightly easier), &lt;code&gt;go&lt;/code&gt;, &lt;code&gt;make&lt;/code&gt;, my terminal (&lt;code&gt;kitty&lt;/code&gt;), some
small command-line tools like &lt;code&gt;fzf&lt;/code&gt; and such, my web browser&lt;sup&gt;&lt;a href=&quot;#user-content-fn-browser&quot;&gt;11&lt;/a&gt;&lt;/sup&gt;, and
that’s it. Nothing else.&lt;/p&gt;
&lt;h3&gt;Swinging The Hammer Myself&lt;/h3&gt;
&lt;p&gt;Let’s start freecoding!&lt;/p&gt;
&lt;p&gt;I was in the middle of a rewrite of this project from Zig to Go. As such, my
goal for this freecoding session was to port the &lt;code&gt;nixos option&lt;/code&gt; command’s TUI,
but only using documentation, as well as occasionally resorting to browsing
issue trackers and examples to get a feel for dependencies I was using.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Oh man, I struggled at first&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;I didn’t realize how much I relied on LSP and such to move around
badly-organized codebases. Not to mention, how did I even look for invocations
and other such things?&lt;/p&gt;
&lt;p&gt;Simple: use &lt;code&gt;grep&lt;/code&gt;/&lt;code&gt;rg&lt;/code&gt; and name variables better so that they’re more
&lt;a href=&quot;https://mrcoles.com/is-it-greppable/&quot;&gt;“&lt;code&gt;grep&lt;/code&gt;-pable”&lt;/a&gt;!&lt;/p&gt;
&lt;p&gt;What about errors? How would I know if my code had errors? How would I start
testing my code?&lt;/p&gt;
&lt;p&gt;My process of writing code started to change after asking these questions: I
started recompiling quite frequently to check for errors, and was writing less
useless code and testing it more frequently as a result. I even started mocking
out my desired behavior in comments and in super rough design documents, rather
than just diving in headfirst.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;I seemed to have forgotten that source code is communication between not just
the developer and computer, but between developers as well.&lt;/strong&gt; And once I
realized this, it all clicked, and my world exploded for the better.&lt;/p&gt;
&lt;p&gt;Navigating around package names and functions forced me to think about how I was
organizing my code better, and made me create better abstractions around my
screens and program state. Not to mention that it helped me research different
solutions to problems that I ran into, since I was finally asking the correct
questions about my own code before I even typed it into the buffer. This was
looking good!&lt;/p&gt;
&lt;p&gt;I had the &lt;code&gt;nixos option&lt;/code&gt; rewrite complete in a week, all while going to and from
my university and working on it during classes. Even though this was much slower
than I would have liked it to be, I learned a ton throughout the process about
many things. Some things like how the Elm architecture translated to a much more
imperative programming language, how to mimic Rust-style enums with interfaces,
and many other fruitful revelations came from just turning the fucking
autocomplete off.&lt;/p&gt;
&lt;p&gt;Just think about that for a second. This is a great source of knowledge! And it
just came from turning my tools off and understanding why they were there in the
first place. And now, hell, I can probably work more effectively in more
restricted environments and am more adaptable to shitty situations thrown at me.&lt;/p&gt;
&lt;p&gt;One of my friends mentioned this same phenomenon too in different ways. Quote
from one of our conversations about this (with some editorializing for clarity):&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Yeah, I actually did the whole “freecoding” thing subconsciously during our
filesystems&lt;sup&gt;&lt;a href=&quot;#user-content-fn-filesystems&quot;&gt;12&lt;/a&gt;&lt;/sup&gt; project. There’s I think some mechanism in my brain
where I try to make code more readable when I’m not using an LSP or have
legitimate syntax highlighting. The process of “paper and pen” compilation in
my head as I’m writing code is valuable, since the best way to not get
compiler errors when writing with 0 tooling is to just not write any fucking
compiler errors (5head, etc.).&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Let’s be frank for a second. &lt;em&gt;This doesn’t seem like some big realization!&lt;/em&gt;, you
may be thinking. And you’re right. &lt;strong&gt;It’s the way that most fruitful learning
happens: by understanding from first principles why you need the tools you do.&lt;/strong&gt;
You’re not gonna know what a hydraulic press is for if you don’t know what the
fuck the term “pressure” is.&lt;/p&gt;
&lt;p&gt;So now the question arises: regardless of the fact that I had this enlightening
experience from programming with minimal tooling, why should people not use LLMs
for code? Are LLMs not just another tool in the box, just like LSP and code
autocomplete? Are they really as damaging as I claim they are to programming
knowledge?&lt;/p&gt;
&lt;h2&gt;The Beasts That Cannot Comprehend&lt;/h2&gt;
&lt;p&gt;LLMs are fundamentally just
&lt;a href=&quot;https://bea.stollnitz.com/blog/how-gpt-works-technical/&quot;&gt;next-word-predictors&lt;/a&gt;
with a decently high degree of accuracy. While this seems like a rather
reductive view of the amazing technology behind them like the
&lt;a href=&quot;https://en.wikipedia.org/wiki/Transformer_(deep_learning_architecture)&quot;&gt;transformer architecture&lt;/a&gt;,
the whole idea is that with a high degree of accuracy, they are able to simulate
intelligent output.&lt;/p&gt;
&lt;p&gt;This next-token tech has existed for a while in the form of other precursors,
such as &lt;a href=&quot;https://en.wikipedia.org/wiki/Markov_chain&quot;&gt;Markov chains&lt;/a&gt;. So this
isn’t a new, groundbreaking concept, it’s just an evolved version of it.&lt;/p&gt;
&lt;p&gt;However, this means that no real semantic information is being encoded about
tokens, but rather LLMs will be learning about how likely one token is to follow
another.&lt;/p&gt;
&lt;p&gt;Think about it this way: I want an LLM to evaluate the expression “1 + 1 =”.
However, the LLM only knows what the token “1” is in relation to other tokens,
since it sees it as an “embedding” (aka some numerical value in a
multi-dimensional space). It &lt;em&gt;may&lt;/em&gt; predict that “2” is the next token after
this, due to the fact that it’s probably seen this relation in its training
data.&lt;/p&gt;
&lt;p&gt;That’s INCREDIBLY wasteful calculations to predict what the next token is when
it comes to mathematical expressions, simply because the LLM has no idea what a
“number” even is definitionally. It only sees what the number token is in
approximation to other tokens in its datasets. Not to mention that a nonzero
chance of being wrong should never happen when attempting to evaluate
expressions. You don’t expect a fucking calculator to be wrong, right? So why
should we suddenly start trusting the output of something that has the potential
to be confidently wrong by its own core design principles?&lt;/p&gt;
&lt;p&gt;Let’s take this concern, and apply it mathematically with some basic statistics.
Since an LLM is a token &lt;em&gt;predictor&lt;/em&gt;, it has some nonzero chance of generating
incorrect predictions. Let’s see how this can spiral during a prompt-based
conversation; assume a flat per-token accuracy rate of 99.99% over a wide
variety of situations, so a 0.01% chance of error for the next token. This is
probably more generous than I can give most LLMs credit for, but I digress.&lt;/p&gt;
&lt;p&gt;The possibility that all tokens are theoretically correct assuming this flat
per-token accuracy relies on the probability of purely independent events all
following a predicted outcome, as represented in the following formula, where
&lt;code&gt;n&lt;/code&gt; is the number of events:&lt;/p&gt;
&lt;span&gt;&lt;span&gt;&lt;span&gt;Pallcorrect=pn=0.9999nP_{all correct} = p^n = 0.9999^n&lt;/span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;P&lt;/span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt;ll&lt;/span&gt;&lt;span&gt;correc&lt;/span&gt;&lt;span&gt;t&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;n&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;0.999&lt;/span&gt;&lt;span&gt;&lt;span&gt;9&lt;/span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;n&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;p&gt;Interestingly, in this formula, since I am treating each event as purely
independent, this is probably the highest rate of accuracy that could be
achieved for this hypothetical LLM in particular. But I’ll get into why that’s a
problem later.&lt;/p&gt;
&lt;p&gt;The expected number of errors is linearly growing, would then follow this
formula:&lt;/p&gt;
&lt;span&gt;&lt;span&gt;&lt;span&gt;E(n)=n∗(1−p)=n∗(1−0.9999)=n∗0.0001E(n) = n * (1 - p) = n * (1 - 0.9999) = n * 0.0001&lt;/span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;E&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;n&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;n&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;∗&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;−&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;n&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;∗&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;−&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;0.9999&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;n&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;∗&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;0.0001&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;p&gt;With some more realistic numbers of tokens:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;1000 tokens :: &lt;span&gt;&lt;span&gt;E(1000)=1000∗0.0001=0.1E(1000) = 1000 * 0.0001 = 0.1&lt;/span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;E&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;1000&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;1000&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;∗&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;0.0001&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;0.1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;10,000 tokens :: &lt;span&gt;&lt;span&gt;E(10000)=10000∗0.0001=1E(10000) = 10000 * 0.0001 = 1&lt;/span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;E&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;10000&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;10000&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;∗&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;0.0001&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;100,000 tokens :: &lt;span&gt;&lt;span&gt;E(100000)=100000∗0.0001=10E(100000) = 100000 * 0.0001 = 10&lt;/span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;E&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;100000&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;100000&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;∗&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;0.0001&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;10&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;10 errors over a 100,000 token conversation? Doesn’t seem like much at first.
But here’s the real kicker: &lt;strong&gt;semantic drift&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Remember what I said earlier about treating each event as purely independent?
Well, that’s just not the way models work. A &lt;em&gt;next-token predictor&lt;/em&gt; is always
generating dependent events by definition!&lt;/p&gt;
&lt;p&gt;This leads to all sorts of cascading errors, and thus error probability is no
longer linearly growing, but rather &lt;em&gt;exponential&lt;/em&gt;. One “error” could easily be a
categorical one that shifts the entire conversation in a completely unwanted
direction with even more “errors”, especially ones generated early on in an LLM
prompting session. Models don’t recover from this easily without some
re-grounding, either in the form of introducing new/better context or just a
full blown new prompting session without the previous errors (aka putting in
more coins spinning the wheel again).&lt;/p&gt;
&lt;p&gt;Programs usually have tons of moving parts to them, so mistakes can very easily
compound to give you a terrible developer experience. One variable name that
didn’t get translated over? Oops, sorry! Time to look through and replace it.
Misleading comments that go unnoticed since they don’t interfere with code
execution? You betcha! A logic bug in one small place that you didn’t think
there should be one? Have fun drinking away your sorrows at 3 AM wondering why
something broke because it was a &lt;code&gt;&amp;gt;&lt;/code&gt; rather than a &lt;code&gt;&amp;gt;=&lt;/code&gt; sign!&lt;sup&gt;&lt;a href=&quot;#user-content-fn-stoned&quot;&gt;13&lt;/a&gt;&lt;/sup&gt; Because
the LLM is so smart, apparently.&lt;/p&gt;
&lt;p&gt;Not to mention the fact that if one relies on AI for test generation, and is the
type of developer to have the mental binary between “It works!” and “Why no
work?”&lt;sup&gt;&lt;a href=&quot;#user-content-fn-why-no-work&quot;&gt;14&lt;/a&gt;&lt;/sup&gt; and nothing else in the brain (cue: aka vibe coders), they
run into another problem. Their tests might just straight-up be wrong, and it’ll
slip right past their empty brain and into code that I’ll have an aneurysm
looking at two years later.&lt;/p&gt;
&lt;p&gt;And the most insidious of all: generating code that works, but is &lt;em&gt;complete
fucking spaghetti&lt;/em&gt; that is so tangled that even Mario himself would struggle to
eat it. Tech debt is very real, and someone ends up paying the price for it down
the line. And it sure as hell isn’t the AI that generated the slop.&lt;/p&gt;
&lt;h3&gt;The Heroes They Could Not Comprehend&lt;/h3&gt;
&lt;p&gt;This mountain of problems is why I massively respect the programmers of old and
the people that are willing to dive into unknown topics without the comfort of
an LLM.&lt;/p&gt;
&lt;p&gt;To be quite frank, I probably would have fallen into the same hole, had I not
been interested in the likes of Zig and Nix. Two of my favorite pieces of tech
in existence as of the time of writing; both were focused on very different
realms, but they oddly had one thing in common in 2022.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Both had a terrible lack of datasets and resources for LLMs to train on.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;And oh boy, was that a fucking blessing in disguise for me.&lt;/p&gt;
&lt;p&gt;I remember there were countless times that I tried to generate Nix code for my
system, thinking that it would find the right options in the module system and
correct configurations to create for me. That it would come up with the right
abstractions. Nah, it just generated nonexistent module options and blatantly
told me false things about how the Nix module system worked to merge modules
together, and caused me to go down rabbit holes for &lt;em&gt;days&lt;/em&gt; on wild goose chases.&lt;/p&gt;
&lt;p&gt;I tried to do the same things with Zig, but oh my God, it was somehow &lt;em&gt;worse&lt;/em&gt;.
Because it wouldn’t just come up with nonexistent APIs from the standard lib or
try to hallucinate terrible-performing code that would throw instances of very
obvious detectable illegal behavior or not use the language effectively.&lt;/p&gt;
&lt;p&gt;Nah, it would just &lt;strong&gt;throw out invalid syntax.&lt;/strong&gt; And &lt;em&gt;trust me&lt;/em&gt;, it took
everything in me to not crash out and shut down in the middle of physics class
when that happened&lt;sup&gt;&lt;a href=&quot;#user-content-fn-invalid-syntax&quot;&gt;15&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;Who the hell is this pipe dream of vibe coding even fooling? Tons of people,
unfortunately. Just look at tech bro LinkedIn. Or maybe listen to an investors
meeting at YCombinator when they are deciding who to fund in their next
batch&lt;sup&gt;&lt;a href=&quot;#user-content-fn-yc&quot;&gt;16&lt;/a&gt;&lt;/sup&gt;. Or actually, don’t. Save yourself some sanity.&lt;/p&gt;
&lt;h2&gt;The Fool Says In His Heart: I Can Vibe Code!&lt;sup&gt;&lt;a href=&quot;#user-content-fn-the-fool&quot;&gt;17&lt;/a&gt;&lt;/sup&gt;&lt;/h2&gt;
&lt;p&gt;Ah, the ones that refuse to learn, and thus cannot even see that they are the
fools in this game. Fools don’t know that they are fools, right? They think that
they’re wise.&lt;/p&gt;
&lt;p&gt;So how is it that despite my massively positive experiences with the lack of AI
in my programming career, that vast swaths of programmers and tech CEOs are
enamored by their siren-like singing? &lt;em&gt;Why were they fooled by the fancy
weighted billion-side dice?&lt;sup&gt;&lt;a href=&quot;#user-content-fn-dice&quot;&gt;18&lt;/a&gt;&lt;/sup&gt;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;The real answer? The fools have always existed when it comes to new tech.&lt;/p&gt;
&lt;p&gt;Every single time another SF-based AI CEO
&lt;a href=&quot;https://www.tomsguide.com/ai/chatgpt/sam-altman-claims-agi-is-coming-in-2025-and-machines-will-be-able-to-think-like-humans-when-it-happens&quot;&gt;floats another promise of an “artificial general intelligence”&lt;/a&gt;
(AGI), another piece of my soul leaves my body out of sheer cringe. Since
somehow the next iteration of a fancy next-word predictor is the sign of
ultimate intelligence for our species. Give me a fucking break.&lt;/p&gt;
&lt;p&gt;Not only do we have AI CEOs blatantly lying about their products’ capabilities
in an unprecedented way, the drop-shippers and tech bros EAT THAT SHIT UP. They
shill the ever-living FUCK out of Claude and the like, and it’s like they can
never shut up about their latest course on how to utilize it “properly”. The
middle managers hear “We can hire less developers!” and go straight for layoffs,
thinking that the cost for fully AI-assisted coding will be less than hiring
junior developers. Then startup founders follow after this example, thinking
“Maybe I don’t need to hire a developer and I can make this idea myself!”&lt;/p&gt;
&lt;p&gt;Can you? You can, but for a price. And a very hidden one. There’s no such thing
as a free lunch, you’ve heard this thousands of times!!! Why do you never
learn??? Have you seen the &lt;a href=&quot;https://docs.cursor.com/models&quot;&gt;prices&lt;/a&gt; for tools
like Cursor IDE and the like? They’re &lt;strong&gt;fucking outrageous&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;At the time of writing, there is a Free plan that allows 200 requests &lt;em&gt;per
month&lt;/em&gt;. That’s pure gambler’s fallacy, a pure distillation of that “Just one
more prompt, bro!” attitude, so pure that it looks blue&lt;sup&gt;&lt;a href=&quot;#user-content-fn-breaking-bad&quot;&gt;19&lt;/a&gt;&lt;/sup&gt;! Oh, but
it gets worse.&lt;/p&gt;
&lt;p&gt;Pay $20 per month, and you get 500 requests! Hell yeah, more tickets for the
slot machine! Not to mention as long as you wait in line for a little longer
each time, you can actually spin the wheel as many times as you want! Because
you technically get unlimited “requests”, just on a much slower mode. Because
that’s economically sustainable, right?&lt;/p&gt;
&lt;p&gt;Well, OK, hold on. What if you use another model? “Bring your own model”, just
like you’d BYOB some moonshine to a house party or something, except much worse.
Well, it’s charged per request.&lt;/p&gt;
&lt;p&gt;But what’s a request even? As per documentation:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;A request represents a single message sent to the model, which includes your
message, any relevant context from your codebase, and the model’s response.&lt;/p&gt;
&lt;p&gt;One request is $0.04.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;WHAT??????&lt;/p&gt;
&lt;p&gt;25 requests is 1 dollar????&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;“Hit me. Hit me. Hit me. Why no work? Fuck, I guess hit me again!”&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;That’s exactly what I imagine vibe coders saying when they are in front of this
absolute fucking Ponzi scheme of an application that is Cursor, or any other
so-called “agentic coder” or whatever memetic juice they’re on again.&lt;/p&gt;
&lt;p&gt;And the even more outrageous thing about it? When investors eventually get tired
of sinking trillions of dollars into AI for massively diminishing returns, AI
companies will charge out their ass to break even and not die. And then these
vibe coder fucks will pay even MORE just to get the same shit-tier output, all
because they are not willing to learn the fucking tech themselves.&lt;/p&gt;
&lt;p&gt;This is pure, out-in-the-open
&lt;a href=&quot;https://en.wikipedia.org/wiki/Vendor_lock-in&quot;&gt;vendor lock-in&lt;/a&gt;, just like Apple
would have done. They reel you in like a fish, hoping you become reliant on
their tooling, and then eventually extort you for what you’ve got. And vibe
coders will happily pay these exorbitant fees, smugly thinking that they’ve
successfully put us real engineers out of a job like the fools they are. Steve
Jobs would be so proud of Cursor and OpenAI. And no, &lt;em&gt;that’s not a fucking
compliment&lt;/em&gt;, as some tech bros would have you believe.&lt;/p&gt;
&lt;h2&gt;Reconciliation?&lt;/h2&gt;
&lt;p&gt;Is it possible to reconcile vibe coding with freecoding? More specifically, can
they coexist?&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Possibly&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;As much as I bash on AI dumbing down the general populace, in the hands of
master craftsmen it can take them quite far. While I am no master craftsman, I
can very confidently say that liberal usage of AI has helped me learn how to
utilize complex systems such as Kubernetes quite quickly. Hell, I used ChatGPT
at work to help me put together a GitHub Actions &lt;code&gt;actions-runner-controller&lt;/code&gt;
setup with self-hosted Nix caching and other tools, despite never having worked
with Kubernetes in any meaningful capacity beforehand. But the key is that I’ve
learned how to utilize it while doing so, and will absolutely remember the
concepts and mistakes that I have made and learn from them, even with that
liberal AI usage.&lt;/p&gt;
&lt;p&gt;Remember low-code tools? I put vibe coding squarely in the same place. It’s for
the people who don’t have an innate desire to learn how complex computer systems
work in order to bring their ideas to life. Or, more crassly, the ones that
simply don’t regard code as the art form that it is.&lt;/p&gt;
&lt;p&gt;Actually, hold on. Let’s generalize that a step further. &lt;strong&gt;Vibe coding is for
the people that want to profit off the work of others without actually doing any
of the work themselves, or at least the most minimal amount possible.&lt;/strong&gt; Like
people that don’t want to pay artists properly for their work, and instead
remove watermarks from stock photos and pass it off as real in their games and
media.&lt;/p&gt;
&lt;p&gt;A remarkably simple analogy, right?&lt;/p&gt;
&lt;p&gt;My goal for writing this piece isn’t to be critical of AI. Many people far
smarter than I am have done so with far more compelling points. I am a fan of
what AI can bring to the table, but it sure as hell isn’t this trash that is
vibe coding. Us humans, we can be far more effective when we use the generative
capabilities of AI with correct context from our own sources of knowledge,
rather than offloading the knowledge itself to the machines and reaping the
limited profits until it’s too late.&lt;/p&gt;
&lt;p&gt;Just remember: when vibe coders hit that “Generate” button, they utilize the
massive amounts of work that went into making the data for these models to
consume. All with nothing but that disrespectful “Just one more prompt, bro!”
attitude. They pay massive amounts of money to companies instead of doing
themselves a favor and learning the technology for themselves, or at the very
least, God forbid, stimulating the economy by creating a job for someone.&lt;/p&gt;
&lt;p&gt;Respect the process or not, it’s your choice at the end of the day to vibe code
or not. It’s not like you can gain muscles by having an industrial machine doing
the lifting for you, but I have no say in whether or not you’ll choose to put in
the work or not. I can only rant about how bad it is for you, and hope you see
the same way I do afterward.&lt;/p&gt;
&lt;p&gt;Fin.&lt;/p&gt;
&lt;p&gt;A special thanks to my friends Zachary Howe and Vignesh Guruswami for reading my
article before publishing it and for suggesting improvements.&lt;/p&gt;
&lt;section&gt;&lt;h2&gt;Footnotes&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Yes, this does remind me of the movie Wall-E. In Wall-E, the whole reason
that the world turned into a landfill was in the name of human progress. I
highly recommend watching Zoe Bee’s video
&lt;a href=&quot;https://www.youtube.com/watch?v=7-_rK0KkB6k&quot;&gt;In Defense of Inefficiency&lt;/a&gt;
for more on the topic. &lt;a href=&quot;#user-content-fnref-wall-e&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Bear with me here. Simply put, I can’t really stand here anymore and claim
that the people that have fully embraced AI are somehow in the right
anymore. IMO, the usage of AI in such a way that it becomes core to your
workflow is a disservice to your own cognitive abilities, and that’s not
something I can excuse as just a valid “tradeoff”. &lt;a href=&quot;#user-content-fnref-ramblings&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;I don’t know why I used the name Jared. If your name’s Jared and this
doesn’t apply to you, sorry. &lt;a href=&quot;#user-content-fnref-jared&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Please. Let’s learn. It’s good for you. I swear. Unless you’re too
brain-rotted by LLMs to understand what the concept of real learning means. &lt;a href=&quot;#user-content-fnref-learn&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Admittedly, after this this experience, I never tried to vibe code again.
Terrible
&lt;a href=&quot;https://github.blog/enterprise-software/collaboration/developer-experience-what-is-it-and-why-should-you-care/&quot;&gt;developer experience&lt;/a&gt;,
to say the least. &lt;a href=&quot;#user-content-fnref-trying-vibe-coding&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;“Gippity” is the endearing term that my friends and I have started to use
for ChatGPT. Other LLMs have their own: Gemini is “Jiminy”, so on, so forth.
Thanks, &lt;a href=&quot;https://www.twitch.tv/theprimeagen&quot;&gt;ThePrimeagen&lt;/a&gt;, for being such a
terrible influence on us. Also, “SQL” is now pronounced “squeal”. Crucify
me. &lt;a href=&quot;#user-content-fnref-gippity&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Yes, it’s very possible to overdo the Don’t Repeat Yourself (DRY) principle.
And LLMs are very prone to this, since they have limited knowledge about
your systems that you are designing. &lt;a href=&quot;#user-content-fnref-dry&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Funny enough, the “Just one more prompt, bro!” attitude is something that I
found about while reading this great
&lt;a href=&quot;https://pivot-to-ai.com/2025/06/05/generative-ai-runs-on-gambling-addiction-just-one-more-prompt-bro/&quot;&gt;article&lt;/a&gt;
that made the connection click for me. Amazing read, give it a go. &lt;a href=&quot;#user-content-fnref-luck&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The “definition of insanity” as mentioned here is “doing the same thing over
and over again and expecting a different result”; this is often
mis-attributed to Albert Einstein, but regardless of the origin, it’s a damn
good quip on being adaptable instead of banging your head against the
fucking wall. &lt;a href=&quot;#user-content-fnref-insanity&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;“&lt;em&gt;Be ever hearing, but never understanding; be ever seeing, but never
perceiving. Make the heart of this people calloused; make their ears dull
and close their eyes. Otherwise they might see with their eyes, hear with
their ears, understand with their hearts, and turn and be healed.&lt;/em&gt;” ~&amp;gt;
Isaiah 6:9-10. &lt;a href=&quot;#user-content-fnref-isaiah&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Don’t you dare say it. A browser is a critical part of using the Internet
and viewing documentation, especially in Go when looking for dependencies
and such, so I don’t want to hear anything about “Oh but you can just search
up answers to questions!” Because no, I didn’t. Trust me on this, that would
have defeated the whole point of the exercise; ego lifting/programming is
bad for everyone involved. &lt;a href=&quot;#user-content-fnref-browser&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;For context, both him and I had to write a full-blown
&lt;a href=&quot;https://en.wikipedia.org/wiki/File_Allocation_Table&quot;&gt;FAT&lt;/a&gt;-style filesystem
with a Linux-like shell for our OS principles class. It was really fun, and
I learned quite a lot. Thanks to my friend Vignesh for this quote, by the
way. &lt;a href=&quot;#user-content-fnref-filesystems&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;I was stoned while writing this, so I definitely could have thought of
better examples. But I feel like this catches my emotions about the subject
better. &lt;a href=&quot;#user-content-fnref-stoned&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Hi Matt. Why no work? &lt;a href=&quot;#user-content-fnref-why-no-work&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Crashing out here is definitely a symptom of the “Just one more prompt,
bro!” attitude I just mentioned in the earlier footnote. Very telling. Funny
enough, LLMs will still struggle with Zig, since it’s such a moving target.
It even struggled in a recent interview that I had with Bun, where I decided
to try it out due to a terrible time constraint of implementing &lt;code&gt;find&lt;/code&gt; in 1
hour with nothing but the C stdlib in Zig. Sometimes, hell, it’s even
struggled with Go when I’ve attempted to ask it for advice, since it didn’t
know the reflection APIs very well and thought that integer range syntax was
invalid despite being in the latest version (1.24 at time of writing). &lt;a href=&quot;#user-content-fnref-invalid-syntax&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Because LOOK AT
&lt;a href=&quot;https://www.ycombinator.com/companies/industry/ai&quot;&gt;HOW MANY AI COMPANIES&lt;/a&gt;
are funded by YCombinator! Scrolling down this genuinely gave me whiplash,
and makes me wonder what will happen to all that money when this bubble
pops. They shoulda given it to me, at least I would have bought a better
drum kit and made some music or some shit. &lt;a href=&quot;#user-content-fnref-yc&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;A reference to Psalm 14:1: “&lt;em&gt;The fool says in his heart, there is no God!&lt;/em&gt;”
I’m &lt;a href=&quot;/musings/the-unchristian&quot;&gt;atheist&lt;/a&gt;, so I don’t believe this, but still
funny to reference regardless. &lt;a href=&quot;#user-content-fnref-the-fool&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Because damn, it really does feel like rolling dice sometimes. Just one that
has millions of “weights” (parameters) in some multi-dimensional directions
that I can’t even comprehend. &lt;a href=&quot;#user-content-fnref-dice&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Yes, this is a vague Breaking Bad reference to the blue sky they sold.
Because vibe coding is basically a drug. &lt;a href=&quot;#user-content-fnref-breaking-bad&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;</content:encoded></item></channel></rss>