<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" xmlns:source="https://source.scripting.com/" xml:lang="en"><title>Stefan VanBuren · TILs</title><link href="https://stefan.vanburen.xyz/til/" rel="alternate" type="text/html"/><link href="https://stefan.vanburen.xyz/til/index.xml" rel="self" type="application/atom+xml"/><id>https://stefan.vanburen.xyz/til/</id><updated>2026-08-25T20:18:04-04:00</updated><rights>© 2026 Stefan VanBuren</rights><icon>https://stefan.vanburen.xyz/icon-512.png</icon><generator uri="https://gohugo.io/">Hugo</generator><author><name>Stefan VanBuren</name><email>stefan@vanburen.xyz</email></author><entry><title type="html">&lt;code&gt;hugo --clock&lt;/code&gt;</title><link href="https://stefan.vanburen.xyz/til/hugo-clock/" rel="alternate" type="text/html"/><id>https://stefan.vanburen.xyz/til/hugo-clock/</id><published>2026-08-25T14:14:43-04:00</published><updated>2026-08-25T20:18:04-04:00</updated><content type="html">&lt;p&gt;I &lt;a href="/colophon"&gt;use &lt;code&gt;hugo&lt;/code&gt; as my SSG&lt;/a&gt;.&lt;/p&gt;&#10;&lt;p&gt;&lt;a href="https://gohugo.io/commands/hugo/"&gt;&lt;code&gt;hugo&lt;/code&gt;&lt;/a&gt; has a &lt;code&gt;--clock&lt;/code&gt; flag that lets you set the clock&#10;(using a starting &lt;a href="https://www.rfc-editor.org/info/rfc3339/#section-5.6"&gt;RFC 3339&lt;/a&gt; formatted timestamp)&#10;that the CLI uses for its commands.&#10;For example,&#10;I can run:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-console" data-lang="console"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gp"&gt;$&lt;/span&gt; hugo server --clock 2026-07-01T12:00:00-04:00&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;...&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt; Pages │ 71&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;...&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;Change detected, rebuilding site (#1).&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;2026-07-01 12:00:49.515 -0400&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;To start the server with the clock at noon on July 1st, 2026, EDT.&lt;/p&gt;&#10;&lt;p&gt;However!&#10;Using this flag will &lt;em&gt;exclude&lt;/em&gt; posts that are dated after that timestamp,&#10;so you&amp;rsquo;ll want to add &lt;code&gt;--buildFuture&lt;/code&gt; to avoid this:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-console" data-lang="console"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gp"&gt;$&lt;/span&gt; hugo server --clock 2026-07-01T12:00:00-04:00 --buildFuture&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;...&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt; Pages │ 74&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;...&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;Change detected, rebuilding site (#1).&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;2026-07-01 12:00:49.515 -0400&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I&amp;rsquo;m currently using this to test out the various &lt;a href="/blog/small-seasons"&gt;&amp;ldquo;seasons&amp;rdquo;&lt;/a&gt; on my site,&#10;and for testing the monthly &lt;a href="https://git.sr.ht/~svbn/stefan.vanburen.xyz/tree/8a5f75989b70562ce9708c0a1bebb9f44a1fbc94/item/layouts/home.html?view-source#L3-22"&gt;rotating emojis&lt;/a&gt; on my homepage.&lt;/p&gt;&#10;</content><source:markdown>&#10;I [use `hugo` as my SSG](/colophon).&#10;&#10;[`hugo`][hugo] has a `--clock` flag that lets you set the clock&#10;(using a starting [RFC 3339][rfc-3339] formatted timestamp)&#10;that the CLI uses for its commands.&#10;For example,&#10;I can run:&#10;&#10;```console&#10;$ hugo server --clock 2026-07-01T12:00:00-04:00&#10;...&#10; Pages │ 71&#10;...&#10;Change detected, rebuilding site (#1).&#10;2026-07-01 12:00:49.515 -0400&#10;```&#10;&#10;To start the server with the clock at noon on July 1st, 2026, EDT.&#10;&#10;However!&#10;Using this flag will _exclude_ posts that are dated after that timestamp,&#10;so you'll want to add `--buildFuture` to avoid this:&#10;&#10;```console&#10;$ hugo server --clock 2026-07-01T12:00:00-04:00 --buildFuture&#10;...&#10; Pages │ 74&#10;...&#10;Change detected, rebuilding site (#1).&#10;2026-07-01 12:00:49.515 -0400&#10;```&#10;&#10;I'm currently using this to test out the various ["seasons"](/blog/small-seasons) on my site,&#10;and for testing the monthly [rotating emojis][rotating-emojis] on my homepage.&#10;&#10;[hugo]: https://gohugo.io/commands/hugo/&#10;[rotating-emojis]: https://git.sr.ht/~svbn/stefan.vanburen.xyz/tree/8a5f75989b70562ce9708c0a1bebb9f44a1fbc94/item/layouts/home.html?view-source#L3-22&#10;[rfc-3339]: https://www.rfc-editor.org/info/rfc3339/#section-5.6&#10;</source:markdown></entry><entry><title type="html">Go &lt;code&gt;select&lt;/code&gt; without the coin flip</title><link href="https://stefan.vanburen.xyz/til/go-select-without-the-coin-flip/" rel="alternate" type="text/html"/><id>https://stefan.vanburen.xyz/til/go-select-without-the-coin-flip/</id><published>2026-07-15T18:49:26-04:00</published><updated>2026-07-16T15:34:01-04:00</updated><content type="html">&lt;p&gt;In Go, it&amp;rsquo;s common to wait on a condition via a channel while also respecting context cancellation.&#10;A common idiom looks like:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;select&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;case&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="nx"&gt;condition&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&#9;&lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;case&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Done&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&#9;&lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;An issue with this is that if you&amp;rsquo;d prefer that &lt;code&gt;condition&lt;/code&gt; (i.e., the non-error case) be treated with precedence, you&amp;rsquo;re racing with &lt;code&gt;ctx.Done()&lt;/code&gt;;&#10;&lt;a href="https://go.dev/tour/concurrency/5#:~:text=It%20chooses%20one%20at%20random%20if%20multiple%20are%20ready."&gt;&lt;code&gt;select&lt;/code&gt; chooses a &lt;code&gt;case&lt;/code&gt; at random when multiple cases are ready&lt;/a&gt;.&lt;/p&gt;&#10;&lt;p&gt;My coworker suggested a solution with a nested select within the &lt;code&gt;ctx.Done()&lt;/code&gt; case to re-check &lt;code&gt;condition&lt;/code&gt;:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;select&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;case&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="nx"&gt;condition&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&#9;&lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;case&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Done&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&#9;&lt;/span&gt;&lt;span class="c1"&gt;// Re-check condition in case we raced above.&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&#9;&lt;/span&gt;&lt;span class="k"&gt;select&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&#9;&lt;/span&gt;&lt;span class="k"&gt;case&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="nx"&gt;condition&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&#9;&#9;&lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&#9;&lt;/span&gt;&lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&#9;&#9;&lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&#9;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;But I don&amp;rsquo;t care for that nested select.&#10;Looking at &lt;a href="https://github.com/golang/go/issues/52637"&gt;this Go proposal&lt;/a&gt;,&#10;it looks like this is the best existing idiom,&#10;but I&amp;rsquo;d love to know if there&amp;rsquo;s a better way of solving this without the nested-&lt;code&gt;select&lt;/code&gt; smell.&lt;/p&gt;&#10;&lt;hr&gt;&#10;&lt;p&gt;P.S.:&#10;Vaguely related is the approach &lt;a href="https://github.com/bufbuild/buf/blob/efe167b22b3176a585992eb38a597dbc3c0c52ea/private/pkg/thread/thread.go#L86-L100"&gt;bufbuild/buf&amp;rsquo;s &lt;code&gt;thread.Parallelize&lt;/code&gt; takes&lt;/a&gt;,&#10;with a similar nested &lt;code&gt;select&lt;/code&gt;.&#10;Related PR: &lt;a href="https://github.com/bufbuild/buf/pull/913"&gt;bufbuild/buf#913&lt;/a&gt;.&lt;/p&gt;&#10;</content><source:markdown>&#10;In Go, it's common to wait on a condition via a channel while also respecting context cancellation.&#10;A common idiom looks like:&#10;&#10;```go&#10;select {&#10;case &lt;-condition:&#10;&#9;return nil&#10;case &lt;-ctx.Done():&#10;&#9;return ctx.Err()&#10;}&#10;```&#10;&#10;An issue with this is that if you'd prefer that `condition` (i.e., the non-error case) be treated with precedence, you're racing with `ctx.Done()`;&#10;[`select` chooses a `case` at random when multiple cases are ready][1].&#10;&#10;My coworker suggested a solution with a nested select within the `ctx.Done()` case to re-check `condition`:&#10;&#10;```go&#10;select {&#10;case &lt;-condition:&#10;&#9;return nil&#10;case &lt;-ctx.Done():&#10;&#9;// Re-check condition in case we raced above.&#10;&#9;select {&#10;&#9;case &lt;-condition:&#10;&#9;&#9;return nil&#10;&#9;default:&#10;&#9;&#9;return ctx.Err()&#10;&#9;}&#10;}&#10;```&#10;&#10;But I don't care for that nested select.&#10;Looking at [this Go proposal](https://github.com/golang/go/issues/52637),&#10;it looks like this is the best existing idiom,&#10;but I'd love to know if there's a better way of solving this without the nested-`select` smell.&#10;&#10;* * *&#10;&#10;P.S.:&#10;Vaguely related is the approach [bufbuild/buf's `thread.Parallelize` takes][2],&#10;with a similar nested `select`.&#10;Related PR: [bufbuild/buf#913](https://github.com/bufbuild/buf/pull/913).&#10;&#10;[1]: https://go.dev/tour/concurrency/5#:~:text=It%20chooses%20one%20at%20random%20if%20multiple%20are%20ready.&#10;[2]: https://github.com/bufbuild/buf/blob/efe167b22b3176a585992eb38a597dbc3c0c52ea/private/pkg/thread/thread.go#L86-L100&#10;</source:markdown></entry><entry><title type="html">Sargable</title><link href="https://stefan.vanburen.xyz/til/sargable/" rel="alternate" type="text/html"/><id>https://stefan.vanburen.xyz/til/sargable/</id><published>2026-06-02T08:32:10-04:00</published><updated>2026-06-02T09:11:22-04:00</updated><content type="html">&lt;p&gt;TIL of &lt;a href="https://en.wikipedia.org/wiki/Sargable"&gt;&amp;ldquo;sargable&amp;rdquo;&lt;/a&gt; conditions in SQL queries,&#10;which just means that an index can be used to speed up the query.&#10;One of those &amp;ldquo;you already know the concept, but the &lt;em&gt;handle&lt;/em&gt; is useful&amp;rdquo; sort of things.&lt;/p&gt;&#10;</content><source:markdown>&#10;TIL of ["sargable"][1] conditions in SQL queries,&#10;which just means that an index can be used to speed up the query.&#10;One of those "you already know the concept, but the _handle_ is useful" sort of things.&#10;&#10;[1]: https://en.wikipedia.org/wiki/Sargable&#10;</source:markdown></entry><entry><title type="html">Tangenting</title><link href="https://stefan.vanburen.xyz/til/tangenting/" rel="alternate" type="text/html"/><id>https://stefan.vanburen.xyz/til/tangenting/</id><published>2026-04-26T20:03:30-04:00</published><updated>2026-08-26T09:10:32-04:00</updated><content type="html">&lt;p&gt;&lt;a href="/til/tk"&gt;Again&lt;/a&gt;,&#10;not something I learned &lt;em&gt;today&lt;/em&gt;,&#10;but within the last few weeks:&#10;&amp;ldquo;tangenting&amp;rdquo;.&#10;I read about this in the wonderful Looking At Picture Books Substack,&#10;&lt;a href="https://lookingatpicturebooks.com/p/goodnight-moon"&gt;discussing&lt;/a&gt; one of my (and Wilhelmina&amp;rsquo;s) favorites, &lt;cite&gt;&lt;a href="https://en.wikipedia.org/wiki/Goodnight_Moon"&gt;Goodnight Moon&lt;/a&gt;&lt;/cite&gt;:&lt;/p&gt;&#10;&lt;blockquote&gt;&lt;p&gt;Tangenting is something you get yelled at for when you’re in art/design school. The theory is that the edges of things shouldn’t touch closely. You should instead look for large overlaps, so the different objects or elements are clearly divided — if they’re too close it feels badly planned, and harder to tell where one thing ends and the other begins, so they say.&#10;Clement Hurd&amp;hellip; was SUPER into tangenting.&lt;/p&gt;&#10;&lt;/blockquote&gt;&lt;p&gt;The whole thing is worth a read, if only for the tangenting discussion.&lt;/p&gt;&#10;&lt;hr&gt;&#10;&lt;p&gt;Also related: &lt;a href="https://lookingatpicturebooks.com/p/we-have-a-question-about-the-goodnight"&gt;stamps&lt;/a&gt;.&lt;/p&gt;&#10;</content><source:markdown>&#10;[Again](/til/tk),&#10;not something I learned _today_,&#10;but within the last few weeks:&#10;"tangenting".&#10;I read about this in the wonderful Looking At Picture Books Substack,&#10;[discussing][1] one of my (and Wilhelmina's) favorites, &lt;cite&gt;[Goodnight Moon](https://en.wikipedia.org/wiki/Goodnight_Moon)&lt;/cite&gt;:&#10;&#10;&gt; Tangenting is something you get yelled at for when you’re in art/design school. The theory is that the edges of things shouldn’t touch closely. You should instead look for large overlaps, so the different objects or elements are clearly divided — if they’re too close it feels badly planned, and harder to tell where one thing ends and the other begins, so they say.&#10;&gt; Clement Hurd... was SUPER into tangenting.&#10;&#10;The whole thing is worth a read, if only for the tangenting discussion.&#10;&#10;* * *&#10;&#10;Also related: [stamps][2].&#10;&#10;[1]: https://lookingatpicturebooks.com/p/goodnight-moon&#10;[2]: https://lookingatpicturebooks.com/p/we-have-a-question-about-the-goodnight&#10;</source:markdown></entry><entry><title type="html">TK</title><link href="https://stefan.vanburen.xyz/til/tk/" rel="alternate" type="text/html"/><id>https://stefan.vanburen.xyz/til/tk/</id><published>2026-04-25T19:43:17-04:00</published><updated>2026-08-26T09:10:32-04:00</updated><content type="html">&lt;p&gt;TK means &lt;a href="https://en.wikipedia.org/wiki/To_come_(publishing)"&gt;&amp;ldquo;to come&amp;rdquo;&lt;/a&gt;.&#10;You&amp;rsquo;ll typically see it as &amp;ldquo;TKTK&amp;rdquo; or &amp;ldquo;TKTKTK&amp;rdquo;,&#10;as a simple way to search (i.e., &lt;kbd&gt;&lt;a href="https://en.wikipedia.org/wiki/Command_key"&gt;⌘&lt;/a&gt;&lt;/kbd&gt;+&lt;kbd&gt;f&lt;/kbd&gt;) for details that haven&amp;rsquo;t been written yet.&#10;I use it when drafting blog posts to skip over a sentence or section I want to come back to write later,&#10;to avoid losing momentum.&lt;/p&gt;&#10;&lt;p&gt;I didn&amp;rsquo;t learn this today,&#10;but within the last year or two (not too sure where!);&#10;I was just reminded by seeing it in print&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;sup id="fnref:2"&gt;&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref"&gt;2&lt;/a&gt;&lt;/sup&gt; in &lt;a href="https://en.wikipedia.org/wiki/Merrimack_Valley_Library_Consortium"&gt;my library&lt;/a&gt;&amp;rsquo;s edition of &lt;a href="https://www.jezburrows.com/"&gt;Jez Burrow&amp;rsquo;s&lt;/a&gt; &lt;a href="http://www.dictionarystories.com"&gt;Dictionary Stories&lt;/a&gt;,&#10;which is otherwise lovely so far.&lt;/p&gt;&#10;&lt;div class="footnotes" role="doc-endnotes"&gt;&#10;&lt;hr&gt;&#10;&lt;ol&gt;&#10;&lt;li id="fn:1"&gt;&#10;&lt;p&gt;To be fair, I get it:&#10;it&amp;rsquo;s in a footnote where the &amp;ldquo;TK&amp;rdquo; is taking the place of a page number,&#10;which I imagine is one of the last things to be solidified in a print book after all of the typesetting has taken place.&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;&#10;&lt;/li&gt;&#10;&lt;li id="fn:2"&gt;&#10;&lt;p&gt;Funny timing, but a post on Daring Fireball the day after this one &lt;a href="https://daringfireball.net/2026/04/nyt_wrong_crossword_grid"&gt;mentioned a similar snafu of printing &amp;ldquo;Headline Goes Here&amp;rdquo;&lt;/a&gt; &amp;ndash; &amp;ldquo;TK&amp;rdquo; may have been useful there!&amp;#160;&lt;a href="#fnref:2" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;&#10;&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;/div&gt;&#10;</content><source:markdown>&#10;TK means ["to come"](https://en.wikipedia.org/wiki/To_come_(publishing)).&#10;You'll typically see it as "TKTK" or "TKTKTK",&#10;as a simple way to search (i.e., &lt;kbd&gt;[⌘](https://en.wikipedia.org/wiki/Command_key)&lt;/kbd&gt;+&lt;kbd&gt;f&lt;/kbd&gt;) for details that haven't been written yet.&#10;I use it when drafting blog posts to skip over a sentence or section I want to come back to write later,&#10;to avoid losing momentum.&#10;&#10;I didn't learn this today,&#10;but within the last year or two (not too sure where!);&#10;I was just reminded by seeing it in print[^1][^2] in [my library][3]'s edition of [Jez Burrow's][1] [Dictionary Stories][2],&#10;which is otherwise lovely so far.&#10;&#10;[^1]: To be fair, I get it:&#10;it's in a footnote where the "TK" is taking the place of a page number,&#10;which I imagine is one of the last things to be solidified in a print book after all of the typesetting has taken place.&#10;&#10;[^2]: Funny timing, but a post on Daring Fireball the day after this one [mentioned a similar snafu of printing "Headline Goes Here"][4] -- "TK" may have been useful there!&#10;&#10;[1]: https://www.jezburrows.com/&#10;[2]: http://www.dictionarystories.com&#10;[3]: https://en.wikipedia.org/wiki/Merrimack_Valley_Library_Consortium&#10;[4]: https://daringfireball.net/2026/04/nyt_wrong_crossword_grid&#10;</source:markdown></entry><entry><title type="html">Neovim &lt;code&gt;:Inspect&lt;/code&gt;</title><link href="https://stefan.vanburen.xyz/til/neovim-inspect/" rel="alternate" type="text/html"/><id>https://stefan.vanburen.xyz/til/neovim-inspect/</id><published>2026-01-10T16:26:47-05:00</published><updated>2026-05-11T20:01:13-04:00</updated><content type="html">&lt;p&gt;Yesterday, I was working on &lt;a href="https://github.com/bufbuild/buf/pull/4270"&gt;improving &lt;code&gt;buf&lt;/code&gt;&amp;rsquo;s lsp protobuf semantic tokens&lt;/a&gt;,&#10;and needed to test out how my changes were working in a real &lt;abbr title="Language Server Protocol"&gt;LSP&lt;/abbr&gt; client.&lt;/p&gt;&#10;&lt;p&gt;I knew neovim had a couple of helper functions for inspecting the current position;&#10;first I tried:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-vim" data-lang="vim"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nx"&gt;lua&lt;/span&gt; &lt;span class="nx"&gt;vim&lt;/span&gt;.&lt;span class="nx"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;vim&lt;/span&gt;.&lt;span class="nx"&gt;inspect_pos&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I figured there must be a function more specifically for semantic tokens:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-vim" data-lang="vim"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nx"&gt;lua&lt;/span&gt; &lt;span class="nx"&gt;vim&lt;/span&gt;.&lt;span class="nx"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;vim&lt;/span&gt;.&lt;span class="nx"&gt;lsp&lt;/span&gt;.&lt;span class="nx"&gt;semantic_tokens&lt;/span&gt;.&lt;span class="nx"&gt;get_at_pos&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;But finally I landed on:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-vim" data-lang="vim"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nx"&gt;Inspect&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;(&lt;a href="https://neovim.io/doc/user/lua.html#%3AInspect%21"&gt;neovim docs&lt;/a&gt;)&lt;/p&gt;&#10;&lt;p&gt;which prints both treesitter and semantic tokens,&#10;but in a much cleaner way than the two lua functions.&lt;/p&gt;&#10;&lt;p&gt;I&amp;rsquo;ve &lt;a href="https://github.com/stefanvanburen/dotfiles/commit/7dadb128015d37030f7b3b55b1e849b2873e499b"&gt;keymapped the underlying &lt;code&gt;vim.show_pos()&lt;/code&gt; function to &lt;code&gt;&amp;lt;leader&amp;gt;i&lt;/code&gt; in neovim&lt;/a&gt; for easier access.&lt;/p&gt;&#10;</content><source:markdown>&#10;Yesterday, I was working on [improving `buf`'s lsp protobuf semantic tokens][1],&#10;and needed to test out how my changes were working in a real &lt;abbr title="Language Server Protocol"&gt;LSP&lt;/abbr&gt; client.&#10;&#10;I knew neovim had a couple of helper functions for inspecting the current position;&#10;first I tried:&#10;&#10;```vim&#10;:lua vim.print(vim.inspect_pos())&#10;```&#10;&#10;I figured there must be a function more specifically for semantic tokens:&#10;&#10;```vim&#10;:lua vim.print(vim.lsp.semantic_tokens.get_at_pos())&#10;```&#10;&#10;But finally I landed on:&#10;&#10;```vim&#10;:Inspect&#10;```&#10;&#10;([neovim docs](https://neovim.io/doc/user/lua.html#%3AInspect%21))&#10;&#10;which prints both treesitter and semantic tokens,&#10;but in a much cleaner way than the two lua functions.&#10;&#10;I've [keymapped the underlying `vim.show_pos()` function to `&lt;leader&gt;i` in neovim][2] for easier access.&#10;&#10;[1]: https://github.com/bufbuild/buf/pull/4270&#10;[2]: https://github.com/stefanvanburen/dotfiles/commit/7dadb128015d37030f7b3b55b1e849b2873e499b&#10;</source:markdown></entry><entry><title type="html">Neovim &lt;code&gt;:InspectTree&lt;/code&gt; Tree-sitter</title><link href="https://stefan.vanburen.xyz/til/nvim-inspect-tree-sitter/" rel="alternate" type="text/html"/><id>https://stefan.vanburen.xyz/til/nvim-inspect-tree-sitter/</id><published>2025-09-10T19:50:36-04:00</published><updated>2026-05-11T20:01:13-04:00</updated><content type="html">&lt;p&gt;I had assumed that to interact with Tree-sitter in Neovim,&#10;you still needed to use the utilities from &lt;a href="https://github.com/nvim-treesitter/nvim-treesitter"&gt;nvim-treesitter&lt;/a&gt;.&#10;Apparently that&amp;rsquo;s not the case &amp;mdash;&#10;Neovim has had a builtin &lt;a href="https://neovim.io/doc/user/treesitter.html#%3AInspectTree"&gt;&lt;code&gt;:InspectTree&lt;/code&gt;&lt;/a&gt; command that brings up the &lt;abbr title="Abstract Syntax Tree"&gt;AST&lt;/abbr&gt; from Tree-sitter in a split window,&#10;which is &lt;a href="https://neovim.io/doc/user/options.html#'scrollbind'"&gt;&lt;code&gt;scrollbound&lt;/code&gt;&lt;/a&gt; to the original window and highlights the selected node in both windows.&lt;/p&gt;&#10;&lt;p&gt;It also has a &lt;a href="https://gpanders.com/blog/whats-new-in-neovim-0.10/#tree-sitter-query-editor"&gt;query editor&lt;/a&gt;,&#10;which I haven&amp;rsquo;t yet had the need for,&#10;but it&amp;rsquo;s good to know it exists.&lt;/p&gt;&#10;</content><source:markdown>&#10;I had assumed that to interact with Tree-sitter in Neovim,&#10;you still needed to use the utilities from [nvim-treesitter][1].&#10;Apparently that's not the case ---&#10;Neovim has had a builtin [`:InspectTree`][2] command that brings up the &lt;abbr title="Abstract Syntax Tree"&gt;AST&lt;/abbr&gt; from Tree-sitter in a split window,&#10;which is [`scrollbound`][3] to the original window and highlights the selected node in both windows.&#10;&#10;It also has a [query editor][4],&#10;which I haven't yet had the need for,&#10;but it's good to know it exists.&#10;&#10;[1]: https://github.com/nvim-treesitter/nvim-treesitter&#10;[2]: https://neovim.io/doc/user/treesitter.html#%3AInspectTree&#10;[3]: https://neovim.io/doc/user/options.html#'scrollbind'&#10;[4]: https://gpanders.com/blog/whats-new-in-neovim-0.10/#tree-sitter-query-editor&#10;</source:markdown></entry><entry><title type="html">WikiGnome</title><link href="https://stefan.vanburen.xyz/til/wiki-gnome/" rel="alternate" type="text/html"/><id>https://stefan.vanburen.xyz/til/wiki-gnome/</id><published>2025-09-02T09:10:30-04:00</published><updated>2026-04-16T19:45:21-04:00</updated><content type="html">&lt;p&gt;TIL about &lt;a href="https://en.m.wikipedia.org/wiki/Wikipedia:WikiGnome"&gt;WikiGnomes&lt;/a&gt;:&lt;/p&gt;&#10;&lt;blockquote&gt;&lt;p&gt;A WikiGnome is a wiki user who makes useful incremental edits without clamoring for attention.&#10;WikiGnomes work behind the scenes of a wiki, tying up little loose ends and making things run more smoothly.&lt;/p&gt;&#10;&lt;/blockquote&gt;&lt;p&gt;Some of the most valuable work can be the tiny changes that leave things in a better place than they were found.&lt;/p&gt;&#10;&lt;p&gt;Also, sounds a little like &lt;a href="/blog/prutsen"&gt;prutsen&lt;/a&gt;.&lt;/p&gt;&#10;</content><source:markdown>&#10;TIL about [WikiGnomes][1]:&#10;&#10;&gt; A WikiGnome is a wiki user who makes useful incremental edits without clamoring for attention.&#10;&gt; WikiGnomes work behind the scenes of a wiki, tying up little loose ends and making things run more smoothly.&#10;&#10;Some of the most valuable work can be the tiny changes that leave things in a better place than they were found.&#10;&#10;Also, sounds a little like [prutsen](/blog/prutsen).&#10;&#10;[1]: https://en.m.wikipedia.org/wiki/Wikipedia:WikiGnome&#10;</source:markdown></entry><entry><title type="html">Wrapping arrays in &lt;code&gt;jq&lt;/code&gt;</title><link href="https://stefan.vanburen.xyz/til/jq-wrap-array/" rel="alternate" type="text/html"/><id>https://stefan.vanburen.xyz/til/jq-wrap-array/</id><published>2025-08-20T08:55:08-04:00</published><updated>2026-05-11T20:01:13-04:00</updated><content type="html">&lt;p&gt;Riffing on &lt;a href="https://macwright.com/2025/08/19/using-super"&gt;Tom MacWright&amp;rsquo;s &amp;ldquo;Using super&amp;rdquo; post&lt;/a&gt; from yesterday,&#10;I saw the post and immediately thought of using &lt;a href="https://jqlang.org"&gt;&lt;code&gt;jq&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;&#10;&lt;p&gt;I thought I might get what he was looking for with my initial attempt, something like:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-console" data-lang="console"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gp"&gt;$&lt;/span&gt; pbpaste &lt;span class="p"&gt;|&lt;/span&gt; jq &lt;span class="s1"&gt;&amp;#39;.[].name&amp;#39;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;&amp;#34;ALGOLIA_API_KEY&amp;#34;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;&amp;#34;AMAZON_AWS_ACCESS_KEY_ID&amp;#34;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&amp;hellip; but that left out the wrapper array.&#10;I figured I might need some other &lt;code&gt;jq&lt;/code&gt; function or pipe,&#10;but instead I found that you can just wrap the entire expression in square brackets to wrap the result:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-console" data-lang="console"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gp"&gt;$&lt;/span&gt; pbpaste &lt;span class="p"&gt;|&lt;/span&gt; jq &lt;span class="s1"&gt;&amp;#39;[.[].name]&amp;#39;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;&amp;#34;ALGOLIA_API_KEY&amp;#34;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;&amp;#34;AMAZON_AWS_ACCESS_KEY_ID&amp;#34;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Regardless, &lt;code&gt;jq&lt;/code&gt; feels worth the investment,&#10;especially given &lt;a href="https://github.com/fiatjaf/awesome-jq"&gt;the community (and alternative implementations)&lt;/a&gt; built up around it.&lt;/p&gt;&#10;</content><source:markdown>&#10;Riffing on [Tom MacWright's "Using super" post][1] from yesterday,&#10;I saw the post and immediately thought of using [`jq`][2].&#10;&#10;I thought I might get what he was looking for with my initial attempt, something like:&#10;&#10;```console&#10;$ pbpaste | jq '.[].name'&#10;"ALGOLIA_API_KEY"&#10;"AMAZON_AWS_ACCESS_KEY_ID"&#10;```&#10;&#10;... but that left out the wrapper array.&#10;I figured I might need some other `jq` function or pipe,&#10;but instead I found that you can just wrap the entire expression in square brackets to wrap the result:&#10;&#10;```console&#10;$ pbpaste | jq '[.[].name]'&#10;"ALGOLIA_API_KEY"&#10;"AMAZON_AWS_ACCESS_KEY_ID"&#10;```&#10;&#10;Regardless, `jq` feels worth the investment,&#10;especially given [the community (and alternative implementations)][3] built up around it.&#10;&#10;[1]: https://macwright.com/2025/08/19/using-super&#10;[2]: https://jqlang.org&#10;[3]: https://github.com/fiatjaf/awesome-jq&#10;</source:markdown></entry><entry><title type="html">Neovim LSP Defaults</title><link href="https://stefan.vanburen.xyz/til/neovim-lsp-defaults/" rel="alternate" type="text/html"/><id>https://stefan.vanburen.xyz/til/neovim-lsp-defaults/</id><published>2025-07-01T16:33:55-04:00</published><updated>2026-04-23T06:44:40-04:00</updated><content type="html">&lt;p&gt;neovim released v0.11.0 a little while back,&#10;and in the interest of using the defaults,&#10;I &lt;a href="https://github.com/stefanvanburen/dotfiles/commit/8f8ad882a1092c8c032b13e118efc960317759f4#diff-568ff9545a264b9c3d0daec0395b05bf4217c6448328e6f2e70b1ad55adcbd4eL533-R534"&gt;removed some of my &lt;abbr title="Language Server Protocol"&gt;LSP&lt;/abbr&gt;-related keymaps&lt;/a&gt; that are now &lt;a href="https://gpanders.com/blog/whats-new-in-neovim-0-11/#defaults"&gt;automatically mapped&lt;/a&gt;.&#10;These are listed at &lt;a href="https://neovim.io/doc/user/lsp.html#_defaults"&gt;&lt;code&gt;:help lsp-defaults&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;&#10;</content><source:markdown>&#10;neovim released v0.11.0 a little while back,&#10;and in the interest of using the defaults,&#10;I [removed some of my &lt;abbr title="Language Server Protocol"&gt;LSP&lt;/abbr&gt;-related keymaps][1] that are now [automatically mapped][2].&#10;These are listed at [`:help lsp-defaults`][3].&#10;&#10;[1]: https://github.com/stefanvanburen/dotfiles/commit/8f8ad882a1092c8c032b13e118efc960317759f4#diff-568ff9545a264b9c3d0daec0395b05bf4217c6448328e6f2e70b1ad55adcbd4eL533-R534&#10;[2]: https://gpanders.com/blog/whats-new-in-neovim-0-11/#defaults&#10;[3]: https://neovim.io/doc/user/lsp.html#_defaults&#10;</source:markdown></entry><entry><title type="html">Postgres &lt;code&gt;citext&lt;/code&gt; type</title><link href="https://stefan.vanburen.xyz/til/postgres-citext/" rel="alternate" type="text/html"/><id>https://stefan.vanburen.xyz/til/postgres-citext/</id><published>2024-08-21T07:49:04-04:00</published><updated>2026-05-11T20:01:13-04:00</updated><content type="html">&lt;p&gt;TIL about the Postgres &lt;code&gt;citext&lt;/code&gt; type (that is, &lt;a href="https://www.postgresql.org/docs/current/citext.html"&gt;case-insensitive text type&lt;/a&gt;),&#10;from reading &lt;a href="https://github.com/surprisetalk/ding/blob/main/db.sql#L18-L19"&gt;this schema&lt;/a&gt;.&lt;/p&gt;&#10;&lt;p&gt;Based on this tip from the Postgres docs,&#10;this might not be the right type for general text:&lt;/p&gt;&#10;&lt;blockquote&gt;&lt;p&gt;Consider using nondeterministic collations (see &lt;a href="https://www.postgresql.org/docs/current/collation.html#COLLATION-NONDETERMINISTIC"&gt;Section 24.2.2.4&lt;/a&gt;) instead of this module.&#10;They can be used for case-insensitive comparisons,&#10;accent-insensitive comparisons,&#10;and other combinations,&#10;and they handle more Unicode special cases correctly.&lt;/p&gt;&#10;&lt;/blockquote&gt;&lt;p&gt;&amp;hellip; but if you&amp;rsquo;re storing something like a username or email address that&amp;rsquo;s already limited to an ascii-ish character set,&#10;and want to keep things unique on a case-insensitive basis,&#10;it might be what you&amp;rsquo;re looking for.&lt;/p&gt;&#10;</content><source:markdown>&#10;TIL about the Postgres `citext` type (that is, [case-insensitive text type][1]),&#10;from reading [this schema][2].&#10;&#10;Based on this tip from the Postgres docs,&#10;this might not be the right type for general text:&#10;&#10;&gt; Consider using nondeterministic collations (see [Section 24.2.2.4][3]) instead of this module.&#10;&gt; They can be used for case-insensitive comparisons,&#10;&gt; accent-insensitive comparisons,&#10;&gt; and other combinations,&#10;&gt; and they handle more Unicode special cases correctly.&#10;&#10;... but if you're storing something like a username or email address that's already limited to an ascii-ish character set,&#10;and want to keep things unique on a case-insensitive basis,&#10;it might be what you're looking for.&#10;&#10;[1]: https://www.postgresql.org/docs/current/citext.html&#10;[2]: https://github.com/surprisetalk/ding/blob/main/db.sql#L18-L19&#10;[3]: https://www.postgresql.org/docs/current/collation.html#COLLATION-NONDETERMINISTIC&#10;</source:markdown></entry><entry><title type="html">Go Pass by Value or Pointer</title><link href="https://stefan.vanburen.xyz/til/go-passing/" rel="alternate" type="text/html"/><id>https://stefan.vanburen.xyz/til/go-passing/</id><published>2024-08-18T19:09:03-04:00</published><updated>2024-08-18T19:14:46-04:00</updated><content type="html">&lt;p&gt;I&amp;rsquo;ve long struggled with the choice of how to pass around Go values,&#10;specifically structs.&#10;I was happy to find &lt;a href="https://blog.percywegmann.com/2023/09/20/go-pass-by-value-or-pointer.html"&gt;this article&lt;/a&gt; on the topic,&#10;specifically the conclusion,&#10;reproduced here:&lt;/p&gt;&#10;&lt;blockquote&gt;&lt;ol&gt;&#10;&lt;li&gt;Types that are not structs or arrays should be passed by value.&lt;/li&gt;&#10;&lt;li&gt;Struct types that don’t export their members and are clearly built as immutable value types, like time.Time, should be passed by value. Note that these types are relatively rare, and are even rarer to be defined by you.&lt;/li&gt;&#10;&lt;li&gt;Arrays and all other struct types should be passed by pointer, whether small, large, stateful, or whatever.&lt;/li&gt;&#10;&lt;li&gt;If you’re passing data that could be mutated by a concurrent process and its important to you for that data not to be mutated, explicitly make a copy of it before passing it along. Be aware that you can’t just rely on passing the data by value since that does not create a deep copy.&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;/blockquote&gt;&lt;p&gt;I&amp;rsquo;ll be referencing this moving forward,&#10;and taking the guesswork out of my passing conventions.&lt;/p&gt;&#10;</content><source:markdown>&#10;I've long struggled with the choice of how to pass around Go values,&#10;specifically structs.&#10;I was happy to find [this article][1] on the topic,&#10;specifically the conclusion,&#10;reproduced here:&#10;&#10;&gt; 1. Types that are not structs or arrays should be passed by value.&#10;&gt; 1. Struct types that don’t export their members and are clearly built as immutable value types, like time.Time, should be passed by value. Note that these types are relatively rare, and are even rarer to be defined by you.&#10;&gt; 1. Arrays and all other struct types should be passed by pointer, whether small, large, stateful, or whatever.&#10;&gt; 1. If you’re passing data that could be mutated by a concurrent process and its important to you for that data not to be mutated, explicitly make a copy of it before passing it along. Be aware that you can’t just rely on passing the data by value since that does not create a deep copy.&#10;&#10;I'll be referencing this moving forward,&#10;and taking the guesswork out of my passing conventions.&#10;&#10;[1]: https://blog.percywegmann.com/2023/09/20/go-pass-by-value-or-pointer.html&#10;</source:markdown></entry><entry><title type="html">HTML datetime duration</title><link href="https://stefan.vanburen.xyz/til/html-date-duration/" rel="alternate" type="text/html"/><id>https://stefan.vanburen.xyz/til/html-date-duration/</id><published>2024-07-13T10:18:24-04:00</published><updated>2026-08-26T09:10:32-04:00</updated><content type="html">&lt;p&gt;Last week I learned that an &lt;abbr title="HyperText Markup Language"&gt;HTML&lt;/abbr&gt; &lt;code&gt;datetime&lt;/code&gt; attribute can &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/time#valid_datetime_values"&gt;represent a duration&lt;/a&gt;.&lt;/p&gt;&#10;&lt;p&gt;For example:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-html" data-lang="html"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;time&lt;/span&gt; &lt;span class="na"&gt;datetime&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;PT4H18M3S&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; 4 hours, 18 minutes and 3 seconds&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;time&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The syntax is an &lt;a href="https://en.wikipedia.org/wiki/ISO_8601#Durations"&gt;&lt;abbr title="International Organization for Standardization"&gt;ISO&lt;/abbr&gt; 8601 Duration&lt;/a&gt;.&lt;/p&gt;&#10;</content><source:markdown>&#10;Last week I learned that an &lt;abbr title="HyperText Markup Language"&gt;HTML&lt;/abbr&gt; `datetime` attribute can [represent a duration][valid-datetime-values].&#10;&#10;For example:&#10;&#10;```html&#10;&lt;time datetime="PT4H18M3S"&gt;&#10; 4 hours, 18 minutes and 3 seconds&#10;&lt;/time&gt;&#10;```&#10;&#10;The syntax is an [&lt;abbr title="International Organization for Standardization"&gt;ISO&lt;/abbr&gt; 8601 Duration](https://en.wikipedia.org/wiki/ISO_8601#Durations).&#10;&#10;[valid-datetime-values]: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/time#valid_datetime_values&#10;</source:markdown></entry><entry><title type="html">GBrowse to another branch with Fugitive.vim</title><link href="https://stefan.vanburen.xyz/til/fugitive-objects/" rel="alternate" type="text/html"/><id>https://stefan.vanburen.xyz/til/fugitive-objects/</id><published>2024-07-11T08:28:24-04:00</published><updated>2026-04-23T06:44:40-04:00</updated><content type="html">&lt;p&gt;Last week, I watched a coworker bring up a file on a different branch using fugitive.vim&amp;rsquo;s &lt;a href="https://github.com/tpope/vim-fugitive/blob/8c8cdf4405cb8bdb70dd9812a33bb52363a87dbc/doc/fugitive.txt#L254"&gt;:GBrowse&lt;/a&gt; and I was floored:&#10;almost daily I find myself using it to open the &lt;abbr title="Uniform Resource Locator"&gt;URL&lt;/abbr&gt; to a file to share (or a few lines).&lt;/p&gt;&#10;&lt;p&gt;However, much of the time I&amp;rsquo;m working on a different branch than the one I want to share,&#10;and navigating to the same file on a different branch in the GitHub &lt;abbr title="User Interface"&gt;UI&lt;/abbr&gt; takes too many steps.&#10;First, you realize you&amp;rsquo;re on the wrong branch,&#10;then you change branches,&#10;then you have to navigate back to the file again.&lt;/p&gt;&#10;&lt;p&gt;I had always been vaguely aware of &lt;a href="https://github.com/tpope/vim-fugitive/blob/8c8cdf4405cb8bdb70dd9812a33bb52363a87dbc/doc/fugitive.txt#L611"&gt;&lt;code&gt;:h fugitive-object&lt;/code&gt;&lt;/a&gt;,&#10;but had never put them to use.&lt;/p&gt;&#10;&lt;p&gt;The object you&amp;rsquo;re looking for is &lt;code&gt;&amp;lt;branch&amp;gt;:%&lt;/code&gt;, meaning:&#10;bring up this file (&lt;code&gt;%&lt;/code&gt;) on branch &lt;code&gt;&amp;lt;branch&amp;gt;&lt;/code&gt;.&lt;/p&gt;&#10;&lt;p&gt;For example:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-vim" data-lang="vim"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nx"&gt;GBrowse&lt;/span&gt; &lt;span class="nx"&gt;main&lt;/span&gt;:%&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And of course, this just works with all of the &lt;a href="https://github.com/tpope/vim-fugitive/blob/8c8cdf4405cb8bdb70dd9812a33bb52363a87dbc/doc/fugitive.txt#L264-L277"&gt;&lt;code&gt;:GBrowse&lt;/code&gt; variants&lt;/a&gt;.&lt;/p&gt;&#10;</content><source:markdown>&#10;Last week, I watched a coworker bring up a file on a different branch using fugitive.vim's [:GBrowse][GBrowse] and I was floored:&#10;almost daily I find myself using it to open the &lt;abbr title="Uniform Resource Locator"&gt;URL&lt;/abbr&gt; to a file to share (or a few lines).&#10;&#10;However, much of the time I'm working on a different branch than the one I want to share,&#10;and navigating to the same file on a different branch in the GitHub &lt;abbr title="User Interface"&gt;UI&lt;/abbr&gt; takes too many steps.&#10;First, you realize you're on the wrong branch,&#10;then you change branches,&#10;then you have to navigate back to the file again.&#10;&#10;I had always been vaguely aware of [`:h fugitive-object`][fugitive-object],&#10;but had never put them to use.&#10;&#10;The object you're looking for is `&lt;branch&gt;:%`, meaning:&#10;bring up this file (`%`) on branch `&lt;branch&gt;`.&#10;&#10;For example:&#10;&#10;```vim&#10;:GBrowse main:%&#10;```&#10;&#10;And of course, this just works with all of the [`:GBrowse` variants][GBrowse-variants].&#10;&#10;[GBrowse]: https://github.com/tpope/vim-fugitive/blob/8c8cdf4405cb8bdb70dd9812a33bb52363a87dbc/doc/fugitive.txt#L254&#10;[fugitive-object]: https://github.com/tpope/vim-fugitive/blob/8c8cdf4405cb8bdb70dd9812a33bb52363a87dbc/doc/fugitive.txt#L611&#10;[GBrowse-variants]: https://github.com/tpope/vim-fugitive/blob/8c8cdf4405cb8bdb70dd9812a33bb52363a87dbc/doc/fugitive.txt#L264-L277&#10;</source:markdown></entry><entry><title type="html">Neovim nfnl Directory Configuration</title><link href="https://stefan.vanburen.xyz/til/nvim-nfnl-directory-configuration/" rel="alternate" type="text/html"/><id>https://stefan.vanburen.xyz/til/nvim-nfnl-directory-configuration/</id><published>2024-05-31T08:16:19-04:00</published><updated>2024-06-06T13:49:42-04:00</updated><content type="html">&lt;p&gt;I&amp;rsquo;ve long wanted to figure out how to get &lt;a href="https://github.com/olical/nfnl?tab=readme-ov-file#directory-local-neovim-configuration-in-fennel"&gt;&lt;code&gt;nfnl's&lt;/code&gt; directory-local configuration&lt;/a&gt; working,&#10;but it took me until yesterday to figure it out &amp;mdash;&#10;mostly by &lt;a href="https://github.com/Olical/nfnl/discussions/41"&gt;opening a discussion&lt;/a&gt; and then realizing my mistakes. 🤡&lt;/p&gt;&#10;&lt;p&gt;First, &lt;code&gt;exrc&lt;/code&gt; needs to be &lt;a href="https://github.com/stefanvanburen/dotfiles/commit/beed66f9a0a872f2a0db07b0e2de36ad262e4649"&gt;enabled&lt;/a&gt;.&#10;This will ultimately load the contents of the generated &lt;code&gt;.nvim.lua&lt;/code&gt; file,&#10;as described in &lt;a href="https://neovim.io/doc/user/options.html#'exrc'"&gt;&lt;code&gt;:help 'exrc'&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;&#10;&lt;p&gt;Next, create an &lt;code&gt;.nfnl.fnl&lt;/code&gt; file in the directory.&#10;I&amp;rsquo;ve been creating mine as:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fennel" data-lang="fennel"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;;; .nfnl.fnl&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:source-file-patterns&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:.nvim.fnl&lt;/span&gt;&lt;span class="p"&gt;]}&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&amp;hellip; which means that &lt;code&gt;nfnl&lt;/code&gt; only needs to compile the &lt;code&gt;.nvim.fnl&lt;/code&gt; file into &lt;code&gt;.nvim.lua&lt;/code&gt;.&lt;/p&gt;&#10;&lt;p&gt;From there, I create a &lt;code&gt;.nvim.fnl&lt;/code&gt; file with my configuration.&#10;Often this is to set up a particular project-local formatter or linter with conform or nvim-lint,&#10;so it may look something like:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fennel" data-lang="fennel"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;;; .nvim.fnl&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;let &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;conform&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;require &lt;/span&gt;&lt;span class="ss"&gt;:conform&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;set &lt;/span&gt;&lt;span class="nv"&gt;conform.formatters_by_ft.json&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:prettier&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;With this I&amp;rsquo;ve been able to &lt;a href="https://github.com/stefanvanburen/dotfiles/commit/b7ab7019510c4554e5eb432996bb8378d9e9bd44"&gt;remove a few $WORK specific formatters&lt;/a&gt;.&lt;/p&gt;&#10;&lt;p&gt;Lastly, I&amp;rsquo;ve &lt;a href="https://github.com/stefanvanburen/dotfiles/commit/1d740e2a0337678eb6084e4c97cd0a096ea46a71"&gt;globally gitignored&lt;/a&gt; these files so they aren&amp;rsquo;t accidentally committed.&#10;It&amp;rsquo;s possible to commit the &lt;code&gt;.nvim.lua&lt;/code&gt; files to share with other neovim users that have &lt;code&gt;exrc&lt;/code&gt; enabled,&#10;but I doubt I&amp;rsquo;ll use that very often &amp;mdash;&#10;I&amp;rsquo;m still undecided on if I&amp;rsquo;ll commit these for my personal projects.&lt;/p&gt;&#10;&lt;p&gt;📁&lt;/p&gt;&#10;</content><source:markdown>&#10;I've long wanted to figure out how to get [`nfnl's` directory-local configuration][1] working,&#10;but it took me until yesterday to figure it out ---&#10;mostly by [opening a discussion][2] and then realizing my mistakes. 🤡&#10;&#10;First, `exrc` needs to be [enabled][3].&#10;This will ultimately load the contents of the generated `.nvim.lua` file,&#10;as described in [`:help 'exrc'`][4]&#10;&#10;Next, create an `.nfnl.fnl` file in the directory.&#10;I've been creating mine as:&#10;&#10;```fennel&#10;;; .nfnl.fnl&#10;{:source-file-patterns [:.nvim.fnl]}&#10;```&#10;&#10;... which means that `nfnl` only needs to compile the `.nvim.fnl` file into `.nvim.lua`.&#10;&#10;From there, I create a `.nvim.fnl` file with my configuration.&#10;Often this is to set up a particular project-local formatter or linter with conform or nvim-lint,&#10;so it may look something like:&#10;&#10;```fennel&#10;;; .nvim.fnl&#10;(let [conform (require :conform)]&#10; (set conform.formatters_by_ft.json [:prettier]))&#10;```&#10;&#10;With this I've been able to [remove a few $WORK specific formatters][6].&#10;&#10;Lastly, I've [globally gitignored][5] these files so they aren't accidentally committed.&#10;It's possible to commit the `.nvim.lua` files to share with other neovim users that have `exrc` enabled,&#10;but I doubt I'll use that very often ---&#10;I'm still undecided on if I'll commit these for my personal projects.&#10;&#10;📁&#10;&#10;[1]: https://github.com/olical/nfnl?tab=readme-ov-file#directory-local-neovim-configuration-in-fennel&#10;[2]: https://github.com/Olical/nfnl/discussions/41&#10;[3]: https://github.com/stefanvanburen/dotfiles/commit/beed66f9a0a872f2a0db07b0e2de36ad262e4649&#10;[4]: https://neovim.io/doc/user/options.html#'exrc'&#10;[5]: https://github.com/stefanvanburen/dotfiles/commit/1d740e2a0337678eb6084e4c97cd0a096ea46a71&#10;[6]: https://github.com/stefanvanburen/dotfiles/commit/b7ab7019510c4554e5eb432996bb8378d9e9bd44&#10;</source:markdown></entry><entry><title type="html">Git Stash File(s)</title><link href="https://stefan.vanburen.xyz/til/git-stash-file/" rel="alternate" type="text/html"/><id>https://stefan.vanburen.xyz/til/git-stash-file/</id><published>2024-05-24T07:39:46-04:00</published><updated>2026-04-23T06:44:40-04:00</updated><content type="html">&lt;p&gt;This isn&amp;rsquo;t something I&amp;rsquo;m just learning today,&#10;but I&amp;rsquo;ve looked this up enough to warrant writing it down.&lt;/p&gt;&#10;&lt;p&gt;I&amp;rsquo;m often working on some code,&#10;and come across something unrelated that I want to change.&#10;So I do &amp;mdash; but it really doesn&amp;rsquo;t belong in this commit or &lt;abbr title="Pull Request"&gt;PR&lt;/abbr&gt;/patch/etc.!&lt;/p&gt;&#10;&lt;p&gt;(I&amp;rsquo;ve yet to put in the reps to understand &lt;a href="https://git-scm.com/docs/git-worktree"&gt;worktrees&lt;/a&gt; or &lt;a href="https://martinvonz.github.io/jj"&gt;jujutsu&lt;/a&gt;,&#10;which I&amp;rsquo;m assuming are the &amp;ldquo;real&amp;rdquo; answers here.)&lt;/p&gt;&#10;&lt;p&gt;For now, you can provide &lt;code&gt;git stash push&lt;/code&gt; a filename (or multiple!) to come back to those changes later:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-console" data-lang="console"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gp"&gt;$&lt;/span&gt; git stash push &amp;lt;filename&lt;span class="o"&gt;(&lt;/span&gt;s&lt;span class="o"&gt;)&lt;/span&gt;&amp;gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I&amp;rsquo;m sure there&amp;rsquo;s also a way to do this incrementally with &lt;code&gt;-p&lt;/code&gt; or &lt;a href="https://github.com/tpope/vim-fugitive"&gt;Fugitive&amp;rsquo;s&lt;/a&gt; interface.&lt;/p&gt;&#10;&lt;p&gt;📦&lt;/p&gt;&#10;</content><source:markdown>&#10;This isn't something I'm just learning today,&#10;but I've looked this up enough to warrant writing it down.&#10;&#10;I'm often working on some code,&#10;and come across something unrelated that I want to change.&#10;So I do --- but it really doesn't belong in this commit or &lt;abbr title="Pull Request"&gt;PR&lt;/abbr&gt;/patch/etc.!&#10;&#10;(I've yet to put in the reps to understand [worktrees][] or [jujutsu][],&#10;which I'm assuming are the "real" answers here.)&#10;&#10;For now, you can provide `git stash push` a filename (or multiple!) to come back to those changes later:&#10;&#10;```console&#10;$ git stash push &lt;filename(s)&gt;&#10;```&#10;&#10;I'm sure there's also a way to do this incrementally with `-p` or [Fugitive's][fugitive] interface.&#10;&#10;📦&#10;&#10;[worktrees]: https://git-scm.com/docs/git-worktree&#10;[jujutsu]: https://martinvonz.github.io/jj&#10;[fugitive]: https://github.com/tpope/vim-fugitive&#10;</source:markdown></entry><entry><title type="html">Service Wrappers</title><link href="https://stefan.vanburen.xyz/til/service-wrappers/" rel="alternate" type="text/html"/><id>https://stefan.vanburen.xyz/til/service-wrappers/</id><published>2022-11-01T18:54:05-04:00</published><updated>2024-08-16T08:46:52-04:00</updated><content type="html">&lt;p&gt;TIL of &lt;a href="https://naildrivin5.com/blog/2022/10/31/wrap-third-party-apis-in-service-wrappers-to-simplify-your-code.html"&gt;Service Wrappers&lt;/a&gt;&#10;a technique I&amp;rsquo;ve used in the past but have never had a name for.&#10;It&amp;rsquo;s essentially an approach to wrapping external APIs.&#10;From the article:&lt;/p&gt;&#10;&lt;blockquote&gt;&lt;ul&gt;&#10;&lt;li&gt;Methods should be named in the language of the service, not the language of the app.&lt;/li&gt;&#10;&lt;li&gt;Arguments should use the domain of the service, not the domain of the app.&lt;/li&gt;&#10;&lt;li&gt;Arguments should be whatever type is directly needed by the service, so passing in complex stuff like Active Record should be avoided.&lt;/li&gt;&#10;&lt;li&gt;The return value should not be a complex object from the third party, but ideally only what data a caller will need (often nothing at all). If it must be a complex object, its name or properties should be in the domain of the service.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;/blockquote&gt;&lt;p&gt;The article is written from a Rails perspective,&#10;but is broadly useful &amp;mdash;&#10;always nice to have a name for something useful!&lt;/p&gt;&#10;</content><source:markdown>&#10;TIL of [Service Wrappers][]&#10;a technique I've used in the past but have never had a name for.&#10;It's essentially an approach to wrapping external APIs.&#10;From the article:&#10;&#10;&gt; * Methods should be named in the language of the service, not the language of the app.&#10;&gt; * Arguments should use the domain of the service, not the domain of the app.&#10;&gt; * Arguments should be whatever type is directly needed by the service, so passing in complex stuff like Active Record should be avoided.&#10;&gt; * The return value should not be a complex object from the third party, but ideally only what data a caller will need (often nothing at all). If it must be a complex object, its name or properties should be in the domain of the service.&#10;&#10;The article is written from a Rails perspective,&#10;but is broadly useful ---&#10;always nice to have a name for something useful!&#10;&#10;[Service Wrappers]: https://naildrivin5.com/blog/2022/10/31/wrap-third-party-apis-in-service-wrappers-to-simplify-your-code.html&#10;</source:markdown></entry><entry><title type="html">Go&amp;rsquo;s fmt package Explicit Argument Indexes</title><link href="https://stefan.vanburen.xyz/til/fmt-explicit-argument-indexes/" rel="alternate" type="text/html"/><id>https://stefan.vanburen.xyz/til/fmt-explicit-argument-indexes/</id><published>2022-06-24T15:59:17-04:00</published><updated>2026-04-23T06:44:40-04:00</updated><content type="html">&lt;p&gt;Not sure how I hadn&amp;rsquo;t seen this before:&#10;in Go, you can specify the index of the format argument you want to use for each formatting verb.&lt;/p&gt;&#10;&lt;p&gt;Simply enough, from the &lt;a href="https://pkg.go.dev/fmt#hdr-Explicit_argument_indexes"&gt;package docs&lt;/a&gt;:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Sprintf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;%[2]d %[1]d\n&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;22&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// =&amp;gt; &amp;#34;22 11&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;In the past, I&amp;rsquo;ve actually done something along the lines of:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;test&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Sprintf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;%s %s&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// =&amp;gt; &amp;#34;test test&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Instead, you can do:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;test&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Sprintf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;%[1]s %[1]s&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// =&amp;gt; &amp;#34;test test&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This came up today at $WORK while providing a table name to a constructed &lt;abbr title="Structured Query Language"&gt;SQL&lt;/abbr&gt; query -&#10;the table name needed to be supplied multiple times in an upsert, so we could simply do:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Sprintf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;`&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;INSERT INTO %[1]s (...) VALUES ($1, $2)&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;ON CONFLICT (...) DO UPDATE SET ... = %[1]s.(...) WHERE %[1]s.(...) = $2;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;tableName&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Instead of needing to supply &lt;code&gt;tableName&lt;/code&gt; three times,&#10;it could be supplied once using &lt;code&gt;%[1]s&lt;/code&gt;.&lt;/p&gt;&#10;</content><source:markdown>&#10;Not sure how I hadn't seen this before:&#10;in Go, you can specify the index of the format argument you want to use for each formatting verb.&#10;&#10;Simply enough, from the [package docs](https://pkg.go.dev/fmt#hdr-Explicit_argument_indexes):&#10;&#10;```go&#10;fmt.Sprintf("%[2]d %[1]d\n", 11, 22) // =&gt; "22 11"&#10;```&#10;&#10;In the past, I've actually done something along the lines of:&#10;&#10;```go&#10;s := "test"&#10;fmt.Sprintf("%s %s", s, s) // =&gt; "test test"&#10;```&#10;&#10;Instead, you can do:&#10;&#10;```go&#10;s := "test"&#10;fmt.Sprintf("%[1]s %[1]s", s) // =&gt; "test test"&#10;```&#10;&#10;This came up today at $WORK while providing a table name to a constructed &lt;abbr title="Structured Query Language"&gt;SQL&lt;/abbr&gt; query -&#10;the table name needed to be supplied multiple times in an upsert, so we could simply do:&#10;&#10;```go&#10;query := fmt.Sprintf(`&#10;INSERT INTO %[1]s (...) VALUES ($1, $2)&#10;ON CONFLICT (...) DO UPDATE SET ... = %[1]s.(...) WHERE %[1]s.(...) = $2;&#10;`, tableName)&#10;```&#10;&#10;Instead of needing to supply `tableName` three times,&#10;it could be supplied once using `%[1]s`.&#10;</source:markdown></entry><entry><title type="html">SQLite Connection Configuration</title><link href="https://stefan.vanburen.xyz/til/sqlite-connection-configuration/" rel="alternate" type="text/html"/><id>https://stefan.vanburen.xyz/til/sqlite-connection-configuration/</id><published>2022-04-02T10:54:44-04:00</published><updated>2026-08-26T09:10:32-04:00</updated><content type="html">&lt;p&gt;These notes are basically cribbed from Ben Johnson&amp;rsquo;s excellent &amp;ldquo;Building Production Applications Using Go &amp;amp; SQLite&amp;rdquo; GopherCon talk,&#10;specifically the &lt;a href="https://youtu.be/XcAYkriuQ1o?t=430"&gt;&amp;ldquo;Configuring SQLite&amp;rdquo; section&lt;/a&gt;.&lt;/p&gt;&#10;&lt;p&gt;Generally speaking, you&amp;rsquo;ll want to set three &lt;code&gt;PRAGMA&lt;/code&gt;s on each SQLite connection:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sql" data-lang="sql"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;PRAGMA&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;journal_mode&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;WAL&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;PRAGMA&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;busy_timeout&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;PRAGMA&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;foreign_keys&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;ON&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;In turn,&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;&#10;&lt;p&gt;&lt;a href="https://sqlite.org/pragma.html#pragma_journal_mode"&gt;&lt;code&gt;PRAGMA journal_mode = WAL;&lt;/code&gt;&lt;/a&gt; sets the database into &lt;a href="https://www.sqlite.org/wal.html"&gt;&lt;abbr title="Write-Ahead Logging"&gt;WAL&lt;/abbr&gt; journaling mode&lt;/a&gt;.&#10;Unlike the others,&#10;this doesn&amp;rsquo;t necessarily need to be set on each connection;&#10;once a database is in &lt;a href="https://www.sqlite.org/wal.html"&gt;&lt;abbr title="Write-Ahead Logging"&gt;WAL&lt;/abbr&gt;&lt;/a&gt; mode &lt;a href="https://sqlite.org/wal.html#persistence_of_wal_mode"&gt;it&amp;rsquo;ll stay in that mode across database connections&lt;/a&gt;.&#10;&lt;a href="https://www.sqlite.org/wal.html"&gt;&lt;abbr title="Write-Ahead Logging"&gt;WAL&lt;/abbr&gt;&lt;/a&gt; journaling mode is recommended for most SQLite server applications,&#10;because it makes writers not block readers.&lt;/p&gt;&#10;&lt;/li&gt;&#10;&lt;li&gt;&#10;&lt;p&gt;&lt;a href="https://sqlite.org/pragma.html#pragma_busy_timeout"&gt;&lt;code&gt;PRAGMA busy_timeout = 5000;&lt;/code&gt;&lt;/a&gt; sets the &lt;a href="https://sqlite.org/c3ref/busy_timeout.html"&gt;busy_timeout&lt;/a&gt; to 5000 milliseconds (5 seconds).&#10;Without this setting,&#10;if a write transaction is running,&#10;and another write transaction starts,&#10;the second write transaction will fail immediately.&lt;/p&gt;&#10;&lt;/li&gt;&#10;&lt;li&gt;&#10;&lt;p&gt;&lt;a href="https://sqlite.org/pragma.html#pragma_foreign_keys"&gt;&lt;code&gt;PRAGMA foreign_keys = ON;&lt;/code&gt;&lt;/a&gt; turns on SQLite&amp;rsquo;s &lt;a href="https://sqlite.org/foreignkeys.html"&gt;foreign key support&lt;/a&gt;.&#10;Foreign Keys aren&amp;rsquo;t enabled by default in SQLite for historical reasons,&#10;but foreign keys are great for maintaining data integrity.&#10;So &amp;ndash; use them!&lt;/p&gt;&#10;&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;hr&gt;&#10;&lt;p&gt;&lt;ins datetime="2024-06-30"&gt;Update(2024-06-30): A more complete discussion of these settings and more can be found over at &lt;a href="https://kerkour.com/sqlite-for-servers"&gt;kerkour.com/sqlite-for-servers&lt;/a&gt;&lt;/ins&gt;&lt;/p&gt;&#10;</content><source:markdown>&#10;These notes are basically cribbed from Ben Johnson's excellent "Building Production Applications Using Go &amp; SQLite" GopherCon talk,&#10;specifically the ["Configuring SQLite" section](https://youtu.be/XcAYkriuQ1o?t=430).&#10;&#10;Generally speaking, you'll want to set three `PRAGMA`s on each SQLite connection:&#10;&#10;```sql&#10;PRAGMA journal_mode = WAL;&#10;PRAGMA busy_timeout = 5000;&#10;PRAGMA foreign_keys = ON;&#10;```&#10;&#10;In turn,&#10;&#10;* [`PRAGMA journal_mode = WAL;`][pragma-journal-mode] sets the database into [&lt;abbr title="Write-Ahead Logging"&gt;WAL&lt;/abbr&gt; journaling mode](https://www.sqlite.org/wal.html).&#10; Unlike the others,&#10; this doesn't necessarily need to be set on each connection;&#10; once a database is in [&lt;abbr title="Write-Ahead Logging"&gt;WAL&lt;/abbr&gt;](https://www.sqlite.org/wal.html) mode [it'll stay in that mode across database connections](https://sqlite.org/wal.html#persistence_of_wal_mode).&#10; [&lt;abbr title="Write-Ahead Logging"&gt;WAL&lt;/abbr&gt;](https://www.sqlite.org/wal.html) journaling mode is recommended for most SQLite server applications,&#10; because it makes writers not block readers.&#10;&#10;* [`PRAGMA busy_timeout = 5000;`][pragma-busy-timeout] sets the [busy_timeout](https://sqlite.org/c3ref/busy_timeout.html) to 5000 milliseconds (5 seconds).&#10; Without this setting,&#10; if a write transaction is running,&#10; and another write transaction starts,&#10; the second write transaction will fail immediately.&#10;&#10;* [`PRAGMA foreign_keys = ON;`][pragma-foreign-keys] turns on SQLite's [foreign key support](https://sqlite.org/foreignkeys.html).&#10; Foreign Keys aren't enabled by default in SQLite for historical reasons,&#10; but foreign keys are great for maintaining data integrity.&#10; So -- use them!&#10;&#10;[pragma-journal-mode]: https://sqlite.org/pragma.html#pragma_journal_mode&#10;[pragma-foreign-keys]: https://sqlite.org/pragma.html#pragma_foreign_keys&#10;[pragma-busy-timeout]: https://sqlite.org/pragma.html#pragma_busy_timeout&#10;&#10;* * *&#10;&#10;&lt;ins datetime="2024-06-30"&gt;Update(2024-06-30): A more complete discussion of these settings and more can be found over at [kerkour.com/sqlite-for-servers](https://kerkour.com/sqlite-for-servers)&lt;/ins&gt;&#10;</source:markdown></entry><entry><title type="html">Shell in Markdown</title><link href="https://stefan.vanburen.xyz/til/markdown-shell/" rel="alternate" type="text/html"/><id>https://stefan.vanburen.xyz/til/markdown-shell/</id><published>2022-01-28T16:23:20-05:00</published><updated>2026-04-16T19:36:49-04:00</updated><content type="html">&lt;p&gt;For a long time,&#10;I&amp;rsquo;ve used &lt;code&gt;commandline&lt;/code&gt; as the name for the syntax of code fences in markdown whenever I&amp;rsquo;ve wanted to denote a shell command or session.&lt;/p&gt;&#10;&lt;p&gt;I&amp;rsquo;ve now realized that I&amp;rsquo;ve been using the wrong name,&#10;and also that there are two separate scenarios to keep in mind:&lt;/p&gt;&#10;&lt;hr&gt;&#10;&lt;p&gt;The first scenario is one-off commands, which should use the syntax &lt;a href="https://github.com/github/linguist/blob/73e2d735c3c26577fc89c1cb3f8342e5a8ff1d82/lib/linguist/languages.yml#L5602"&gt;&lt;code&gt;sh&lt;/code&gt;, or &lt;code&gt;shell&lt;/code&gt;&lt;/a&gt;.&#10;This would be showing something you&amp;rsquo;d enter directly at the command line.&lt;/p&gt;&#10;&lt;p&gt;For example:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;brew install git&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I leave out the &lt;a href="https://en.wikipedia.org/wiki/Command-line_interface#Command_prompt"&gt;command prompt&lt;/a&gt;,&#10;because it&amp;rsquo;s implied by the context that the line is to be entered in a command line.&#10;Also, it helps with copy-paste.&lt;/p&gt;&#10;&lt;hr&gt;&#10;&lt;p&gt;The other scenario is showing a shell &lt;em&gt;session&lt;/em&gt;,&#10;which typically shows the command prompt, an entered command, and &lt;em&gt;the response to the command&lt;/em&gt;.&#10;In this instance I&amp;rsquo;d use the &lt;a href="https://github.com/github/linguist/blob/cddf7476af4c95d1572956ffc5c0cb84f7e431c5/lib/linguist/languages.yml#L5921"&gt;&lt;code&gt;console&lt;/code&gt;, or &lt;code&gt;sh-session&lt;/code&gt;&lt;/a&gt; syntax.&lt;/p&gt;&#10;&lt;p&gt;For example:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-console" data-lang="console"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gp"&gt;$&lt;/span&gt; git st&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;?? content/til/markdown-shell.md&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;🐚&lt;/p&gt;&#10;</content><source:markdown>&#10;For a long time,&#10;I've used `commandline` as the name for the syntax of code fences in markdown whenever I've wanted to denote a shell command or session.&#10;&#10;I've now realized that I've been using the wrong name,&#10;and also that there are two separate scenarios to keep in mind:&#10;&#10;* * *&#10;&#10;The first scenario is one-off commands, which should use the syntax [`sh`, or `shell`](https://github.com/github/linguist/blob/73e2d735c3c26577fc89c1cb3f8342e5a8ff1d82/lib/linguist/languages.yml#L5602).&#10;This would be showing something you'd enter directly at the command line.&#10;&#10;For example:&#10;&#10;```sh&#10;brew install git&#10;```&#10;&#10;I leave out the [command prompt](https://en.wikipedia.org/wiki/Command-line_interface#Command_prompt),&#10;because it's implied by the context that the line is to be entered in a command line.&#10;Also, it helps with copy-paste.&#10;&#10;* * *&#10;&#10;The other scenario is showing a shell _session_,&#10;which typically shows the command prompt, an entered command, and _the response to the command_.&#10;In this instance I'd use the [`console`, or `sh-session`](https://github.com/github/linguist/blob/cddf7476af4c95d1572956ffc5c0cb84f7e431c5/lib/linguist/languages.yml#L5921) syntax.&#10;&#10;For example:&#10;&#10;```console&#10;$ git st&#10;?? content/til/markdown-shell.md&#10;```&#10;&#10;🐚&#10;</source:markdown></entry><entry><title type="html">PostgreSQL and jsonb type casts</title><link href="https://stefan.vanburen.xyz/til/postgres-jsonb-casting/" rel="alternate" type="text/html"/><id>https://stefan.vanburen.xyz/til/postgres-jsonb-casting/</id><published>2022-01-19T18:30:21-05:00</published><updated>2026-04-23T06:44:40-04:00</updated><content type="html">&lt;p&gt;Found a bug in one of our systems at work that deals with &lt;a href="https://www.postgresql.org/docs/current/datatype-json.html"&gt;jsonb data&lt;/a&gt; stored in Postgres today.&#10;The quick summary is that one of our systems was looking for the maximum numerical value of a jsonb integer in a table.&lt;/p&gt;&#10;&lt;p&gt;A very simplified example:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-postgresql" data-lang="postgresql"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;balances&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;unnest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;ARRAY&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s1"&gt;&amp;#39;{&amp;#34;balance&amp;#34;: 7}&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nb"&gt;jsonb&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;balance&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s1"&gt;&amp;#39;{&amp;#34;balance&amp;#34;: 17}&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nb"&gt;jsonb&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;balance&amp;#39;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;balances&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This returns &lt;code&gt;7&lt;/code&gt;, because while the &amp;ldquo;balance&amp;rdquo; field is a numeric &lt;abbr title="JavaScript Object Notation"&gt;JSON&lt;/abbr&gt; value, the &lt;a href="https://www.postgresql.org/docs/current/functions-json.html#FUNCTIONS-JSON-PROCESSING"&gt;&lt;code&gt;-&amp;gt;&amp;gt;&lt;/code&gt; operator&lt;/a&gt; turns the value into text! &amp;mdash; which makes the comparison a lexicographical one.&lt;/p&gt;&#10;&lt;p&gt;Instead, you need to make sure to &lt;a href="https://www.postgresql.org/docs/current/sql-expressions.html#SQL-SYNTAX-TYPE-CASTS"&gt;cast the values&lt;/a&gt; before comparing:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-postgresql" data-lang="postgresql"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;balances&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;unnest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;ARRAY&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;{&amp;#34;balance&amp;#34;: 7}&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nb"&gt;jsonb&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;balance&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nb"&gt;integer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;{&amp;#34;balance&amp;#34;: 17}&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nb"&gt;jsonb&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;balance&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nb"&gt;integer&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;balances&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Which returns the expected &lt;code&gt;17&lt;/code&gt;.&lt;/p&gt;&#10;</content><source:markdown>&#10;Found a bug in one of our systems at work that deals with [jsonb data](https://www.postgresql.org/docs/current/datatype-json.html) stored in Postgres today.&#10;The quick summary is that one of our systems was looking for the maximum numerical value of a jsonb integer in a table.&#10;&#10;A very simplified example:&#10;&#10;```postgresql&#10;SELECT&#10; max(balances)&#10;FROM&#10; unnest(ARRAY[&#10; '{"balance": 7}'::jsonb-&gt;&gt;'balance',&#10; '{"balance": 17}'::jsonb-&gt;&gt;'balance'&#10; ]) as balances&#10;```&#10;&#10;This returns `7`, because while the "balance" field is a numeric &lt;abbr title="JavaScript Object Notation"&gt;JSON&lt;/abbr&gt; value, the [`-&gt;&gt;` operator](https://www.postgresql.org/docs/current/functions-json.html#FUNCTIONS-JSON-PROCESSING) turns the value into text! --- which makes the comparison a lexicographical one.&#10;&#10;Instead, you need to make sure to [cast the values](https://www.postgresql.org/docs/current/sql-expressions.html#SQL-SYNTAX-TYPE-CASTS) before comparing:&#10;&#10;```postgresql&#10;SELECT&#10; max(balances)&#10;FROM&#10; unnest(ARRAY[&#10; ('{"balance": 7}'::jsonb-&gt;&gt;'balance')::integer,&#10; ('{"balance": 17}'::jsonb-&gt;&gt;'balance')::integer&#10; ]) as balances&#10;```&#10;&#10;Which returns the expected `17`.&#10;</source:markdown></entry><entry><title type="html">PostgreSQL IN versus ANY</title><link href="https://stefan.vanburen.xyz/til/psql-in-versus-any/" rel="alternate" type="text/html"/><id>https://stefan.vanburen.xyz/til/psql-in-versus-any/</id><published>2022-01-11T08:40:46-05:00</published><updated>2025-11-16T19:49:53-05:00</updated><content type="html">&lt;p&gt;I still feel rather new to PostgreSQL,&#10;and a few times now in Go I&amp;rsquo;ve tried to do something along the lines of:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;pgdb&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;struct&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&#9;&lt;/span&gt;&lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;sql&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DB&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;pgdb&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;getEntityByID&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ids&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;([]&lt;/span&gt;&lt;span class="nx"&gt;Entity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&#9;&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;SELECT * FROM entities WHERE entities.id IN ($1)&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&#9;&lt;/span&gt;&lt;span class="nx"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;QueryContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;pq&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;StringArray&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ids&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&#9;&lt;/span&gt;&lt;span class="cp"&gt;/// ...&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;But the query would always fail or retrieve no rows.&#10;I&amp;rsquo;ve always fixed it by switching the &lt;code&gt;IN $1&lt;/code&gt; for a &lt;code&gt;= ANY($1)&lt;/code&gt;,&#10;but never quite understood why.&lt;/p&gt;&#10;&lt;p&gt;Researching this a bit more today,&#10;I realized that the &lt;a href="https://www.postgresql.org/docs/current/functions-comparisons.html#FUNCTIONS-COMPARISONS-IN-SCALAR"&gt;&lt;code&gt;IN&lt;/code&gt;&lt;/a&gt; function takes a list of literals,&#10;whereas &lt;a href="https://www.postgresql.org/docs/current/functions-comparisons.html#id-1.5.8.30.16"&gt;&lt;code&gt;ANY&lt;/code&gt;&lt;/a&gt; is an array function that takes a single array literal.&#10;Thus, when I&amp;rsquo;m passing a &lt;code&gt;pq.StringArray&lt;/code&gt;, it only works with the array function.&lt;/p&gt;&#10;&lt;hr&gt;&#10;&lt;p&gt;I found this out going through &lt;a href="https://github.com/lib/pq"&gt;lib/pq&lt;/a&gt;&amp;rsquo;s issues,&#10;finding &lt;a href="https://github.com/lib/pq/issues/600"&gt;this issue&lt;/a&gt; with &lt;a href="https://github.com/lib/pq/issues/600#issuecomment-294143990"&gt;this comment&lt;/a&gt; explaining the difference quite well,&#10;which also pointed to a &lt;a href="https://github.com/lib/pq/issues/515"&gt;simpler case&lt;/a&gt; where &lt;a href="https://github.com/lib/pq/issues/515#issuecomment-252393112"&gt;this comment&lt;/a&gt; cleared things up for me.&lt;/p&gt;&#10;</content><source:markdown>&#10;I still feel rather new to PostgreSQL,&#10;and a few times now in Go I've tried to do something along the lines of:&#10;&#10;```go&#10;type pgdb struct {&#10;&#9;db *sql.DB&#10;}&#10;&#10;func (db *pgdb) getEntityByID(ctx context.Context, ids []string) ([]Entity, error) {&#10;&#9;query := "SELECT * FROM entities WHERE entities.id IN ($1)"&#10;&#10;&#9;rows, err := db.db.QueryContext(ctx, query, pq.StringArray(ids))&#10;&#9;/// ...&#10;}&#10;```&#10;&#10;But the query would always fail or retrieve no rows.&#10;I've always fixed it by switching the `IN $1` for a `= ANY($1)`,&#10;but never quite understood why.&#10;&#10;Researching this a bit more today,&#10;I realized that the [`IN`] function takes a list of literals,&#10;whereas [`ANY`] is an array function that takes a single array literal.&#10;Thus, when I'm passing a `pq.StringArray`, it only works with the array function.&#10;&#10;* * *&#10;&#10;I found this out going through [lib/pq](https://github.com/lib/pq)'s issues,&#10;finding [this issue](https://github.com/lib/pq/issues/600) with [this comment](https://github.com/lib/pq/issues/600#issuecomment-294143990) explaining the difference quite well,&#10;which also pointed to a [simpler case](https://github.com/lib/pq/issues/515) where [this comment](https://github.com/lib/pq/issues/515#issuecomment-252393112) cleared things up for me.&#10;&#10;[`IN`]: https://www.postgresql.org/docs/current/functions-comparisons.html#FUNCTIONS-COMPARISONS-IN-SCALAR&#10;[`ANY`]: https://www.postgresql.org/docs/current/functions-comparisons.html#id-1.5.8.30.16&#10;</source:markdown></entry><entry><title type="html">Go&amp;rsquo;s package fmt %q verb for strings</title><link href="https://stefan.vanburen.xyz/til/gofmt-q/" rel="alternate" type="text/html"/><id>https://stefan.vanburen.xyz/til/gofmt-q/</id><published>2021-09-23T10:00:45-04:00</published><updated>2026-04-16T19:36:49-04:00</updated><content type="html">&lt;p&gt;I wasn&amp;rsquo;t aware of the Go &lt;code&gt;fmt&lt;/code&gt; package &lt;code&gt;%q&lt;/code&gt; verb, for quoting strings.&#10;Typically, I&amp;rsquo;ve been escaping the quotes manually and using the &lt;code&gt;%s&lt;/code&gt; verb:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Sprintf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;this needs quoting: \&amp;#34;%s\&amp;#34;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;input&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;But the &lt;code&gt;%q&lt;/code&gt; verb obviates the need for that manual quoting.&lt;/p&gt;&#10;&lt;p&gt;From the &lt;a href="https://pkg.go.dev/fmt"&gt;package documentation&lt;/a&gt;:&lt;/p&gt;&#10;&lt;blockquote&gt;&lt;p&gt;%s the uninterpreted bytes of the string or slice&lt;/p&gt;&#10;&lt;p&gt;%q a double-quoted string safely escaped with Go syntax&lt;/p&gt;&#10;&lt;/blockquote&gt;&lt;p&gt;And a quick example (on the &lt;a href="https://play.golang.org/p/w-DfnVMSi_k"&gt;Go Playground&lt;/a&gt;):&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;package&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&#9;&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;fmt&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&#9;&lt;/span&gt;&lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;I&amp;#39;m a test string&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&#9;&lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;\&amp;#34;%s\&amp;#34;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&#9;&lt;/span&gt;&lt;span class="c1"&gt;// Output: &amp;#34;I&amp;#39;m a test string&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&#9;&lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&#9;&lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;%q&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&#9;&lt;/span&gt;&lt;span class="c1"&gt;// Output: &amp;#34;I&amp;#39;m a test string&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</content><source:markdown>&#10;I wasn't aware of the Go `fmt` package `%q` verb, for quoting strings.&#10;Typically, I've been escaping the quotes manually and using the `%s` verb:&#10;&#10;```go&#10;fmt.Sprintf("this needs quoting: \"%s\"", "input")&#10;```&#10;&#10;But the `%q` verb obviates the need for that manual quoting.&#10;&#10;From the [package documentation](https://pkg.go.dev/fmt):&#10;&#10;&gt; %s the uninterpreted bytes of the string or slice&#10;&gt;&#10;&gt; %q a double-quoted string safely escaped with Go syntax&#10;&#10;And a quick example (on the [Go Playground](https://play.golang.org/p/w-DfnVMSi_k)):&#10;&#10;```go&#10;package main&#10;&#10;import (&#10;&#9;"fmt"&#10;)&#10;&#10;func main() {&#10;&#9;test := "I'm a test string"&#10;&#10;&#9;fmt.Printf("\"%s\"", test)&#10;&#9;// Output: "I'm a test string"&#10;&#9;fmt.Println()&#10;&#9;fmt.Printf("%q", test)&#10;&#9;// Output: "I'm a test string"&#10;}&#10;```&#10;</source:markdown></entry><entry><title type="html">Go Templates Whitespace</title><link href="https://stefan.vanburen.xyz/til/go-templates-whitespace/" rel="alternate" type="text/html"/><id>https://stefan.vanburen.xyz/til/go-templates-whitespace/</id><published>2021-08-31T18:41:20-04:00</published><updated>2026-04-23T06:44:40-04:00</updated><content type="html">&lt;p&gt;When writing &lt;abbr title="HyperText Markup Language"&gt;HTML&lt;/abbr&gt; templates in Go, knowing about the &lt;a href="https://pkg.go.dev/text/template#hdr-Text_and_spaces"&gt;whitespace modifiers&lt;/a&gt; is crucial for keeping both the templates and the output &lt;abbr title="HyperText Markup Language"&gt;HTML&lt;/abbr&gt; looking good.&lt;/p&gt;&#10;&lt;p&gt;For example, if you had the following template, with &lt;code&gt;.SomeText&lt;/code&gt; being &lt;code&gt;&amp;quot;foo&amp;quot;&lt;/code&gt; and &lt;code&gt;.Something&lt;/code&gt; being &lt;code&gt;true&lt;/code&gt;:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-go-html-template" data-lang="go-html-template"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt; &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;/posts&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="cp"&gt;{{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;.Something&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cp"&gt;}}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; Text&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="cp"&gt;{{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cp"&gt;}}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; Image&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="cp"&gt;{{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cp"&gt;}}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="cp"&gt;{{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;.SomeText&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cp"&gt;}}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The actual output &lt;abbr title="HyperText Markup Language"&gt;HTML&lt;/abbr&gt; would look something like:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-html" data-lang="html"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt; &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;/posts&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; Text &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; foo &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Which isn&amp;rsquo;t what you want, because there&amp;rsquo;s extra spaces &amp;mdash; both around &amp;ldquo;Text&amp;rdquo; and around &amp;ldquo;foo&amp;rdquo;.&lt;/p&gt;&#10;&lt;p&gt;Instead, you could do:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-go-html-template" data-lang="go-html-template"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt; &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;/posts&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="cp"&gt;{{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;.Something&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cp"&gt;}}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="cp"&gt;{{-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Text&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cp"&gt;-}}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="cp"&gt;{{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cp"&gt;}}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="cp"&gt;{{-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Image&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cp"&gt;-}}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="cp"&gt;{{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cp"&gt;}}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="cp"&gt;{{-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;.SomeText&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cp"&gt;-}}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;to get&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-html" data-lang="html"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt; &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;/posts&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Text&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;foo&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Which makes both the source code and &lt;abbr title="HyperText Markup Language"&gt;HTML&lt;/abbr&gt; nice and neat.&lt;/p&gt;&#10;</content><source:markdown>&#10;When writing &lt;abbr title="HyperText Markup Language"&gt;HTML&lt;/abbr&gt; templates in Go, knowing about the [whitespace modifiers](https://pkg.go.dev/text/template#hdr-Text_and_spaces) is crucial for keeping both the templates and the output &lt;abbr title="HyperText Markup Language"&gt;HTML&lt;/abbr&gt; looking good.&#10;&#10;For example, if you had the following template, with `.SomeText` being `"foo"` and `.Something` being `true`:&#10;&#10;```go-html-template&#10;&lt;a href="/posts"&gt;&#10; &lt;span&gt;&#10; {{ if .Something }}&#10; Text&#10; {{ else }}&#10; Image&#10; {{ end }}&#10; &lt;/span&gt;&#10; {{ .SomeText }}&#10;&lt;/a&gt;&#10;```&#10;&#10;The actual output &lt;abbr title="HyperText Markup Language"&gt;HTML&lt;/abbr&gt; would look something like:&#10;&#10;```html&#10;&lt;a href="/posts"&gt;&lt;span&gt; Text &lt;/span&gt; foo &lt;/a&gt;&#10;```&#10;&#10;Which isn't what you want, because there's extra spaces --- both around "Text" and around "foo".&#10;&#10;Instead, you could do:&#10;&#10;```go-html-template&#10;&lt;a href="/posts"&gt;&#10; &lt;span&gt;&#10; {{ if .Something }}&#10; {{- Text -}}&#10; {{ else }}&#10; {{- Image -}}&#10; {{ end }}&#10; &lt;/span&gt;&#10; {{- .SomeText -}}&#10;&lt;/a&gt;&#10;```&#10;&#10;to get&#10;&#10;```html&#10;&lt;a href="/posts"&gt;&lt;span&gt;Text&lt;/span&gt;foo&lt;/a&gt;&#10;```&#10;&#10;Which makes both the source code and &lt;abbr title="HyperText Markup Language"&gt;HTML&lt;/abbr&gt; nice and neat.&#10;</source:markdown></entry><entry><title type="html">&lt;code&gt;time.Ticker&lt;/code&gt; in Go</title><link href="https://stefan.vanburen.xyz/til/ticker/" rel="alternate" type="text/html"/><id>https://stefan.vanburen.xyz/til/ticker/</id><published>2021-05-21T20:25:49-04:00</published><updated>2026-05-11T20:01:13-04:00</updated><content type="html">&lt;p&gt;The &lt;a href="https://pkg.go.dev/time#Ticker"&gt;&lt;code&gt;time.Ticker&lt;/code&gt;&lt;/a&gt; type in Go is incredibly useful for situations in which &lt;a href="https://en.wikipedia.org/wiki/Polling_(computer_science)"&gt;polling&lt;/a&gt; is needed.&lt;/p&gt;&#10;&lt;p&gt;The easiest way to use the Ticker is via the &lt;a href="https://pkg.go.dev/time#example-Tick"&gt;&lt;code&gt;time.Tick&lt;/code&gt;&lt;/a&gt; function,&#10;which just provides access to the ticking channel,&#10;which makes it easy to &lt;code&gt;range&lt;/code&gt; over:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;package&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&#9;&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;fmt&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&#9;&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;time&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&#9;&lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;currentTime&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;range&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Tick&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Second&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&#9;&#9;&lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;current time: &amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;currentTime&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&#9;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;For more control, &lt;a href="https://pkg.go.dev/time#NewTicker"&gt;&lt;code&gt;time.NewTicker&lt;/code&gt;&lt;/a&gt; works wonders &amp;mdash;&#10;there&amp;rsquo;s no better example than the one &lt;a href="https://go.dev/play/p/nG23A6LEd19"&gt;from the docs&lt;/a&gt;!&lt;/p&gt;&#10;</content><source:markdown>&#10;The [`time.Ticker`](https://pkg.go.dev/time#Ticker) type in Go is incredibly useful for situations in which [polling](https://en.wikipedia.org/wiki/Polling_(computer_science)) is needed.&#10;&#10;The easiest way to use the Ticker is via the [`time.Tick`](https://pkg.go.dev/time#example-Tick) function,&#10;which just provides access to the ticking channel,&#10;which makes it easy to `range` over:&#10;&#10;```go&#10;package main&#10;&#10;import (&#10;&#9;"fmt"&#10;&#9;"time"&#10;)&#10;&#10;func main() {&#10;&#9;for currentTime := range time.Tick(time.Second) {&#10;&#9;&#9;fmt.Println("current time: ", currentTime)&#10;&#9;}&#10;}&#10;```&#10;&#10;For more control, [`time.NewTicker`](https://pkg.go.dev/time#NewTicker) works wonders ---&#10;there's no better example than the one [from the docs](https://go.dev/play/p/nG23A6LEd19)!&#10;</source:markdown></entry><entry><title type="html">&lt;code&gt;run&lt;/code&gt; vs &lt;code&gt;errgroup&lt;/code&gt;</title><link href="https://stefan.vanburen.xyz/til/errgroup-vs-run/" rel="alternate" type="text/html"/><id>https://stefan.vanburen.xyz/til/errgroup-vs-run/</id><published>2021-05-11T10:38:26-04:00</published><updated>2026-05-11T20:01:13-04:00</updated><content type="html">&lt;p&gt;I typically don&amp;rsquo;t do much with concurrency in Go, but messed around this morning with both &lt;a href="https://github.com/oklog/run"&gt;oklog/run&lt;/a&gt; and &lt;a href="https://golang.org/x/sync/errgroup"&gt;sync/errgroup&lt;/a&gt;.&lt;/p&gt;&#10;&lt;p&gt;I&amp;rsquo;ve defaulted to using oklog/run in the past, but I realized that while they have similar interfaces, they&amp;rsquo;re useful for different things:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;oklog/run is useful for long-running processes.&#10;&lt;ul&gt;&#10;&lt;li&gt;think starting up http servers alongside other components, and waiting for one to finish, in which case all other components are signalled to finish.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;/li&gt;&#10;&lt;li&gt;sync/errgroup is useful for short-lived, parallel processes.&#10;&lt;ul&gt;&#10;&lt;li&gt;think units of work to be done in parallel, where one process resulting in an error should stop the entire computation.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;/li&gt;&#10;&lt;/ul&gt;&#10;</content><source:markdown>&#10;I typically don't do much with concurrency in Go, but messed around this morning with both [oklog/run](https://github.com/oklog/run) and [sync/errgroup](https://golang.org/x/sync/errgroup).&#10;&#10;I've defaulted to using oklog/run in the past, but I realized that while they have similar interfaces, they're useful for different things:&#10;&#10;* oklog/run is useful for long-running processes.&#10; * think starting up http servers alongside other components, and waiting for one to finish, in which case all other components are signalled to finish.&#10;* sync/errgroup is useful for short-lived, parallel processes.&#10; * think units of work to be done in parallel, where one process resulting in an error should stop the entire computation.&#10;</source:markdown></entry><entry><title type="html">Use &lt;code&gt;jq&lt;/code&gt; to see if a JSON key exists</title><link href="https://stefan.vanburen.xyz/til/jq-key-exists/" rel="alternate" type="text/html"/><id>https://stefan.vanburen.xyz/til/jq-key-exists/</id><published>2021-04-22T11:11:03-04:00</published><updated>2026-05-11T20:01:13-04:00</updated><content type="html">&lt;p&gt;I&amp;rsquo;m constantly using &lt;a href="https://stedolan.github.io/jq/"&gt;&lt;code&gt;jq&lt;/code&gt;&lt;/a&gt; to deal with &lt;abbr title="JavaScript Object Notation"&gt;JSON&lt;/abbr&gt; via the &lt;abbr title="Command-Line Interface"&gt;CLI&lt;/abbr&gt;.&lt;/p&gt;&#10;&lt;p&gt;Today I needed to figure out the difference between a key existing with a &lt;code&gt;null&lt;/code&gt; value or not existing at all in a bit of &lt;abbr title="JavaScript Object Notation"&gt;JSON&lt;/abbr&gt;.&lt;/p&gt;&#10;&lt;p&gt;By default, &amp;ldquo;querying&amp;rdquo; a key via &lt;code&gt;jq&lt;/code&gt; will return &lt;code&gt;null&lt;/code&gt; whether the key exists and is &lt;code&gt;null&lt;/code&gt;, &lt;strong&gt;or&lt;/strong&gt; the key doesn&amp;rsquo;t exist:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fish" data-lang="fish"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;△ &lt;span class="c"&gt;# NOTE: the following is in fish, but should be straightforward to port to other shells&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;△ set &lt;span class="nv"&gt;json&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;{&amp;#34;test&amp;#34;: null}&amp;#39;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;△ echo &lt;span class="nv"&gt;$json&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nf"&gt;jq&lt;/span&gt; .test&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nf"&gt;null&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;△ echo &lt;span class="nv"&gt;$json&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nf"&gt;jq&lt;/span&gt; .nonexistent&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nf"&gt;null&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Instead, you can use the &lt;code&gt;jq&lt;/code&gt;&amp;rsquo;s &lt;a href="https://stedolan.github.io/jq/manual/#has(key)"&gt;&lt;code&gt;has&lt;/code&gt;&lt;/a&gt; function to determine the difference:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fish" data-lang="fish"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;△ echo &lt;span class="nv"&gt;$json&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nf"&gt;jq&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;has(&amp;#34;test&amp;#34;)&amp;#39;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;true&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;△ echo &lt;span class="nv"&gt;$json&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nf"&gt;jq&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;has(&amp;#34;nonexistent&amp;#34;)&amp;#39;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;false&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</content><source:markdown>&#10;I'm constantly using [`jq`](https://stedolan.github.io/jq/) to deal with &lt;abbr title="JavaScript Object Notation"&gt;JSON&lt;/abbr&gt; via the &lt;abbr title="Command-Line Interface"&gt;CLI&lt;/abbr&gt;.&#10;&#10;Today I needed to figure out the difference between a key existing with a `null` value or not existing at all in a bit of &lt;abbr title="JavaScript Object Notation"&gt;JSON&lt;/abbr&gt;.&#10;&#10;By default, "querying" a key via `jq` will return `null` whether the key exists and is `null`, **or** the key doesn't exist:&#10;&#10;```fish&#10;△ # NOTE: the following is in fish, but should be straightforward to port to other shells&#10;&#10;△ set json '{"test": null}'&#10;&#10;△ echo $json | jq .test&#10;null&#10;&#10;△ echo $json | jq .nonexistent&#10;null&#10;```&#10;&#10;Instead, you can use the `jq`'s [`has`](https://stedolan.github.io/jq/manual/#has(key)) function to determine the difference:&#10;&#10;```fish&#10;△ echo $json | jq 'has("test")'&#10;true&#10;&#10;△ echo $json | jq 'has("nonexistent")'&#10;false&#10;```&#10;</source:markdown></entry></feed>