<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Simon Shine</title><link>https://simonshine.dk/</link><description>Recent content on Simon Shine</description><generator>Hugo</generator><language>en-us</language><lastBuildDate>Sat, 15 Aug 2026 21:31:07 +0200</lastBuildDate><atom:link href="https://simonshine.dk/index.xml" rel="self" type="application/rss+xml"/><item><title>Agentic Playwright testing with Nix</title><link>https://simonshine.dk/articles/agentic-playwright-testing-with-nix/</link><pubDate>Sat, 15 Aug 2026 21:31:07 +0200</pubDate><guid>https://simonshine.dk/articles/agentic-playwright-testing-with-nix/</guid><description>&lt;p&gt;I don&amp;rsquo;t do much front-end web development these days, but I enable people who do.&lt;/p&gt;
&lt;p&gt;I use a lot of coding agents for my daily work with back-end APIs and service meshes.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve known for a long time that agentic workflows instrumenting &lt;a href="https://playwright.dev/"&gt;Playwright&lt;/a&gt; to run headless
browser tests based on prompts has advanced quite a lot. I just haven&amp;rsquo;t got around to try it out.&lt;/p&gt;
&lt;h2 id="tldr"&gt;
 tl;dr
 &lt;a class="heading-anchor" href="#tldr" aria-label="Link to this section"&gt;&amp;para;&lt;/a&gt;
&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Playwright, its browsers and its MCP server are all in nixpkgs. No &lt;code&gt;package.json&lt;/code&gt;, no &lt;code&gt;npm install&lt;/code&gt;, no &lt;code&gt;npx playwright install&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;nixpkgs&amp;rsquo; &lt;code&gt;playwright-test&lt;/code&gt; hardcodes the &lt;em&gt;full&lt;/em&gt; browser set into its wrapper, so the naive setup
drags 2.2 GB of Chromium, Firefox and WebKit into your devshell whether you want them or not.
I managed to push that down to 634 MB.&lt;/li&gt;
&lt;li&gt;Headed Chromium and interactive debugging lives behind a &lt;a href="https://simonshine.dk/articles/dendritic-flake-partitions"&gt;flake-parts partition&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;The &lt;a href="https://playwright.dev/docs/test-agents"&gt;planner/generator/healer agents&lt;/a&gt; run on an existing Claude Code session.&lt;/li&gt;
&lt;li&gt;I practice TDD workflows for writing end-to-end tests.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="the-application-under-test"&gt;
 The application under test
 &lt;a class="heading-anchor" href="#the-application-under-test" aria-label="Link to this section"&gt;&amp;para;&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;The thing I&amp;rsquo;m practising on is a generic client area dashboard for a software service, written in
Rust on &lt;a href="https://github.com/tokio-rs/topcoat"&gt;topcoat&lt;/a&gt; with &lt;a href="https://github.com/tokio-rs/toasty"&gt;toasty&lt;/a&gt;. So I&amp;rsquo;m doing three things here: Testing topcoat,
toying around with client dashboards, and getting my agentic playwright testing workflow up.&lt;/p&gt;</description></item><item><title>Hashpinning</title><link>https://simonshine.dk/articles/hashpinning/</link><pubDate>Sat, 08 Aug 2026 01:59:35 +0200</pubDate><guid>https://simonshine.dk/articles/hashpinning/</guid><description>&lt;p&gt;GitHub Actions workflows usually start like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;- &lt;span style="color:#f92672"&gt;uses&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;actions/checkout@v6&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;v6&lt;/code&gt; is a git tag that points to some commit for the release of this version of an action. There is
also a &lt;code&gt;v6.0.1&lt;/code&gt; tag. Every time a minor or patch version is released, e.g. &lt;code&gt;v6.0.2&lt;/code&gt; or &lt;code&gt;v6.1.0&lt;/code&gt;, the
&lt;code&gt;v6&lt;/code&gt; tag is moved to that. Being unspecific means you always get the latest version that begins with
&lt;code&gt;v6&lt;/code&gt;. This is a feature.&lt;/p&gt;</description></item><item><title>Git config-based hooks with hk-nix</title><link>https://simonshine.dk/articles/git-config-based-hooks-with-hk-nix/</link><pubDate>Sat, 01 Aug 2026 00:00:00 +0100</pubDate><guid>https://simonshine.dk/articles/git-config-based-hooks-with-hk-nix/</guid><description>&lt;p&gt;&lt;a href="https://hk.jdx.dev/"&gt;hk&lt;/a&gt; is a fast, modern git hook manager and project linter configured in &lt;a href="https://pkl-lang.org/"&gt;Pkl&lt;/a&gt;.
&lt;a href="https://github.com/nix-tools/hk-nix"&gt;&lt;code&gt;hk-nix&lt;/code&gt;&lt;/a&gt; is to hk what &lt;a href="https://github.com/sudosubin/lefthook.nix"&gt;lefthook-nix&lt;/a&gt; is to lefthook: you install hk with
Nixpkgs, declare your hooks in Nix, pin every tool with Nix, and the hooks install themselves
whenever you enter the &lt;a href="https://github.com/numtide/devshell"&gt;devshell&lt;/a&gt;. It is the same stack I &lt;a href="https://simonshine.dk/articles/lefthook-treefmt-direnv-nix/"&gt;previously built with
lefthook&lt;/a&gt;, with a nicer configuration language and one fewer workaround.&lt;/p&gt;
&lt;h2 id="synergies"&gt;
 Synergies
 &lt;a class="heading-anchor" href="#synergies" aria-label="Link to this section"&gt;&amp;para;&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Nix + direnv:&lt;/strong&gt; You only ever need to install these two manually: Nix being the package manager,
and direnv being the thing that activates the Nix devshell. All other tooling is derived, pinned and
installed for you.&lt;/p&gt;</description></item><item><title>Using sops-nix templates to support passwords in legacy configs</title><link>https://simonshine.dk/articles/sops-nix-templates/</link><pubDate>Sun, 05 Jul 2026 22:51:02 +0200</pubDate><guid>https://simonshine.dk/articles/sops-nix-templates/</guid><description>&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; This article builds on my &lt;a href="https://simonshine.dk/articles/dendritic-sops-nix-flake-template/"&gt;dendritic sops-nix flake template&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Nix is littered with alternative solutions to things.&lt;/p&gt;
&lt;p&gt;Most people who use Nix to manage their &lt;a href="https://irssi.org/"&gt;irssi&lt;/a&gt; configuration would use the &lt;a href="https://nix-community.github.io/home-manager/options/home-manager/programs/irssi.html"&gt;home-manager
irssi extensions&lt;/a&gt;. But I have a principle that I&amp;rsquo;ve held onto: I like to keep
program configuration non-nixified so that they can still hypothetically be used outside of Nix.&lt;/p&gt;
&lt;h2 id="the-problem-irssi-keeps-password-authentication-in-the-config-file"&gt;
 The problem: irssi keeps password authentication in the config file
 &lt;a class="heading-anchor" href="#the-problem-irssi-keeps-password-authentication-in-the-config-file" aria-label="Link to this section"&gt;&amp;para;&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;Irssi was first released in 1999. Back then you didn&amp;rsquo;t think about these things.&lt;/p&gt;</description></item><item><title>Authenticating Tailscale non-interactively via API with sops-nix</title><link>https://simonshine.dk/articles/tailscale-api-authentication-sops-nix/</link><pubDate>Sun, 05 Jul 2026 21:01:12 +0200</pubDate><guid>https://simonshine.dk/articles/tailscale-api-authentication-sops-nix/</guid><description>&lt;p&gt;For the past three years I&amp;rsquo;ve connected my Nix machines using Tailscale by running &lt;code&gt;sudo tailscale login&lt;/code&gt; once per machine.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve known that you can actually &lt;a href="https://tailscale.com/docs/reference/tailscale-api"&gt;log onto a Tailnet with an API key&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Why bother? Running &lt;code&gt;sudo tailscale login&lt;/code&gt; takes ten seconds, your browser&amp;rsquo;s password manager kicks in, you&amp;rsquo;re done.&lt;/p&gt;
&lt;p&gt;This is actually simple and ships almost out of the box, if you already have &lt;a href="https://simonshine.dk/articles/dendritic-sops-nix-flake-template/"&gt;sops-nix secrets management in your system configuration&lt;/a&gt;.&lt;/p&gt;</description></item><item><title>A dendritic sops-nix flake template</title><link>https://simonshine.dk/articles/dendritic-sops-nix-flake-template/</link><pubDate>Sun, 05 Jul 2026 02:35:37 +0200</pubDate><guid>https://simonshine.dk/articles/dendritic-sops-nix-flake-template/</guid><description>&lt;p&gt;This tutorial sets up a flake using flake-parts, import-tree, devshell and sops-nix.&lt;/p&gt;
&lt;p&gt;You can see the template here: &lt;a href="https://github.com/sshine/nix-sops-flake-template"&gt;https://github.com/sshine/nix-sops-flake-template&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Unlike agenix/ragenix, the sops-nix NixOS module does not require importing
a Nix file describing recipients, so it composes cleanly inside flake-parts
modules without triggering &lt;a href="https://nix.dev/manual/nix/2.26/language/import-from-derivation"&gt;IFDs&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="1-a-dendritic-flake"&gt;
 1. A dendritic flake
 &lt;a class="heading-anchor" href="#1-a-dendritic-flake" aria-label="Link to this section"&gt;&amp;para;&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;This flake auto-imports all .nix files in lib/.&lt;/p&gt;
&lt;p&gt;You may want for that directory to be named something else, depending on your project.&lt;/p&gt;</description></item><item><title>Dendritic flake partitions</title><link>https://simonshine.dk/articles/dendritic-flake-partitions/</link><pubDate>Sun, 21 Jun 2026 13:39:00 +0200</pubDate><guid>https://simonshine.dk/articles/dendritic-flake-partitions/</guid><description>&lt;h2 id="tldr"&gt;
 tl;dr
 &lt;a class="heading-anchor" href="#tldr" aria-label="Link to this section"&gt;&amp;para;&lt;/a&gt;
&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Some of my sandboxed CLI agents aren&amp;rsquo;t in nixpkgs, so they ride along as flake inputs.&lt;/li&gt;
&lt;li&gt;They&amp;rsquo;re heavy, and were polluting the &lt;code&gt;flake.lock&lt;/code&gt; of every machine that consumes the flake.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://flake.parts/options/flake-parts-partitions"&gt;flake-parts partitions&lt;/a&gt; move those inputs into a nested flake that&amp;rsquo;s only evaluated when you actually
build the thing.&lt;/li&gt;
&lt;li&gt;Because my flake is dendritic (&lt;a href="https://github.com/denful/import-tree"&gt;import-tree&lt;/a&gt;), I tuck that nested flake inside the import tree using import-tree&amp;rsquo;s ignored-paths convention: &lt;code&gt;_inputs/flake.{nix,lock}&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;While researching this I found a good guide, &lt;a href="https://github.com/Doc-Steve/dendritic-design-with-flake-parts"&gt;Dr. Steve&amp;rsquo;s Dendritic Design with the Flake Parts
Framework: A guide on how to structure your Nix code with Flake Parts using the Dendritic
Pattern&lt;/a&gt;. It covers aspects,
collectors, factories, but doesn&amp;rsquo;t mention flake-parts partitions or lazy input loading.&lt;/p&gt;</description></item><item><title>Managing Secrets privately with sops-nix</title><link>https://simonshine.dk/articles/managing-secrets-privately-with-sops-nix/</link><pubDate>Fri, 15 May 2026 02:37:41 +0200</pubDate><guid>https://simonshine.dk/articles/managing-secrets-privately-with-sops-nix/</guid><description>&lt;h2 id="tldr"&gt;
 tl;dr
 &lt;a class="heading-anchor" href="#tldr" aria-label="Link to this section"&gt;&amp;para;&lt;/a&gt;
&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;I want to commit my secrets encrypted to git with &lt;a href="https://github.com/Mic92/sops-nix"&gt;sops-nix&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;I want to publish my infra-as-code, but don&amp;rsquo;t want to publish my git secrets&lt;/li&gt;
&lt;li&gt;So I put my secrets in a private &lt;code&gt;git+file&lt;/code&gt; flake input&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="motivation"&gt;
 Motivation
 &lt;a class="heading-anchor" href="#motivation" aria-label="Link to this section"&gt;&amp;para;&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;I run Kubernetes clusters managed declaratively with &lt;a href="https://www.talos.dev/"&gt;Talos Linux&lt;/a&gt; and &lt;a href="https://github.com/hall/kubenix"&gt;kubenix&lt;/a&gt;. I want to publish the entire repository that defines the cluster. It includes commands that require authentication (&lt;code&gt;kubectl&lt;/code&gt;, &lt;code&gt;talosctl&lt;/code&gt;, &lt;code&gt;hcloud&lt;/code&gt;, etc.) that don&amp;rsquo;t work without their secrets.&lt;/p&gt;</description></item><item><title>Is the novelty budget dead?</title><link>https://simonshine.dk/articles/is-the-novelty-budget-dead/</link><pubDate>Fri, 24 Apr 2026 12:25:31 +0200</pubDate><guid>https://simonshine.dk/articles/is-the-novelty-budget-dead/</guid><description>&lt;p&gt;Michael Snoyman wrote a thinkpiece called &lt;a href="https://www.snoyman.com/blog/2019/11/boring-haskell-manifesto/"&gt;The Boring Haskell Manifesto&lt;/a&gt; (2019) about how to adopt Haskell into your organization, and how to make your organization productive with Haskell for non-wizards.&lt;/p&gt;
&lt;p&gt;The article introduced me to the concept of a &amp;ldquo;&lt;a href="https://shimweasel.com/2018/08/25/novelty-budgets"&gt;novelty budget&lt;/a&gt;&amp;rdquo; (Mark Wotton, 2018): the limit your project has on new, cool stuff. If you exceed your novelty budget you impose too much risk: new software with unknown limitations, spontaneous learning curves, longer onboarding, harder to hire, longer to develop, things you might need to scrap and rewrite in a less fancy way.&lt;/p&gt;</description></item><item><title>NixOS runners on Forgejo Actions</title><link>https://simonshine.dk/articles/forgejo-actions-nixos-runners/</link><pubDate>Thu, 23 Apr 2026 22:06:40 +0200</pubDate><guid>https://simonshine.dk/articles/forgejo-actions-nixos-runners/</guid><description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; You can see the project in action here: &lt;a href="https://git.shine.town/infra/runners"&gt;https://git.shine.town/infra/runners&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I decided to bootstrap a Kubernetes cluster with &lt;a href="https://www.talos.dev/"&gt;Talos&lt;/a&gt; and
&lt;a href="https://kubenix.org/"&gt;kubenix&lt;/a&gt; during my vacation.&lt;/p&gt;
&lt;p&gt;A lot of experience came out of that, and I&amp;rsquo;m trying to unpack it gradually.&lt;/p&gt;
&lt;p&gt;One of the things I host on this cluster is &lt;a href="https://forgejo.org/"&gt;Forgejo&lt;/a&gt;, which is
&lt;a href="https://codeberg.org/"&gt;Codeberg&lt;/a&gt;&amp;rsquo;s fork of Gitea. Forgejo has &lt;a href="https://forgejo.org/docs/next/user/actions/reference/"&gt;Forgejo
Actions&lt;/a&gt; which is a CI system that mimicks
GitHub Actions, and it has a &lt;a href="https://forgejo.org/docs/next/user/packages/container/"&gt;Container Registry&lt;/a&gt;
much like GHCR, or DockerHub.&lt;/p&gt;</description></item><item><title>Three levels of Nix</title><link>https://simonshine.dk/articles/three-levels-of-nix/</link><pubDate>Sun, 15 Mar 2026 12:34:52 +0100</pubDate><guid>https://simonshine.dk/articles/three-levels-of-nix/</guid><description>&lt;p&gt;When using Nix to orchestrate a new project, I find myself choosing between these three levels:&lt;/p&gt;
&lt;h2 id="1-just-a-shellnix"&gt;
 1. Just a &lt;code&gt;shell.nix&lt;/code&gt;
 &lt;a class="heading-anchor" href="#1-just-a-shellnix" aria-label="Link to this section"&gt;&amp;para;&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;This is good for starting on, it&amp;rsquo;s easy to sell to colleagues or project owners you&amp;rsquo;re not close
with or already bought on the idea of Nix. It leaves a very small footprint in terms of lines of
code: Every lines in this file is worth its own weight, since it usually just mentions packages to
install. That also makes it very tangible. It could look like:&lt;/p&gt;</description></item><item><title>Vendored theme workflow after one month</title><link>https://simonshine.dk/articles/vendored-theme-workflow-after-one-month/</link><pubDate>Sat, 07 Mar 2026 23:03:24 +0100</pubDate><guid>https://simonshine.dk/articles/vendored-theme-workflow-after-one-month/</guid><description>&lt;p&gt;It&amp;rsquo;s exactly one month since I wrote:&lt;/p&gt;

&lt;li class="posts-list-item"&gt;
 &lt;a class="posts-list-item-title" href="https://simonshine.dk/articles/vendoring-hugo-themes-with-nix-and-git"&gt;Vendoring Hugo themes with Nix and Git&lt;/a&gt;
 &lt;span class="posts-list-item-description"&gt;
 &lt;svg class="icon icon-calendar" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"&gt;&lt;title&gt;calendar&lt;/title&gt;&lt;rect x="3" y="4" width="18" height="18" rx="2" ry="2"&gt;&lt;/rect&gt;&lt;line x1="16" y1="2" x2="16" y2="6"&gt;&lt;/line&gt;&lt;line x1="8" y1="2" x2="8" y2="6"&gt;&lt;/line&gt;&lt;line x1="3" y1="10" x2="21" y2="10"&gt;&lt;/line&gt;&lt;/svg&gt;
 Feb 7, 2026
 &lt;/span&gt;
&lt;/li&gt;

&lt;p&gt;I read an interesting article, &lt;a href="https://mcyoung.xyz/2024/04/17/calling-convention/"&gt;The Rust Calling Convention We Deserve&lt;/a&gt; by Miguel
Young de la Sota, and I couldn&amp;rsquo;t help but think that the website was really nicely made:
Inspirational use of CSS3, a personal font choice, and proper underlines under hyperlinks.&lt;/p&gt;</description></item><item><title>Using lefthook and treefmt with Nix</title><link>https://simonshine.dk/articles/lefthook-treefmt-direnv-nix/</link><pubDate>Sat, 07 Mar 2026 21:47:14 +0100</pubDate><guid>https://simonshine.dk/articles/lefthook-treefmt-direnv-nix/</guid><description>&lt;p&gt;In my recent attempts to modernize my CI pipelines, I&amp;rsquo;ve experimented with and subsequently let go
of &lt;a href="https://pre-commit.com/"&gt;pre-commit&lt;/a&gt;. The reason why I didn&amp;rsquo;t stick with pre-commit is that &lt;a href="https://simonshine.dk/articles/speeding-up-ci-with-nix/#letting-go-of-pre-commit"&gt;pre-commit drags
in several software runtimes and toolchains&lt;/a&gt; when building it from scratch. When
Nix fails to build something from nixpkgs, it&amp;rsquo;s an omen of &lt;a href="https://www.youtube.com/watch?v=QrGrOK8oZG8"&gt;too many chefs&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Alternatives I&amp;rsquo;m aware of:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://lefthook.dev/"&gt;lefthook&lt;/a&gt;: A generic tool written in Go; defines hooks in a lefthook.yml file.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/typicode/husky"&gt;husky&lt;/a&gt;: A generic tool written in JavaScript; enough said.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/rhysd/cargo-husky"&gt;cargo-husky&lt;/a&gt;: A tool specialized to Rust/Cargo; very nice, but not generic enough.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I initially disregarded lefthook because of its special lefthook.yml.&lt;/p&gt;</description></item><item><title>Dendritic Nix With nixos-shell</title><link>https://simonshine.dk/articles/dendritic-nix-with-nixos-shell/</link><pubDate>Sun, 01 Mar 2026 14:03:12 +0100</pubDate><guid>https://simonshine.dk/articles/dendritic-nix-with-nixos-shell/</guid><description>&lt;h2 id="tldr"&gt;
 tl;dr
 &lt;a class="heading-anchor" href="#tldr" aria-label="Link to this section"&gt;&amp;para;&lt;/a&gt;
&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;I&amp;rsquo;m refactoring my system flake using &lt;a href="https://github.com/mightyiam/dendritic/"&gt;The Dendritic Pattern&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;I&amp;rsquo;m testing the system flake using &lt;a href="https://github.com/Mic92/nixos-shell"&gt;&lt;code&gt;nixos-shell&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;I&amp;rsquo;ll completely wipe my laptop&amp;rsquo;s NixOS installation when ready&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="table-of-contents"&gt;
 Table of contents
 &lt;a class="heading-anchor" href="#table-of-contents" aria-label="Link to this section"&gt;&amp;para;&lt;/a&gt;
&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="#motivation-some-ranting"&gt;Motivation (some ranting)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#what-is-a-dendritic-flake"&gt;What is a dendritic flake?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#what-is-nixos-shell"&gt;What is nixos-shell?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#the-bootstrap"&gt;The bootstrap&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="motivation-some-ranting"&gt;
 Motivation (some ranting)
 &lt;a class="heading-anchor" href="#motivation-some-ranting" aria-label="Link to this section"&gt;&amp;para;&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;Last week I &lt;a href="https://simonshine.dk/articles/nixos-anywhere-on-hetzner/"&gt;experimented with &lt;code&gt;nixos-anywhere&lt;/code&gt;, &lt;code&gt;deploy-rs&lt;/code&gt; and &lt;code&gt;disko&lt;/code&gt; on Hetzner&lt;/a&gt; which
lets me easily deploy NixOS on a Hetzner VM even though NixOS is not one of the dashboard options.&lt;/p&gt;</description></item><item><title>Learning about AutoAgents by benchmarking Claude Code vs. OpenCode</title><link>https://simonshine.dk/articles/autoagents-claude-vs-opencode/</link><pubDate>Sun, 22 Feb 2026 23:22:30 +0100</pubDate><guid>https://simonshine.dk/articles/autoagents-claude-vs-opencode/</guid><description>&lt;h2 id="tldr"&gt;
 tl;dr
 &lt;a class="heading-anchor" href="#tldr" aria-label="Link to this section"&gt;&amp;para;&lt;/a&gt;
&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;I&amp;rsquo;m extending &lt;a href="https://liquidos-ai.github.io/AutoAgents/"&gt;AutoAgents&lt;/a&gt; so that it supports
&lt;a href="https://z.ai/subscribe?ic=F8DVGJASYD"&gt;Z.ai&lt;/a&gt;, a frontier AI company&lt;/li&gt;
&lt;li&gt;Z.ai&amp;rsquo;s GLM-5 model might or might not compete with Claude Opus 4.5 at a very competitive price.&lt;/li&gt;
&lt;li&gt;I&amp;rsquo;m testing both the &lt;a href="https://opencode.ai/"&gt;OpenCode&lt;/a&gt; CLI vs. Claude Code CLI, and Z.ai&amp;rsquo;s GLM-5 against Claude Opus 4.5.&lt;/li&gt;
&lt;li&gt;Claude is considerably faster, but neglects testing. OpenCode&amp;rsquo;s tests were not end-to-end.&lt;/li&gt;
&lt;li&gt;OpenStack writes tests, but not ones that actually contact the API. When I ask it to do that, it
does, and the test fails without fixing it, and when I copy-paste the error code from running it,
it gets fixed.&lt;/li&gt;
&lt;li&gt;Compare their commits &lt;a href="https://github.com/sshine/AutoAgents/pull/1/changes"&gt;here&lt;/a&gt; and
&lt;a href="https://github.com/sshine/AutoAgents/pull/2/changes"&gt;here&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;I&amp;rsquo;d feel confident to vibe-code the full integration with either stack.&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="alternative-to-openclaw-in-rust"&gt;
 Alternative to OpenClaw in Rust
 &lt;a class="heading-anchor" href="#alternative-to-openclaw-in-rust" aria-label="Link to this section"&gt;&amp;para;&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;Lately I&amp;rsquo;ve been playing around with OpenClaw. I really like the whole idea, and I don&amp;rsquo;t like the
codebase. It uses Node.js and that whole ecosystem just smells like security incidents. I did some
research into who is making AI frameworks in Rust, and I found two interesting projects:&lt;/p&gt;</description></item><item><title>nixos-anywhere on Hetzner</title><link>https://simonshine.dk/articles/nixos-anywhere-on-hetzner/</link><pubDate>Fri, 20 Feb 2026 00:55:22 +0100</pubDate><guid>https://simonshine.dk/articles/nixos-anywhere-on-hetzner/</guid><description>&lt;h2 id="tldr"&gt;
 tl;dr:
 &lt;a class="heading-anchor" href="#tldr" aria-label="Link to this section"&gt;&amp;para;&lt;/a&gt;
&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;I installed NixOS using &lt;a href="https://github.com/nix-community/nixos-anywhere"&gt;nixos-anywhere&lt;/a&gt; for the first time.&lt;/li&gt;
&lt;li&gt;Along the way I also finally tried (and succeeded) to use &lt;a href="https://github.com/nix-community/disko"&gt;disko&lt;/a&gt; and &lt;a href="https://github.com/serokell/deploy-rs"&gt;deploy-rs&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Deploying from MacBook&amp;rsquo;s aarch64-darwin to the VM&amp;rsquo;s x86-64 required a few tweaks.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Tonight I wanted to try and run OpenClaw. I thought I would give it a persistent VM and that it
should run NixOS, of course. I already run several NixOS VMs in the cloud, but their deployment was
made with &lt;a href="https://github.com/apricote/hcloud-upload-image/"&gt;nixos-infect&lt;/a&gt;, which I promised myself I wouldn&amp;rsquo;t do again.&lt;/p&gt;</description></item><item><title>Vaultwarden on NixOS</title><link>https://simonshine.dk/articles/vaultwarden-on-nixos/</link><pubDate>Thu, 19 Feb 2026 08:51:15 +0100</pubDate><guid>https://simonshine.dk/articles/vaultwarden-on-nixos/</guid><description>&lt;p&gt;This post is about self-hosting &lt;a href="https://github.com/dani-garcia/vaultwarden"&gt;Vaultwarden&lt;/a&gt;, an unofficial Bitwarden server, so that
you can have self-hosted password management. Bitwarden clients on computers and phones can be
configured to use a custom server.&lt;/p&gt;
&lt;p&gt;I found a few blog posts that deserve some credit; sadly, none of them provide the simplest possible
configuration, or every piece of information I needed.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.ersocon.net/articles/vaultwarden-nixos~05f8a119-c382-413b-a945-43bf3266418b"&gt;Vaultwarden NixOS - Install/Configure Server&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.adyxax.org/blog/2023/12/20/migrating-vaultwarden-to-nixos/"&gt;Migrating vaultwarden to nixos | Yet Another SysAdmin Website&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/jasper-clarke/i-use-nixos-for-my-home-server-and-you-should-too-1n7p"&gt;I use NixOS for my home-server, and you should too! - DEV Community&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://blog.alper-celik.dev/posts/self-hosting-vaultwarden-and-setting-up-ssl-certificates-under-tailscale-in-nixos/"&gt;Self Hosting Vaultwarden and Setting up SSL Certificates under Tailscale in Nixos - Alper Çelik&amp;rsquo;s blog&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;They either assume you want to host your Vaultwarden on a VPN (making Let’s Encrypt not an option),
using secrets management, using Borg as backup system, or using postgresql as the database. All
reasonable choices, but hardly the simplest possible.&lt;/p&gt;</description></item><item><title>A git primer for future colleagues</title><link>https://simonshine.dk/articles/git-primer/</link><pubDate>Sat, 14 Feb 2026 15:56:43 +0100</pubDate><guid>https://simonshine.dk/articles/git-primer/</guid><description>&lt;p&gt;You become a git expert when you learn about &lt;code&gt;git reflog&lt;/code&gt;. Not because it&amp;rsquo;s an advanced feature.&lt;/p&gt;
&lt;p&gt;But because it gives you the confidence that whatever command you run can be reverted.&lt;/p&gt;
&lt;p&gt;Once you can undo any git command, your willingness to experiment increases significantly, and you
become the export.&lt;/p&gt;
&lt;p&gt;Besides that, &lt;code&gt;git reflog&lt;/code&gt; is not the most useful command, since it&amp;rsquo;s mostly for when you fucked up,
which is less and less.&lt;/p&gt;</description></item><item><title>Vendoring Hugo themes with Nix and Git</title><link>https://simonshine.dk/articles/vendoring-hugo-themes-with-nix-and-git/</link><pubDate>Sat, 07 Feb 2026 13:44:17 +0100</pubDate><guid>https://simonshine.dk/articles/vendoring-hugo-themes-with-nix-and-git/</guid><description>&lt;h2 id="tldr"&gt;
 tl;dr:
 &lt;a class="heading-anchor" href="#tldr" aria-label="Link to this section"&gt;&amp;para;&lt;/a&gt;
&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;I forked the git repo for my Hugo theme and pushed my feature to a branch&lt;/li&gt;
&lt;li&gt;I refactored the blog repo&amp;rsquo;s Nix code to be multi-file and flake-based&lt;/li&gt;
&lt;li&gt;I switched my Nix-vendored theme to use this git repo/branch&lt;/li&gt;
&lt;li&gt;I made it possible to use the git repo checked out locally for rapid prototyping&lt;/li&gt;
&lt;li&gt;I retained the possibility to use the Nix-vendored theme (same branch, but committed and pushed)&lt;/li&gt;
&lt;li&gt;I found a way to easily deploy using either pathway without changing files (&lt;code&gt;--theme&lt;/code&gt;, &lt;code&gt;just&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="context"&gt;
 Context
 &lt;a class="heading-anchor" href="#context" aria-label="Link to this section"&gt;&amp;para;&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;I migrated this website to Hugo in February 2025:&lt;/p&gt;</description></item><item><title>Speeding up CI more with Nix</title><link>https://simonshine.dk/articles/speeding-up-ci-more-with-nix/</link><pubDate>Mon, 02 Feb 2026 00:44:38 +0100</pubDate><guid>https://simonshine.dk/articles/speeding-up-ci-more-with-nix/</guid><description>&lt;p&gt;Recently I wrote &lt;a href="https://simonshine.dk/articles/speeding-up-ci-with-nix/"&gt;Speeding up CI with Nix&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I improved a 2m30s build to 1m30s after it had recently deteriorated to 10m30s.&lt;/p&gt;
&lt;p&gt;I have another repository that would benefit from Nix.&lt;/p&gt;
&lt;p&gt;Not because of speedups, but because it fails to install Chinese fonts properly.&lt;/p&gt;
&lt;p&gt;My Typst résumé&amp;rsquo;s CI failed to render Chinese fonts for the longest time; for that reason, I never
actually made use of the PDFs that I released. It is the &lt;code&gt;fontist/setup-fontist&lt;/code&gt; and &lt;code&gt;fontist install --formula noto_serif_cjk&lt;/code&gt; parts below (using 3m2s out of 3m20s):&lt;/p&gt;</description></item><item><title>Speeding up CI with Nix</title><link>https://simonshine.dk/articles/speeding-up-ci-with-nix/</link><pubDate>Sun, 25 Jan 2026 12:28:38 +0100</pubDate><guid>https://simonshine.dk/articles/speeding-up-ci-with-nix/</guid><description>&lt;p&gt;In the article &lt;a href="https://simonshine.dk/articles/maintaining-libraries-with-claude"&gt;Maintaining libraries with Claude&lt;/a&gt; I
update the CI pipeline to release the package automatically on Hackage and GitHub when pushing a
version tag.&lt;/p&gt;
&lt;p&gt;I added a git hook, &lt;a href="https://github.com/sshine/evm-opcodes/blob/main/.github/hooks/check-cabal-sync.sh"&gt;check-cabal-sync.sh&lt;/a&gt;, that checks the version in package.yaml against the
version in evm-opcodes.cabal, as well as the version in the git tag, to ensure I ran &lt;code&gt;hpack&lt;/code&gt; before
releasing; otherwise Hackage upload will fail.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Total CI running time: 2m30s before&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Of which &lt;code&gt;haskell-actions/setup&lt;/code&gt; takes: 2m5s.&lt;/p&gt;</description></item><item><title>zsh config for NixOS + nix-darwin</title><link>https://simonshine.dk/articles/nix-darwin-zsh/</link><pubDate>Sun, 25 Jan 2026 01:11:55 +0100</pubDate><guid>https://simonshine.dk/articles/nix-darwin-zsh/</guid><description>&lt;h2 id="tldr"&gt;
 tl;dr:
 &lt;a class="heading-anchor" href="#tldr" aria-label="Link to this section"&gt;&amp;para;&lt;/a&gt;
&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;I&amp;rsquo;m taking Nix more seriously on my MacBook&lt;/li&gt;
&lt;li&gt;I want to share my zsh config across all systems&lt;/li&gt;
&lt;li&gt;Because my zsh config was NixOS-only, I&amp;rsquo;ve had to refactor it&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="why"&gt;
 Why
 &lt;a class="heading-anchor" href="#why" aria-label="Link to this section"&gt;&amp;para;&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;I&amp;rsquo;ve been running NixOS for 3 years now.&lt;/p&gt;
&lt;p&gt;During this whole time I&amp;rsquo;ve also owned a MacBook.&lt;/p&gt;
&lt;p&gt;And while I started installing Nix on my MacBook, I didn&amp;rsquo;t really appreciate it until installing
NixOS on an x86-64-based laptop. Nix, compared to Homebrew, just felt complicated and unnecessary
as a way to install programs.&lt;/p&gt;</description></item><item><title>Maintaining libraries with Claude</title><link>https://simonshine.dk/articles/maintaining-libraries-with-claude/</link><pubDate>Wed, 14 Jan 2026 23:38:11 +0100</pubDate><guid>https://simonshine.dk/articles/maintaining-libraries-with-claude/</guid><description>&lt;h2 id="tldr"&gt;
 tl;dr
 &lt;a class="heading-anchor" href="#tldr" aria-label="Link to this section"&gt;&amp;para;&lt;/a&gt;
&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Claude bumps the versions.&lt;/li&gt;
&lt;li&gt;Claude finds missing opcodes.&lt;/li&gt;
&lt;li&gt;Claude finds even more missing opcodes I didn&amp;rsquo;t know about.&lt;/li&gt;
&lt;li&gt;Claude correctly handles compiler warnings that indicate cascading changes.&lt;/li&gt;
&lt;li&gt;Claude correctly handles what files are auto-generated and how to regenerate them.&lt;/li&gt;
&lt;li&gt;Stretch Goal #1: Release via CI to Hackage and GitHub&lt;/li&gt;
&lt;li&gt;Stretch Goal #2: Validate hpack pre-push&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="the-goal"&gt;
 The goal
 &lt;a class="heading-anchor" href="#the-goal" aria-label="Link to this section"&gt;&amp;para;&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;I maintain an open source library uploaded to a package repository called
&lt;a href="https://hackage.haskell.org/package/evm-opcodes"&gt;evm-opcodes&lt;/a&gt;. It just exposes an enum with the &lt;a href="https://www.evm.codes/"&gt;assembly instructions (opcodes) for
Ethereum&amp;rsquo;s VM&lt;/a&gt;, in case you want to build a smart contract compiler in Haskell.&lt;/p&gt;</description></item><item><title>Using NixOS for Exam Computers</title><link>https://simonshine.dk/articles/nixos-exam-computers/</link><pubDate>Tue, 13 Jan 2026 16:20:48 +0100</pubDate><guid>https://simonshine.dk/articles/nixos-exam-computers/</guid><description>&lt;p&gt;I used NixOS to define two exam computers for a programming exam.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;One in a presentation room with an examiner and a censor, connected to a projector.&lt;/li&gt;
&lt;li&gt;Another one in an adjacent preparation room, just connected to the internet.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Both computers had the same software development environments, and a system user for each student.
The presentation computer was connected to a projector with screen mirroring. Some syncing of files
was necessary between the two computers. Both computers needed various exam task descriptions on
disk.&lt;/p&gt;</description></item><item><title>Kubernetes-valgfaget der ikke blev til noget</title><link>https://simonshine.dk/articles/kubernetes-valgfag/</link><pubDate>Wed, 07 Jan 2026 13:53:14 +0100</pubDate><guid>https://simonshine.dk/articles/kubernetes-valgfag/</guid><description>&lt;p&gt;Planen var at stoppe som underviser på fuldtid og have et enkelt valgfag som timelærer. Valgfaget
blev desværre aflyst efter det var blevet godkendt og var blevet valgt af de studerende, for at
spare på eksterne timer. Det var lidt ærgerligt, da jeg havde været transparent omkring planen af
hensyn til at give god tid til at finde min afløser.&lt;/p&gt;
&lt;p&gt;Titlen skulle have været &amp;ldquo;Cloud Native Kubernetes&amp;rdquo; og var det næstmest populære valgfag til foråret.&lt;/p&gt;</description></item><item><title>End of teaching retrospective</title><link>https://simonshine.dk/articles/end-of-teaching-retrospective/</link><pubDate>Thu, 01 Jan 2026 09:30:15 +0100</pubDate><guid>https://simonshine.dk/articles/end-of-teaching-retrospective/</guid><description>&lt;p&gt;I quit my teacher job.&lt;/p&gt;
&lt;p&gt;Unlike most of my teaching blog posts, this entry is not in Danish.&lt;/p&gt;
&lt;p&gt;I want to write a retrospective blog post about having been a CS teacher for two years, having been
on paternity leave, and picking up consulting again, but via an agency instead of freelance, which
I had done in the years prior, and not primarily focusing on embedded Rust.&lt;/p&gt;
&lt;p&gt;I switched the consulting agency of employment in the last minute before quitting the teacher job.
The story that motivated me to stop teaching is tied to an ambitious project that the old agency is
doing, and since I wasn&amp;rsquo;t going to work there after all, the story I told had to change.&lt;/p&gt;</description></item><item><title>Funktionel datamodellering</title><link>https://simonshine.dk/articles/funktionel-datamodellering/</link><pubDate>Thu, 01 Jan 2026 00:00:00 +0100</pubDate><guid>https://simonshine.dk/articles/funktionel-datamodellering/</guid><description>&lt;p&gt;I &lt;a href="https://simonshine.dk/articles/rekursivt-beruset-med-monoider/"&gt;Om at blive rekursivt beruset med monoider&lt;/a&gt;
modellerer jeg &lt;em&gt;superdrinks&lt;/em&gt; som en &lt;a href="https://en.wikipedia.org/wiki/Monoid"&gt;monoid&lt;/a&gt; og viser hvordan man kan programmere med dem
ved hjælp af simple algebraiske datatyper. Og i &lt;a href="https://simonshine.dk/articles/design-patterns-popkultur/"&gt;Design Patterns i popkultur&lt;/a&gt; fandt jeg et eksempel på et design pattern:&lt;/p&gt;
&lt;p&gt;PPAP kan modelleres som et &lt;a href="https://en.wikipedia.org/wiki/Magma_(algebra)#Free_magma"&gt;frit magma&lt;/a&gt;: en algebraisk struktur lukket under komposition,
men uden kommutativitet, idempotens, eller identitetselement. Men hvordan gør man det til en konkret
datastruktur i et programmeringssprog?&lt;/p&gt;
&lt;p&gt;Et frit magma er helt oplagt at modellere som en datatype i et type-sikkert programmeringssprog som Java, Rust eller Haskell. Vi kan udnytte typesystemet til at fange de algebraiske egenskaber vi har analyseret, sådan at datatypen opfører sig efter de regler vi har etableret gælder for det fænomen, vi modellerer.&lt;/p&gt;</description></item><item><title>Animating the NixOS logo</title><link>https://simonshine.dk/articles/nixos-logo/</link><pubDate>Tue, 30 Dec 2025 03:23:47 +0100</pubDate><guid>https://simonshine.dk/articles/nixos-logo/</guid><description>&lt;p&gt;I really like NixOS.&lt;/p&gt;
&lt;p&gt;I also like to draw the NixOS logo.&lt;/p&gt;
&lt;p&gt;While there are SVGs and PNGs at &lt;a href="https://github.com/sshine/web-sys-turtles"&gt;github:nixos/branding&lt;/a&gt;, the logo is very geometric
and should be easily expressed as a program. My first attempt with &lt;a href="https://github.com/sshine/web-sys-turtles"&gt;turtle graphics&lt;/a&gt;
was an experiment to work with HTML Canvas via Wasm. It works by instructing a small robot turtle,
Bob, to walk and paint in the shape of a λ; you then repeat that six times:&lt;/p&gt;</description></item><item><title>Design Patterns i popkultur</title><link>https://simonshine.dk/articles/design-patterns-popkultur/</link><pubDate>Thu, 04 Sep 2025 11:34:00 +0200</pubDate><guid>https://simonshine.dk/articles/design-patterns-popkultur/</guid><description>&lt;p&gt;Jeg har, som mange andre, været grebet af sangen PPAP:&lt;/p&gt;
&lt;div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;"&gt;
			&lt;iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share; fullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/Ct6BUPvE2sM?autoplay=0&amp;amp;controls=1&amp;amp;end=0&amp;amp;loop=0&amp;amp;mute=0&amp;amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"&gt;&lt;/iframe&gt;
		&lt;/div&gt;

&lt;p&gt;Jeg har ikke kunnet lade sangen slippe, fordi PIKOTARO tydeligvis formidler abstrakt algebra:&lt;/p&gt;
&lt;p&gt;Studiet af algebraiske strukturer, hvor man betragter sammensætningen af matematiske objekter, der
ikke nødvendigvis er tal.&lt;/p&gt;
&lt;p&gt;Abstrakt algebra finder bred anvendelse i programmering. Vi kalder det et &lt;a href="https://fsharpforfunandprofit.com/fppatterns/"&gt;designmønster&lt;/a&gt;,
fordi det er et abstrakt mønster som går igen, når vi arbejder med konkrete datastrukturer.
Designmønstre er værd at lære fordi man med tiden kan arbejde på et højere niveau, hvor man kan
drage på flere af sine erfaringer, fordi mange ting er lavet på den samme måde.&lt;/p&gt;</description></item><item><title>JSON Schema in Haskell using AutoDoCodec</title><link>https://simonshine.dk/articles/json-schema-haskell-autodocodec/</link><pubDate>Sat, 26 Jul 2025 16:00:57 +0100</pubDate><guid>https://simonshine.dk/articles/json-schema-haskell-autodocodec/</guid><description>&lt;p&gt;In April 2022 I made a &lt;a href="https://simonshine.dk/articles/a-review-of-json-schema-libraries-for-haskell/"&gt;review of Haskell libraries for JSON Schema&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The outcome of my research was that there weren&amp;rsquo;t really any good libraries for dealing with &lt;a href="https://json-schema.org/"&gt;JSON Schema&lt;/a&gt; from within Haskell. That was a surprising disappointment, because Haskell often has very good libraries (although they may be hard to use). The reason is that the JSON Schema specification itself isn&amp;rsquo;t very well-made.&lt;/p&gt;
&lt;p&gt;I eventually gave up using any of them.&lt;/p&gt;</description></item><item><title>Just: En bedre Makefile</title><link>https://simonshine.dk/articles/just-en-bedre-makefile/</link><pubDate>Mon, 21 Jul 2025 23:24:15 +0000</pubDate><guid>https://simonshine.dk/articles/just-en-bedre-makefile/</guid><description>&lt;p&gt;Har du nogensinde arbejdet på et softwareprojekt og set en fil kaldet &lt;code&gt;Makefile&lt;/code&gt;? Makefiles bruges traditionelt til at automatisere kompileringsprocesser, men mange udviklere bruger dem også til at køre almindelige projektkommandoer som:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;make test
make deploy
make start
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Men der findes et bedre alternativ: Justfile&lt;/p&gt;
&lt;h2 id="problemet-med-makefiles-til-projektkommandoer"&gt;
 Problemet med Makefiles til projektkommandoer
 &lt;a class="heading-anchor" href="#problemet-med-makefiles-til-projektkommandoer" aria-label="Link to this section"&gt;&amp;para;&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;Makefiles blev oprindeligt designet til at kompilere filer - ikke til at køre projektkommandoer. Når du bruger Make til at køre tasks som &lt;code&gt;make test&lt;/code&gt; eller &lt;code&gt;make deploy&lt;/code&gt;, misbruger du faktisk værktøjet.&lt;/p&gt;</description></item><item><title>Claude Code og git-bug: Når AI har brug for issue tracking</title><link>https://simonshine.dk/articles/claude-code-git-bug/</link><pubDate>Mon, 21 Jul 2025 13:03:06 +0000</pubDate><guid>https://simonshine.dk/articles/claude-code-git-bug/</guid><description>&lt;p&gt;Jeg prøvede for nyligt at kombinere Claude Code med issue tracking.&lt;/p&gt;
&lt;p&gt;Som udvikler er det en udfordring at holde styr på bugs, opgaver og ideer i sine projekter. Vi
mennesker bruger grafiske issue tracking-værktøjer som Jira og GitHub Projects. Personligt har jeg
været glad for &lt;a href="https://linear.app"&gt;Linear.app&lt;/a&gt;, som er gratis op til 250 tickets. Claude Code har sit eget
ad-hoc to-do-system, men den er sessionsbundet, og persister ikke med projektet.&lt;/p&gt;
&lt;p&gt;Jeg har indtil nu brugt Claude Code til at dumpe dens kontekst ind i tekstfiler såsom
&lt;a href="https://www.anthropic.com/engineering/claude-code-best-practices"&gt;&lt;code&gt;CLAUDE.md&lt;/code&gt;&lt;/a&gt;, &lt;code&gt;PLAN.md&lt;/code&gt; og &lt;code&gt;doc/XYZ.md&lt;/code&gt; så jeg kan vende tilbage til
den forståelse som opbygges omkring delproblemer.&lt;/p&gt;</description></item><item><title>Hvordan understøtter man europæisk cloud i undervisningen?</title><link>https://simonshine.dk/articles/europaeisk-cloud-i-undervisning/</link><pubDate>Tue, 06 May 2025 13:03:06 +0000</pubDate><guid>https://simonshine.dk/articles/europaeisk-cloud-i-undervisning/</guid><description>&lt;h2 id="tldr-den-ultra-korte-version"&gt;
 &lt;em&gt;tl;dr&lt;/em&gt; (den ultra korte version):
 &lt;a class="heading-anchor" href="#tldr-den-ultra-korte-version" aria-label="Link to this section"&gt;&amp;para;&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;I de tekniske fag på Erhvervsakademi København kunne man&amp;hellip;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;afskaffe brugen af Azure Web Apps, Azure Managed MySQL på 2. semester&lt;/li&gt;
&lt;li&gt;introducere Docker Compose og Linux på 2. semester i stedet&lt;/li&gt;
&lt;li&gt;driftssætte via Docker Compose på Linux på en VPS på 3. semester&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="en-opsummering-af-artiklen-nedenfor"&gt;
 En opsummering af artiklen nedenfor:
 &lt;a class="heading-anchor" href="#en-opsummering-af-artiklen-nedenfor" aria-label="Link to this section"&gt;&amp;para;&lt;/a&gt;
&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Ved at undervise i cloud-agnostisk teknologi som ikke fastlåser én til den enkelte cloud:
&lt;ul&gt;
&lt;li&gt;Teknologier som Docker Compose, Linux og SSH, Terraform, Ansible og Kubernetes&lt;/li&gt;
&lt;li&gt;Workflows som &lt;a href="https://about.gitlab.com/topics/gitops/"&gt;GitOps&lt;/a&gt;, organisationer såsom &lt;a href="https://www.cncf.io/about/who-we-are/"&gt;CNCF&lt;/a&gt; (Cloud Native Computing Foundation)&lt;/li&gt;
&lt;li&gt;VM&amp;rsquo;er, S3, open-source relationelle databaser, Virtual Private Cloud (netværksopsætning)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Undgå at undervise i cloud-teknologier som er helt unikke for én cloud fordi de er særligt indviklede:
&lt;ul&gt;
&lt;li&gt;Databaser: Amazon DynamoDB / Azure Cosmos DB / Google Cloud Firestore&lt;/li&gt;
&lt;li&gt;Proprietære on-premise-løsninger: AWS Outpost&lt;/li&gt;
&lt;li&gt;Web interfaces: Azure Web Apps&lt;/li&gt;
&lt;li&gt;Identitet: Azure AD / Entra ID / AWS IAM / Cloud IAM / Cloud Identity&lt;/li&gt;
&lt;li&gt;Containerized serverless: Google Cloud Run / AWS Fargate / Azure Container Apps&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Der findes ingen europæiske cloud-alternativer til de store amerikanske clouds&lt;/li&gt;
&lt;li&gt;Der findes suveræne cloud-baserede alternativer, hvis man siger &amp;ldquo;cloud&amp;rdquo; er et spektrum&lt;/li&gt;
&lt;li&gt;Suverænitet opnås igennem ejerskab frem for outsourcing af drift og intellekt&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="hvordan-forstås-suverænitet"&gt;
 Hvordan forstås suverænitet?
 &lt;a class="heading-anchor" href="#hvordan-forst%c3%a5s-suver%c3%a6nitet" aria-label="Link to this section"&gt;&amp;para;&lt;/a&gt;
&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;ldquo;The cloud is just someone else&amp;rsquo;s computer.&amp;rdquo; - &lt;a href="https://blog.codinghorror.com/the-cloud-is-just-someone-elses-computer/"&gt;Jeff Atwood&lt;/a&gt; (StackOverflow co-founder)&lt;/p&gt;</description></item><item><title>Hvorfor blev jeg datamatiker-lærer?</title><link>https://simonshine.dk/articles/hvorfor-blev-jeg-datamatiker-laerer/</link><pubDate>Thu, 17 Apr 2025 16:41:46 +0100</pubDate><guid>https://simonshine.dk/articles/hvorfor-blev-jeg-datamatiker-laerer/</guid><description>&lt;blockquote&gt;
&lt;p&gt;Artiklen her er introen til min daværende blog, datamatik.blog, som jeg drev i 2025 mens jeg var
datamatiker-lærer på KEA, Københavns Erhvervsakademi. Jeg tror måske jeg følte, jeg skulle
forsvare at gå ned i løn og undervise på en kort videregående uddannelse.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Hej, jeg hedder Simon Shine, og jeg er en programmør født i 1985.&lt;/p&gt;
&lt;p&gt;Jeg er også datamatiker-lærer på &lt;a href="https://ek.dk"&gt;Erhvervsakademi København&lt;/a&gt;, fusionen mellem &lt;a href="https://kea.dk"&gt;KEA&lt;/a&gt; og &lt;a href="https://cphbusiness.dk"&gt;Cphbusiness&lt;/a&gt; som foregår her i 2025. Det er det største erhvervsakademi i Danmark. Jeg underviser i programmering og grundlæggende datalogi. Datamatiker er en 2,5-årig uddannelse med fokus på praktisk softwareudvikling. Jeg har været lærer der siden 2024. Før det tilbragte jeg mange år som undervisningsassistent på &lt;a href="https://di.ku.dk"&gt;DIKU&lt;/a&gt;, Datalogisk Institut på Københavns Universitet, i stedet for at færdiggøre min uddannelse.&lt;/p&gt;</description></item><item><title>Om at blive rekursivt beruset med monoider</title><link>https://simonshine.dk/articles/rekursivt-beruset-med-monoider/</link><pubDate>Sun, 13 Apr 2025 20:26:14 +0000</pubDate><guid>https://simonshine.dk/articles/rekursivt-beruset-med-monoider/</guid><description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Disclaimer:&lt;/strong&gt; Jeg er &lt;em&gt;ikke&lt;/em&gt; mixolog. Dette er &lt;em&gt;ikke&lt;/em&gt; professionel cocktailrådgivning!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Lyt i stedet:&lt;/strong&gt; Podcast-afsnittet &lt;a href="https://haskellweekly.news/episode/26.html"&gt;Haskell Weekly, Episode 26: Recursive Monoids&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Jeg læste engang, at man kunne modellere cocktails som &lt;a href="https://en.wikipedia.org/wiki/Monoid"&gt;monoider&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Det er et virkelig cool eksempel på funktionsorienteret modellering, som jeg gerne vil uddybe.&lt;/p&gt;
&lt;p&gt;Først vil jeg demonstrere min uvidenhed og antage, at cocktailopskrifter er frie kommutative monoider over ingredienser:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Rækkefølgen hvori du tilføjer ingredienser har ingen betydning,&lt;/li&gt;
&lt;li&gt;Hvis du kombinerer to cocktails, får du en anden cocktail,&lt;/li&gt;
&lt;li&gt;Identitetscocktailen er den tomme cocktail uden ingredienser.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Dernæst vil jeg genfortælle en rekursiv cocktailopskrift, som jeg lærte om da jeg studerede datalogi.&lt;/p&gt;</description></item><item><title>Deploying a Rust web service on NixOS</title><link>https://simonshine.dk/articles/deploy-rust-webservice-nixos/</link><pubDate>Thu, 10 Apr 2025 11:31:54 +0000</pubDate><guid>https://simonshine.dk/articles/deploy-rust-webservice-nixos/</guid><description>&lt;p&gt;I have a small Rust web service that serves HTTP on a port.&lt;/p&gt;
&lt;p&gt;In order to run this in production, I want to&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;compile and install the web service binary on my server,&lt;/li&gt;
&lt;li&gt;run it as a non-interactive user on a local port, and&lt;/li&gt;
&lt;li&gt;expose the web service via an nginx reverse proxy&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I will use a subdirectory of an already existing website, such as a &lt;a href="https://simonshine.dk/articles/hugo-static-site-setup"&gt;Hugo
static website&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="a-web-service-package-derivation"&gt;
 A web service package derivation
 &lt;a class="heading-anchor" href="#a-web-service-package-derivation" aria-label="Link to this section"&gt;&amp;para;&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;In order to build and package my project, I need to express a derivation.&lt;/p&gt;</description></item><item><title>Corne Split Keyboard Layout</title><link>https://simonshine.dk/articles/corne-split-keyboard-layout/</link><pubDate>Thu, 10 Apr 2025 08:57:09 +0000</pubDate><guid>https://simonshine.dk/articles/corne-split-keyboard-layout/</guid><description>&lt;p&gt;I min barsel har jeg leget med et Corne v3 split keyboard, som jeg har købt på Taobao.&lt;/p&gt;
&lt;p&gt;Det har 46 taster. Til sammenligning har min MacBook Pro med dansk layout 79 taster.&lt;/p&gt;
&lt;h2 id="de-knapper-man-mister-vs-de-knapper-man-får"&gt;
 De knapper man mister vs. de knapper man får
 &lt;a class="heading-anchor" href="#de-knapper-man-mister-vs-de-knapper-man-f%c3%a5r" aria-label="Link to this section"&gt;&amp;para;&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;Man fristes til at fokusere på alle de knapper, man mister, frem for alle de knapper man får.&lt;/p&gt;
&lt;p&gt;Især får man noget som alle tastaturer burde have: Ekstra knapper til tommelfingrene.&lt;/p&gt;</description></item><item><title>Packaging open-source projects with Nix</title><link>https://simonshine.dk/articles/packaging-foss-example/</link><pubDate>Mon, 07 Apr 2025 13:33:50 +0000</pubDate><guid>https://simonshine.dk/articles/packaging-foss-example/</guid><description>&lt;h2 id="introduction"&gt;
 Introduction
 &lt;a class="heading-anchor" href="#introduction" aria-label="Link to this section"&gt;&amp;para;&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Problem:&lt;/strong&gt; You run NixOS and want to download, compile and run some project on GitHub.&lt;/p&gt;
&lt;p&gt;While most projects come with a README that tell you what packages to &lt;code&gt;apt-get install&lt;/code&gt;
if you run Ubuntu, they often don&amp;rsquo;t explain how to do the same on NixOS. This
makes NixOS a harder Linux distro to use, because you need to be able to write
Nix to run certain programs.&lt;/p&gt;</description></item><item><title>Hvad er "datamatik"?</title><link>https://simonshine.dk/articles/hvad-er-datamatik/</link><pubDate>Thu, 06 Mar 2025 23:58:01 +0100</pubDate><guid>https://simonshine.dk/articles/hvad-er-datamatik/</guid><description>&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://ordnet.dk/ddo/ordbog?aselect=datamatik&amp;amp;query=datamatik"&gt;datamatik&lt;/a&gt;, sb. (læren om) hensigtsmæssig behandling af data på edb-systemer [1966]&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I dette første blogindlæg vil jeg sætte spørgsmålstegn ved bloggens navn.&lt;/p&gt;
&lt;p&gt;Ordet &amp;ldquo;datamat&amp;rdquo; er en anakronisme: Opfundet i 1960&amp;rsquo;erne, da referencer til computere blev almindelige, og dansk populærkultur endnu ikke var opslugt af amerikansk engelsk. Det hedder &amp;ldquo;computer&amp;rdquo; nu, med mindre man har boet i kælderen på datalogisk institut.&lt;/p&gt;
&lt;p&gt;Mens &amp;ldquo;datamat&amp;rdquo; er en sjov, gammeldags måde at beskrive sin computer på, er &amp;ldquo;datamatik&amp;rdquo; gået så meget af mode, at ingen bruger ordet, selv ikke for sjov. Datamatik&amp;rsquo;s fætter, &amp;ldquo;datalogi&amp;rdquo;, bruges af universitetsuddannelsen.&lt;/p&gt;</description></item><item><title>Setting up a Hugo static website with Nix</title><link>https://simonshine.dk/articles/hugo-static-site-setup/</link><pubDate>Mon, 24 Feb 2025 00:00:00 +0000</pubDate><guid>https://simonshine.dk/articles/hugo-static-site-setup/</guid><description>&lt;h2 id="tldr"&gt;
 tl;dr
 &lt;a class="heading-anchor" href="#tldr" aria-label="Link to this section"&gt;&amp;para;&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;See the tutorial code at &lt;a href="https://github.com/nix-tools/nix-hugo"&gt;https://github.com/nix-tools/nix-hugo&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;This tutorial…&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Provides a development environment for running interactively&lt;/li&gt;
&lt;li&gt;Provides a derivation for deployment on NixOS&lt;/li&gt;
&lt;li&gt;Deploys to a webserver using &lt;code&gt;just&lt;/code&gt;, &lt;code&gt;ssh&lt;/code&gt; and &lt;code&gt;scp&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="step-0-prerequisites"&gt;
 Step 0: Prerequisites
 &lt;a class="heading-anchor" href="#step-0-prerequisites" aria-label="Link to this section"&gt;&amp;para;&lt;/a&gt;
&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;You have Nix installed (required)&lt;/li&gt;
&lt;li&gt;You have flakes enabled (required)&lt;/li&gt;
&lt;li&gt;You have nix-direnv enabled (recommended)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="step-1-a-development-environment"&gt;
 Step 1: A development environment
 &lt;a class="heading-anchor" href="#step-1-a-development-environment" aria-label="Link to this section"&gt;&amp;para;&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;A dev shell is provided as a file called shell.nix:&lt;/p&gt;</description></item><item><title>Minimal Sufficient Groups</title><link>https://simonshine.dk/articles/minimal-sufficient-groups/</link><pubDate>Sun, 02 Feb 2025 16:11:57 +0100</pubDate><guid>https://simonshine.dk/articles/minimal-sufficient-groups/</guid><description>&lt;p&gt;This is a programming interview question I got some years ago.&lt;/p&gt;
&lt;h2 id="the-problem"&gt;
 The problem
 &lt;a class="heading-anchor" href="#the-problem" aria-label="Link to this section"&gt;&amp;para;&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;When a certain company creates a new project, they enumerate the skills required for the project, and they form a project group where each of those skills are represented. Project groups have two rules:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Sufficient:&lt;/strong&gt; Each required skill should be represented with at least one group member.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Minimal:&lt;/strong&gt; Each group member should be essential, so removing any one member from the team should make the rest of the team no longer sufficient. Members can have overlapping skills, as long as everyone is essential in some capacity.&lt;/p&gt;</description></item><item><title>Why you should be careful with the Default trait/typeclass</title><link>https://simonshine.dk/articles/why-you-should-be-careful-with-the-default-trait-typeclass/</link><pubDate>Tue, 25 Oct 2022 16:13:28 +0100</pubDate><guid>https://simonshine.dk/articles/why-you-should-be-careful-with-the-default-trait-typeclass/</guid><description>&lt;p&gt;You heard it. &lt;strong&gt;&lt;code&gt;Default&lt;/code&gt; considered harmful!&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;tl;dr:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Default&lt;/code&gt; has no defining or testable properties&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Default&lt;/code&gt; is not even guaranteed to be the same between runs&lt;/li&gt;
&lt;li&gt;Defaults are context-specific: One type does not always have one default&lt;/li&gt;
&lt;li&gt;Defaults are useful for transitive derivation, but context drift risks causing errors&lt;/li&gt;
&lt;li&gt;Your &lt;code&gt;Default&lt;/code&gt; instance is dangerous if you cannot carelessly replace it with any other value&lt;/li&gt;
&lt;li&gt;Haskellers: Use the &lt;code&gt;default*&lt;/code&gt; pattern instead&lt;/li&gt;
&lt;li&gt;Rustaceans: Use &lt;code&gt;Default&lt;/code&gt; with care and respect&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="default-in-rust-and-in-haskell"&gt;
 &lt;code&gt;Default&lt;/code&gt; in Rust and in Haskell
 &lt;a class="heading-anchor" href="#default-in-rust-and-in-haskell" aria-label="Link to this section"&gt;&amp;para;&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;Rust&amp;rsquo;s standard library has a &lt;a href="https://doc.rust-lang.org/std/default/trait.Default.html"&gt;&lt;code&gt;Default&lt;/code&gt; trait&lt;/a&gt; that lets you provide a default value for any type:&lt;/p&gt;</description></item><item><title>A review of JSON Schema libraries for Haskell</title><link>https://simonshine.dk/articles/a-review-of-json-schema-libraries-for-haskell/</link><pubDate>Sun, 10 Apr 2022 16:14:27 +0100</pubDate><guid>https://simonshine.dk/articles/a-review-of-json-schema-libraries-for-haskell/</guid><description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Update 2025-07-26: This review is obsolete. &lt;a href="https://simonshine.dk/articles/json-schema-haskell-autodocodec/"&gt;Consider using AutoDoCodec&lt;/a&gt;.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;a href="http://json-schema.org/"&gt;JSON Schema&lt;/a&gt; is a JSON format that describes JSON formats.&lt;/p&gt;
&lt;p&gt;It is mostly used to validate that a JSON value has the correct format, but it can also be used to generate random values that fit a schema definition. This may be useful for testing.&lt;/p&gt;
&lt;p&gt;The latest version of JSON Schema is called &amp;ldquo;Draft 2020-12&amp;rdquo;, and the way to define a schema using this draft:&lt;/p&gt;</description></item><item><title>Has-style traits in Rust</title><link>https://simonshine.dk/articles/has-style-traits-in-rust/</link><pubDate>Thu, 31 Mar 2022 16:16:50 +0100</pubDate><guid>https://simonshine.dk/articles/has-style-traits-in-rust/</guid><description>&lt;p&gt;I recently experimented with &lt;a href="https://simonshine.dk/blog/rust-traits/"&gt;Rust trait instances on bounded generic types&lt;/a&gt;. This is the fancy way of saying &lt;code&gt;impl&amp;lt;T: SomeTrait&amp;gt; AnotherTrait for T&lt;/code&gt;, i.e. any number of &lt;code&gt;AnotherTrait&lt;/code&gt; instances created as a result of the presence of a &lt;code&gt;SomeTrait&lt;/code&gt; instance.&lt;/p&gt;
&lt;p&gt;Here is a similar but slightly more complicated example of that.&lt;/p&gt;
&lt;p&gt;I have a number of things that all have a common base set of properties. I&amp;rsquo;d like for the common base set to be described with one trait, and all the ways in which the things are different to be described with another trait that may depend on the common things.&lt;/p&gt;</description></item><item><title>Writing code on doors</title><link>https://simonshine.dk/articles/writing-code-on-doors/</link><pubDate>Tue, 29 Mar 2022 16:20:20 +0100</pubDate><guid>https://simonshine.dk/articles/writing-code-on-doors/</guid><description>&lt;p&gt;Did you ever see pseudo-code in job ads that looked like&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-rust" data-lang="rust"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (&lt;span style="color:#75715e"&gt;$you&lt;/span&gt;.like_to_work(&lt;span style="color:#75715e"&gt;$here&lt;/span&gt;) &lt;span style="color:#f92672"&gt;===&lt;/span&gt; True) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;$job&lt;/span&gt;.apply();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;and thought to yourself a bunch of things about variables not being in scope, and whether their actual code has utterly specific library functions? Did you ever try to design an authentic piece of pseudo-code and found how difficult it is to remain concise enough to get the point across, but also idiomatic enough to even care expressing this as code?&lt;/p&gt;</description></item><item><title>A Terraform DNS moment</title><link>https://simonshine.dk/articles/a-terraform-dns-moment/</link><pubDate>Sat, 29 Jan 2022 16:21:50 +0100</pubDate><guid>https://simonshine.dk/articles/a-terraform-dns-moment/</guid><description>&lt;p&gt;&lt;strong&gt;tl;dr:&lt;/strong&gt; I stuck the IPv4 and IPv6 address of my VPS into an SPF record field text.&lt;/p&gt;
&lt;p&gt;I manage my DNS with Terraform. This means that past domain registration and changing nameservers, I manage DNS with the Terraform command-line interface and git. Today I experienced a moment of happiness when two pieces of personal infrastructure fitted.&lt;/p&gt;
&lt;p&gt;The scenario: I&amp;rsquo;m setting up a send-only email server on a VPS for Gitea notifications. If this works out well, I will consider using it for a newsletter. Doing this on a commercial cloud VPS means that my server&amp;rsquo;s IPv4 address gets a bad score. One way to improve on that score is to add an &lt;a href="http://www.open-spf.org/SPF_Record_Syntax/"&gt;SPF record&lt;/a&gt;.&lt;/p&gt;</description></item><item><title>Fiat Currencies Are Not One Thing</title><link>https://simonshine.dk/articles/fiat-currencies-are-not-one-thing/</link><pubDate>Fri, 10 Dec 2021 16:25:17 +0100</pubDate><guid>https://simonshine.dk/articles/fiat-currencies-are-not-one-thing/</guid><description>&lt;p&gt;When we discuss the potential future usefulness of cryptocurrency, we compare against existing use-cases of fiat currency. Given a particular fiat currency &amp;ndash; its name, its unit, and its exchange rate &amp;ndash; our language enforces us to think of it as one currency.&lt;/p&gt;
&lt;p&gt;This is a misconception that will lead to poorly designed alternatives with strong, but unaligned monetary properties at the wrong time. If we don&amp;rsquo;t think of a fiat currency as many currencies within one brand, then any attempt to re-model those use-cases with cryptocurrencies will come short of being practically useful.&lt;/p&gt;</description></item><item><title>Jq Hack 2 Curling the Right Binary on Github</title><link>https://simonshine.dk/articles/jq-hack-2-curling-the-right-binary-on-github/</link><pubDate>Sat, 04 Dec 2021 16:26:20 +0100</pubDate><guid>https://simonshine.dk/articles/jq-hack-2-curling-the-right-binary-on-github/</guid><description>&lt;p&gt;Something I see quite often is a combination of &lt;code&gt;grep&lt;/code&gt; and &lt;code&gt;cut&lt;/code&gt; to extract URLs from JSON objects, for example:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;$ curl -s https://api.github.com/repos/go-gitea/gitea/releases/latest \
 | grep browser_download_url \
 | cut -d &amp;#39;&amp;#34;&amp;#39; -f 4 \
 | grep &amp;#39;\linux-amd64$&amp;#39; \
 | wget -i -
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This is neat because &lt;code&gt;grep&lt;/code&gt; and &lt;code&gt;cut&lt;/code&gt; are almost always available on Linux. And the approach works for all kinds of plaintext documents with a semi-expectable format, not just JSON, so the approach is worth keeping around. But for JSON in particular, this approach is unnecessarily fragile because of a few unreasonable assumptions:&lt;/p&gt;</description></item><item><title>Why rewriting version control history matters</title><link>https://simonshine.dk/articles/why-rewriting-version-control-history-matters/</link><pubDate>Thu, 25 Nov 2021 16:27:18 +0100</pubDate><guid>https://simonshine.dk/articles/why-rewriting-version-control-history-matters/</guid><description>&lt;blockquote&gt;
&lt;p&gt;Life is understood backwards, but must be lived forwards.&lt;/p&gt;
&lt;p&gt;&amp;mdash; Søren Kierkegaard&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="a-summary-on-good-commit-messages"&gt;
 A summary on good commit messages
 &lt;a class="heading-anchor" href="#a-summary-on-good-commit-messages" aria-label="Link to this section"&gt;&amp;para;&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;Because version control systems, like git and GitHub, are at the heart of modern software development workflows, most workflows are heavily impacted by and defined in terms of how to operate and cooperate around version control.&lt;/p&gt;
&lt;p&gt;Making use of issue trackers, pull requests, review tools, and commit messages, there are many right ways to distribute the communication that revolves around a change in source code. So&amp;hellip;&lt;/p&gt;</description></item><item><title>Efficient Probability Testing</title><link>https://simonshine.dk/articles/efficient-probability-testing/</link><pubDate>Tue, 21 Sep 2021 16:02:16 +0100</pubDate><guid>https://simonshine.dk/articles/efficient-probability-testing/</guid><description>&lt;p&gt;I want share two open-source contributions that were lost on their original audience, yet has taught me something about testing functions that use randomness.&lt;/p&gt;
&lt;p&gt;I once had a task at work where a website displayed a banner ad from a selection of banner ads with some internal logic related to categories, weights and fallback banners. Since the result was random, how do you test the selection algorithm?&lt;/p&gt;
&lt;p&gt;In the concrete task, I ended up decoupling the &lt;code&gt;rand()&lt;/code&gt; call from the logic that builds the probability distribution, so that it could be tested deterministically. But below are some other approaches that I really like.&lt;/p&gt;</description></item><item><title>Baking metaphors in the imperative/declarative programming debate</title><link>https://simonshine.dk/articles/baking-metaphors-in-the-imperative-declarative-programming-debate/</link><pubDate>Fri, 17 Sep 2021 16:33:16 +0100</pubDate><guid>https://simonshine.dk/articles/baking-metaphors-in-the-imperative-declarative-programming-debate/</guid><description>&lt;p&gt;tl;dr:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Cake recipes contain both declarative and imperative parts.&lt;/li&gt;
&lt;li&gt;Declarative syntax is better for describing the problem domain.&lt;/li&gt;
&lt;li&gt;Each programming paradigm has its place in software architecture.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="baking-metaphors"&gt;
 Baking metaphors
 &lt;a class="heading-anchor" href="#baking-metaphors" aria-label="Link to this section"&gt;&amp;para;&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;An argument is floating around that imperative programming is easier, more natural for humans for the same reason that baking recipes are imperative. Here is a pie recipe for example:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://i.imgur.com/RninUoY.jpg" alt="A pie recipe"&gt;&lt;/p&gt;
&lt;p&gt;Typical baking recipes contain three parts:&lt;/p&gt;</description></item><item><title>Extracting the KUBECONFIG for a DigitalOcean Kubernetes cluster from a Terraform .tfstate</title><link>https://simonshine.dk/articles/extracting-the-kubeconfig-for-a-digitalocean-kubernetes-cluster-from-a-terraform-tfstate/</link><pubDate>Sat, 10 Jul 2021 16:34:07 +0100</pubDate><guid>https://simonshine.dk/articles/extracting-the-kubeconfig-for-a-digitalocean-kubernetes-cluster-from-a-terraform-tfstate/</guid><description>&lt;p&gt;When provisioning a Kubernetes cluster from DigitalOcean with Terraform, the &lt;code&gt;.tfstate&lt;/code&gt; contains a field called &lt;code&gt;raw_config&lt;/code&gt; that authenticates &lt;code&gt;kubectl&lt;/code&gt;. It can elegantly be extracted with &lt;code&gt;jq&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ae81ff"&gt;$ jq -r &amp;#39;.resources[]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ae81ff"&gt;| select(.type == &amp;#34;digitalocean_kubernetes_cluster&amp;#34;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ae81ff"&gt;| .instances[].attributes.kube_config[].raw_config&amp;#39; \ &lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ae81ff"&gt;terraform.tfstate&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;apiVersion&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;v1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;kind&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;Config&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;clusters&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;- &lt;span style="color:#f92672"&gt;cluster&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;certificate-authority-data&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;server&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;https://...k8s.ondigitalocean.com&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;your-cluster-name&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;contexts&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;- &lt;span style="color:#f92672"&gt;context&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;cluster&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;your-cluster-name&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;user&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;your-cluster-name-admin&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;your-cluster-name&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;current-context&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;your-cluster-name&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;users&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;- &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;your-cluster-name-admin&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;user&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;token&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If you provision multiple clusters, you cannot simply pipe the multiple &lt;code&gt;kind: Config&lt;/code&gt;s, but otherwhise, this output can be dumped straight into &lt;code&gt;~/.kube/config&lt;/code&gt;. Otherwise, you may want to dump it to a specific file before you run&lt;/p&gt;</description></item><item><title>Getting recursively drunk with monoids</title><link>https://simonshine.dk/articles/getting-recursively-drunk-with-monoids/</link><pubDate>Sun, 04 Oct 2020 16:05:52 +0100</pubDate><guid>https://simonshine.dk/articles/getting-recursively-drunk-with-monoids/</guid><description>&lt;p&gt;&lt;strong&gt;Disclaimer:&lt;/strong&gt; I am not a &lt;em&gt;mixologist&lt;/em&gt;. This is not professional cocktail advice!&lt;/p&gt;
&lt;p&gt;Sam Horvath-Hunt blogged about &lt;a href="https://dev.to/samhh/monoids-and-semigroups-2b94"&gt;modelling cocktails as monoids&lt;/a&gt;. This is a really cool example of FP modelling that I want to expand on. (Lennart Kolmodin once wrote that the dance steps of Tango form a monoid.)&lt;/p&gt;
&lt;p&gt;First, I will demonstrate my ignorance by assuming that cocktail recipes are free commutative monoids over ingredients:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The order in which you add ingredients does not matter,&lt;/li&gt;
&lt;li&gt;If you add two cocktails together, you get another cocktail,&lt;/li&gt;
&lt;li&gt;The identity cocktail is the empty cocktail with no ingredients in it.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Second, I want to up the ante with a recursive cocktail recipe.&lt;/p&gt;</description></item><item><title>Jq Hack 1 Colored Less</title><link>https://simonshine.dk/articles/jq-hack-1-colored-less/</link><pubDate>Mon, 28 Sep 2020 16:37:28 +0100</pubDate><guid>https://simonshine.dk/articles/jq-hack-1-colored-less/</guid><description>&lt;p&gt;Sometimes you want to look at a lot of JSON output in the terminal. Sometimes this JSON output is nicely formatted, and often it is compacted by a REST endpoint. Examples of verbose output:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;$ kubectl get nodes -o json
$ curl &amp;#39;https://hacker-news.firebaseio.com/v0/topstories.json&amp;#39;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;In both cases you can pipe the output through:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;$ ... | jq -C . | less -R
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The &lt;code&gt;-C&lt;/code&gt; parameter to &lt;code&gt;jq&lt;/code&gt; forces colors. When omitted, &lt;code&gt;jq&lt;/code&gt; will only print colors when an interactive terminal is detected. Piping further into &lt;code&gt;less&lt;/code&gt; removes the colors. Additionally, we have to tell &lt;code&gt;less&lt;/code&gt; to interpolate those ANSI colors so that we don&amp;rsquo;t see a bunch of &lt;code&gt;ESC[1;39m&lt;/code&gt; codes.&lt;/p&gt;</description></item><item><title>What if, when you grep, it goes into pager mode when results don't fit one screen?</title><link>https://simonshine.dk/articles/grep-pager-mode/</link><pubDate>Mon, 28 Sep 2020 16:35:50 +0100</pubDate><guid>https://simonshine.dk/articles/grep-pager-mode/</guid><description>&lt;p&gt;There is a general theme in GNU shell commands that if a program is able to deliver ANSI-colored output, it only does so in interactive shells, or if explicitly instructed. If you want to pipe output but have colors remain for later printing, you sometimes have to augment both the sending and the receiving command.&lt;/p&gt;
&lt;p&gt;Of the many alternatives to &lt;code&gt;grep&lt;/code&gt; I use &lt;code&gt;ack&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Given the following &lt;code&gt;~/.ackrc&lt;/code&gt;,&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;--flush
--pager
less -R -F
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;whenever I &lt;code&gt;ack&lt;/code&gt; for something, it uses &lt;code&gt;less&lt;/code&gt; as a pager, and if the search results fit a screen, it just prints them, including colors, and otherwise it enters &lt;code&gt;less&lt;/code&gt;&amp;lsquo;es interactive mode.&lt;/p&gt;</description></item><item><title>'Parse, don't validate' using ViewPatterns</title><link>https://simonshine.dk/articles/parse-don-t-validate-using-viewpatterns/</link><pubDate>Mon, 14 Sep 2020 16:38:44 +0100</pubDate><guid>https://simonshine.dk/articles/parse-don-t-validate-using-viewpatterns/</guid><description>&lt;p&gt;In 2019, Alexis King wrote the &lt;a href="https://lexi-lambda.github.io/blog/2019/11/05/parse-don-t-validate/"&gt;Parse, don&amp;rsquo;t validate&lt;/a&gt; article that catches some of the essences of type-driven software development and design. Several books were released on this matter, including Alexander Granin&amp;rsquo;s &lt;a href="https://leanpub.com/functional-design-and-architecture"&gt;Functional Design and Architecture&lt;/a&gt;, Sandy Maguire&amp;rsquo;s &lt;a href="https://leanpub.com/thinking-with-types"&gt;Thinking with Types: Type-Level Programming in Haskell&lt;/a&gt;, or even Edwin Brady&amp;rsquo;s &lt;a href="https://www.manning.com/books/type-driven-development-with-idris"&gt;Type-Driven Development with Idris&lt;/a&gt; for a book about the future.&lt;/p&gt;
&lt;p&gt;This blog post is about where Alexis&amp;rsquo; three words got me today.&lt;/p&gt;
&lt;p&gt;I refactor a Haskell library that handles Ethereum VM opcodes, and in particular, the part of the library that lets you convert a &lt;code&gt;Word8&lt;/code&gt; into an &lt;code&gt;Opcode&lt;/code&gt;.&lt;/p&gt;</description></item><item><title>Aggressive refactoring with Haskell</title><link>https://simonshine.dk/articles/aggressive-refactoring/</link><pubDate>Sat, 12 Sep 2020 16:10:02 +0100</pubDate><guid>https://simonshine.dk/articles/aggressive-refactoring/</guid><description>&lt;p&gt;Here is a small example of aggressive refactoring in Haskell. I am in the codebase of a compiler for a small smart contract language, and I&amp;rsquo;m replacing occurrences of &lt;code&gt;Integer&lt;/code&gt; with occurrences of &lt;code&gt;Word256&lt;/code&gt;, since the code generator in question targets Ethereum which has 256-bit word sizes.&lt;/p&gt;
&lt;p&gt;The compiler was originally made with &lt;code&gt;Integer&lt;/code&gt;, and in some cases its very own &lt;code&gt;Word256&lt;/code&gt; which was made in a crude way. &lt;code&gt;Integer&lt;/code&gt; because this is a very convenient data type in Haskell, even though arbitrary precision in this case is actually wrong, and the crude &lt;code&gt;Word256&lt;/code&gt; because it seemed simpler to make this type rather than import a library.&lt;/p&gt;</description></item><item><title>Hvorfor må jeg ikke eje en svensker?</title><link>https://simonshine.dk/articles/hvorfor-maa-jeg-ikke-eje-en-svensker/</link><pubDate>Wed, 01 Jan 2003 10:14:48 +0100</pubDate><guid>https://simonshine.dk/articles/hvorfor-maa-jeg-ikke-eje-en-svensker/</guid><description>&lt;p&gt;Hvorfor må jeg ikke eje en svensker?&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The following essay is an adaptation of a letter titled &lt;a href="https://rationalwiki.org/wiki/Why_can't_I_own_a_Canadian%3F"&gt;Why Can’t I Own a Canadian?&lt;/a&gt; by Dr. Laura Schlessinger in October 2002. This adaptation was originally posted by me to Ateistisk Selskab’s web forums but was later lost from here and copied without attribution.&lt;/p&gt;
&lt;p&gt;An archived copy of the article exists on &lt;a href="https://web.archive.org/web/20190607091530/https://simonshine.dk/posts/2003-01-01-hvorfor-maa-jeg-ikke-eje-en-svensker.html"&gt;an older version of this website&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Kære kristen&lt;/p&gt;
&lt;p&gt;Tak fordi du gør så meget for at prædike Guds ord. Jeg har lært en masse ved at gå i kirke, og jeg prøver at dele min viden med så mange som muligt. Når nogen for eksempel forsvarer homoseksualitet henviser jeg dem bare til 3. Mosebog kapitel 18 vers 22 der siger at du ikke må have samleje med en mand, som man har samleje med en kvinde. Det er en vederstyggelighed. Diskussion slut. Jeg har dog nogle spørgsmål om nogle andre love og hvordan man bør følge dem:&lt;/p&gt;</description></item><item><title/><link>https://simonshine.dk/about/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://simonshine.dk/about/</guid><description>&lt;h1 id="welcome"&gt;
 Welcome!
 &lt;a class="heading-anchor" href="#welcome" aria-label="Link to this section"&gt;&amp;para;&lt;/a&gt;
&lt;/h1&gt;
&lt;p&gt;I&amp;rsquo;m Simon.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m a computer programmer from Copenhagen, Denmark. I grew up on the island of Amager, and that&amp;rsquo;s
where I still live. I have been coding since 1997, and I studied Computer Science at &lt;a href="https://di.ku.dk"&gt;DIKU&lt;/a&gt;
in the period 2008-2017. I have been absolutely fascinated by programming, terminal systems
and computer networks ever since.&lt;/p&gt;
&lt;p&gt;My first website from the late 1990s, long lost, contained fantasy cartography produced by &lt;a href="https://www.gimp.org/"&gt;The
GIMP&lt;/a&gt;. Unfortunately those pages never left my home computer, and they were only available
when my 56k modem was connected, via constantly changing IP addresses. I didn&amp;rsquo;t get a lot of views.&lt;/p&gt;</description></item><item><title/><link>https://simonshine.dk/contact/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://simonshine.dk/contact/</guid><description>&lt;h1 id="contact"&gt;
 Contact
 &lt;a class="heading-anchor" href="#contact" aria-label="Link to this section"&gt;&amp;para;&lt;/a&gt;
&lt;/h1&gt;
&lt;p&gt;You can reach me at &lt;a href="mailto:simon@simonshine.dk"&gt;simon@simonshine.dk&lt;/a&gt;.&lt;/p&gt;</description></item><item><title/><link>https://simonshine.dk/links/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://simonshine.dk/links/</guid><description>&lt;h1 id="retro-articles-and-links"&gt;
 Retro articles and links
 &lt;a class="heading-anchor" href="#retro-articles-and-links" aria-label="Link to this section"&gt;&amp;para;&lt;/a&gt;
&lt;/h1&gt;
&lt;p&gt;My website was always more of a link collection than a blog.&lt;/p&gt;
&lt;p&gt;Since my website has since got several blogs, my link collection has become secondary.&lt;/p&gt;
&lt;p&gt;Keeping with nostalgia, this collection has accumulated over 20 years and contains timeless pieces.&lt;/p&gt;
&lt;p&gt;
&lt;li class="posts-list-item"&gt;
 &lt;a class="posts-list-item-title" href="https://lexi-lambda.github.io/blog/2019/11/05/parse-don-t-validate/"&gt;Parse, Don&amp;#39;t Validate&lt;/a&gt;
 &lt;span class="posts-list-item-description"&gt;
 &lt;svg class="icon icon-calendar" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"&gt;&lt;title&gt;calendar&lt;/title&gt;&lt;rect x="3" y="4" width="18" height="18" rx="2" ry="2"&gt;&lt;/rect&gt;&lt;line x1="16" y1="2" x2="16" y2="6"&gt;&lt;/line&gt;&lt;line x1="8" y1="2" x2="8" y2="6"&gt;&lt;/line&gt;&lt;line x1="3" y1="10" x2="21" y2="10"&gt;&lt;/line&gt;&lt;/svg&gt;
 2019, by Alexis King
 &lt;/span&gt;
&lt;/li&gt;


&lt;li class="posts-list-item"&gt;
 &lt;a class="posts-list-item-title" href="http://www.niquette.com/books/sophmag/heurist.htm"&gt;Discovering Assumptions&lt;/a&gt;
 &lt;span class="posts-list-item-description"&gt;
 &lt;svg class="icon icon-calendar" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"&gt;&lt;title&gt;calendar&lt;/title&gt;&lt;rect x="3" y="4" width="18" height="18" rx="2" ry="2"&gt;&lt;/rect&gt;&lt;line x1="16" y1="2" x2="16" y2="6"&gt;&lt;/line&gt;&lt;line x1="8" y1="2" x2="8" y2="6"&gt;&lt;/line&gt;&lt;line x1="3" y1="10" x2="21" y2="10"&gt;&lt;/line&gt;&lt;/svg&gt;
 1996, by Paul Niquette
 &lt;/span&gt;
&lt;/li&gt;


&lt;li class="posts-list-item"&gt;
 &lt;a class="posts-list-item-title" href="https://www.scottaaronson.com/blog/?p=1820"&gt;Eigenmorality&lt;/a&gt;
 &lt;span class="posts-list-item-description"&gt;
 &lt;svg class="icon icon-calendar" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"&gt;&lt;title&gt;calendar&lt;/title&gt;&lt;rect x="3" y="4" width="18" height="18" rx="2" ry="2"&gt;&lt;/rect&gt;&lt;line x1="16" y1="2" x2="16" y2="6"&gt;&lt;/line&gt;&lt;line x1="8" y1="2" x2="8" y2="6"&gt;&lt;/line&gt;&lt;line x1="3" y1="10" x2="21" y2="10"&gt;&lt;/line&gt;&lt;/svg&gt;
 2014, by Scott Aaronson
 &lt;/span&gt;
&lt;/li&gt;


&lt;li class="posts-list-item"&gt;
 &lt;a class="posts-list-item-title" href="http://www.princeton.edu/~pkrugman/interstellar.pdf"&gt;The Theory of Interstellar Trade&lt;/a&gt;
 &lt;span class="posts-list-item-description"&gt;
 &lt;svg class="icon icon-calendar" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"&gt;&lt;title&gt;calendar&lt;/title&gt;&lt;rect x="3" y="4" width="18" height="18" rx="2" ry="2"&gt;&lt;/rect&gt;&lt;line x1="16" y1="2" x2="16" y2="6"&gt;&lt;/line&gt;&lt;line x1="8" y1="2" x2="8" y2="6"&gt;&lt;/line&gt;&lt;line x1="3" y1="10" x2="21" y2="10"&gt;&lt;/line&gt;&lt;/svg&gt;
 1978, by Paul Krugman
 &lt;/span&gt;
&lt;/li&gt;


&lt;li class="posts-list-item"&gt;
 &lt;a class="posts-list-item-title" href="https://www.scottaaronson.com/writings/bignumbers.html"&gt;Who can name the bigger number?&lt;/a&gt;
 &lt;span class="posts-list-item-description"&gt;
 &lt;svg class="icon icon-calendar" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"&gt;&lt;title&gt;calendar&lt;/title&gt;&lt;rect x="3" y="4" width="18" height="18" rx="2" ry="2"&gt;&lt;/rect&gt;&lt;line x1="16" y1="2" x2="16" y2="6"&gt;&lt;/line&gt;&lt;line x1="8" y1="2" x2="8" y2="6"&gt;&lt;/line&gt;&lt;line x1="3" y1="10" x2="21" y2="10"&gt;&lt;/line&gt;&lt;/svg&gt;
 2005, by Scott Aaronson
 &lt;/span&gt;
&lt;/li&gt;


&lt;li class="posts-list-item"&gt;
 &lt;a class="posts-list-item-title" href="https://archive.nytimes.com/www.nytimes.com/books/first/h/hoffman-man.html"&gt;The man who loved only numbers&lt;/a&gt;
 &lt;span class="posts-list-item-description"&gt;
 &lt;svg class="icon icon-calendar" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"&gt;&lt;title&gt;calendar&lt;/title&gt;&lt;rect x="3" y="4" width="18" height="18" rx="2" ry="2"&gt;&lt;/rect&gt;&lt;line x1="16" y1="2" x2="16" y2="6"&gt;&lt;/line&gt;&lt;line x1="8" y1="2" x2="8" y2="6"&gt;&lt;/line&gt;&lt;line x1="3" y1="10" x2="21" y2="10"&gt;&lt;/line&gt;&lt;/svg&gt;
 2018, by Paul Hoffman
 &lt;/span&gt;
&lt;/li&gt;


&lt;li class="posts-list-item"&gt;
 &lt;a class="posts-list-item-title" href="https://www.chiark.greenend.org.uk/~sgtatham/dice/"&gt;A pair of dice that never roll 7&lt;/a&gt;
 &lt;span class="posts-list-item-description"&gt;
 &lt;svg class="icon icon-calendar" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"&gt;&lt;title&gt;calendar&lt;/title&gt;&lt;rect x="3" y="4" width="18" height="18" rx="2" ry="2"&gt;&lt;/rect&gt;&lt;line x1="16" y1="2" x2="16" y2="6"&gt;&lt;/line&gt;&lt;line x1="8" y1="2" x2="8" y2="6"&gt;&lt;/line&gt;&lt;line x1="3" y1="10" x2="21" y2="10"&gt;&lt;/line&gt;&lt;/svg&gt;
 2004, by Simon Tatham
 &lt;/span&gt;
&lt;/li&gt;


&lt;li class="posts-list-item"&gt;
 &lt;a class="posts-list-item-title" href="https://swtch.com/~rsc/regexp/regexp1.html"&gt;Making regex faster with Thompson NFAs&lt;/a&gt;
 &lt;span class="posts-list-item-description"&gt;
 &lt;svg class="icon icon-calendar" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"&gt;&lt;title&gt;calendar&lt;/title&gt;&lt;rect x="3" y="4" width="18" height="18" rx="2" ry="2"&gt;&lt;/rect&gt;&lt;line x1="16" y1="2" x2="16" y2="6"&gt;&lt;/line&gt;&lt;line x1="8" y1="2" x2="8" y2="6"&gt;&lt;/line&gt;&lt;line x1="3" y1="10" x2="21" y2="10"&gt;&lt;/line&gt;&lt;/svg&gt;
 2007, by Russ Cox
 &lt;/span&gt;
&lt;/li&gt;


&lt;li class="posts-list-item"&gt;
 &lt;a class="posts-list-item-title" href="https://hex.ooo/library/alicebob.html"&gt;The Alice and Bob After Dinner Speech&lt;/a&gt;
 &lt;span class="posts-list-item-description"&gt;
 &lt;svg class="icon icon-calendar" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"&gt;&lt;title&gt;calendar&lt;/title&gt;&lt;rect x="3" y="4" width="18" height="18" rx="2" ry="2"&gt;&lt;/rect&gt;&lt;line x1="16" y1="2" x2="16" y2="6"&gt;&lt;/line&gt;&lt;line x1="8" y1="2" x2="8" y2="6"&gt;&lt;/line&gt;&lt;line x1="3" y1="10" x2="21" y2="10"&gt;&lt;/line&gt;&lt;/svg&gt;
 1984, by John Gordon
 &lt;/span&gt;
&lt;/li&gt;


&lt;li class="posts-list-item"&gt;
 &lt;a class="posts-list-item-title" href="https://web.archive.org/web/20250418102107/https://simonshine.dk/i8600.html"&gt;OpenBSD on the Dell Inspiron 8600&lt;/a&gt;
 &lt;span class="posts-list-item-description"&gt;
 &lt;svg class="icon icon-calendar" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"&gt;&lt;title&gt;calendar&lt;/title&gt;&lt;rect x="3" y="4" width="18" height="18" rx="2" ry="2"&gt;&lt;/rect&gt;&lt;line x1="16" y1="2" x2="16" y2="6"&gt;&lt;/line&gt;&lt;line x1="8" y1="2" x2="8" y2="6"&gt;&lt;/line&gt;&lt;line x1="3" y1="10" x2="21" y2="10"&gt;&lt;/line&gt;&lt;/svg&gt;
 2004
 &lt;/span&gt;
&lt;/li&gt;
&lt;/p&gt;</description></item></channel></rss>