<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>llimllib's notes</title>
  <id>http://notes.billmill.org/</id>
  <link href="https://notes.billmill.org/atom.xml" rel="self" />
  <updated>2026-03-07T12:52:13.690775Z</updated>
  <generator uri="https://github.com/llimllib/obsidian_notes/" version="1.0">
    Obsidian Notes
  </generator>
  <author><name>Bill Mill</name></author>
  <entry>
    <id>https://notes.billmill.org/blog/2026/01/my_tools_in_2026.html</id>
    <link type="text/html" href="https://notes.billmill.org/blog/2026/01/my_tools_in_2026.html" />
    <title>my tools in 2026</title>
    <published>2026-01-03T18:35:04.419Z</published>
    <updated>2026-01-03T18:35:04.419Z</updated>
    <content type="html">&lt;p&gt;Here&#x27;s a brief survey of the tools I&#x27;m currently using&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#computer&quot;&gt;Computer&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#software&quot;&gt;Software&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#editor&quot;&gt;editor&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#neovim-plugins&quot;&gt;neovim plugins&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#terminal&quot;&gt;terminal&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#terminal-software&quot;&gt;terminal software&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#browser&quot;&gt;Browser&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#browser-plugins&quot;&gt;browser plugins&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#note-taking&quot;&gt;Note Taking&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#mail&quot;&gt;Mail&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#chat&quot;&gt;Chat&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#music&quot;&gt;Music&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#webcam&quot;&gt;Webcam&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#photos&quot;&gt;Photos&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#video&quot;&gt;Video&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&quot;computer&quot;&gt;Computer&lt;/h1&gt;
&lt;p&gt;I use a 14-inch MacBook Pro M1 Max that I bought in 2021. It is, on balance, the best computer I have ever owned. The keyboard is usable (not the best macbook keyboard ever, but... fine), the screen is lovely, it sleeps when it&#x27;s supposed to sleep and the battery still lasts a long time.&lt;/p&gt;
&lt;p&gt;The right shift key is broken, but using the left one hasn&#x27;t proved to be much of a challenge.&lt;/p&gt;
&lt;p&gt;I usually replace my computers every 5 years, but I don&#x27;t see any reason why I&#x27;d need a new one next year.&lt;/p&gt;
&lt;h1 id=&quot;software&quot;&gt;Software&lt;/h1&gt;
&lt;h2 id=&quot;editor&quot;&gt;editor&lt;/h2&gt;
&lt;p&gt;The most important piece of software on my computer is &lt;a href=&quot;/computer_usage/vim/neovim.html&quot; class=&quot;internal-link&quot;&gt;neovim&lt;/a&gt;. I&#x27;ve been using vim-ish software since 2003 or so, and on balance I think the neovim team has done a great job shepherding the software into the modern age.&lt;/p&gt;
&lt;p&gt;I get a bit irritated that I need to edit my configuration more often than I used to with Vim, but coding tools are changing so fast right now that it doesn&#x27;t seem possible to go back to the world where I edited my config file once every other year.&lt;/p&gt;
&lt;h3 id=&quot;neovim-plugins&quot;&gt;neovim plugins&lt;/h3&gt;
&lt;p&gt;My vim config is available &lt;a href=&quot;https://github.com/llimllib/personal_code/tree/master/homedir/.config/nvim&quot; class=&quot;external-link&quot;&gt;here&lt;/a&gt;. I won&#x27;t list all the plugins; there aren&#x27;t &lt;a href=&quot;https://github.com/llimllib/personal_code/blob/master/homedir/.config/nvim/lua/lazyconfig.lua&quot; class=&quot;external-link&quot;&gt;that many&lt;/a&gt;, and most of them are trivial, rarely-used or both. The ones I need are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/nvim-telescope/telescope.nvim&quot; class=&quot;external-link&quot;&gt;telescope.nvim&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;I have &amp;quot;open by filename search&amp;quot; bound to &lt;code&gt;,ff&lt;/code&gt; and &amp;quot;open by grep search&amp;quot; bound to &lt;code&gt;,fg&lt;/code&gt;, and those are probably the two most common tools I use in vim. Telescope looks great and works fast (I use &lt;a href=&quot;https://github.com/nvim-telescope/telescope-fzf-native.nvim&quot; class=&quot;external-link&quot;&gt;telescope-fzf-native&lt;/a&gt; for grep)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/olimorris/codecompanion.nvim&quot; class=&quot;external-link&quot;&gt;codecompanion&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;I added this in January last year, and it&#x27;s become the default way I interact with LLMs. It has generally very nice features for working with buffers in vim and handles communications with LLMs cleanly and simply. You don&#x27;t need Cursor et al to work with LLMs in your editor!
&lt;ul&gt;
&lt;li&gt;I do use &lt;a href=&quot;https://claude.com/product/claude-code&quot; class=&quot;external-link&quot;&gt;claude code&lt;/a&gt; for agentic missions, as I have not had much success with agentic mode in codecompanion - I use it more for smaller tasks while I&#x27;m coding, and it&#x27;s low-friction enough to make that pretty painless&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/sainnhe/sonokai&quot; class=&quot;external-link&quot;&gt;sonokai&lt;/a&gt; colorscheme
&lt;ul&gt;
&lt;li&gt;I use a &lt;a href=&quot;https://github.com/llimllib/personal_code/blob/master/homedir/.config/nvim/lua/colorscheme.lua&quot; class=&quot;external-link&quot;&gt;customized version&lt;/a&gt;, and it makes me happy.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Now that vim has native LSP support, I could honestly probably get away with just those plugins. Here&#x27;s a screenshot of what nvim looks like in a terminal window for me, with a telescope grep open:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;/images/Pasted image 20260103135829.png&quot;&gt;&lt;img class=&quot;bodyimg&quot; src=&quot;/images/Pasted image 20260103135829.png&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;terminal&quot;&gt;terminal&lt;/h2&gt;
&lt;p&gt;I use &lt;a href=&quot;/computer_usage/terminals/kitty.html&quot; class=&quot;internal-link&quot;&gt;kitty&lt;/a&gt; and my &lt;a href=&quot;https://github.com/llimllib/personal_code/tree/master/homedir/.config/kitty&quot; class=&quot;external-link&quot;&gt;config is here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I&#x27;d probably switch to &lt;a href=&quot;/computer_usage/terminals/ghostty.html&quot; class=&quot;internal-link&quot;&gt;ghostty&lt;/a&gt; if it &lt;a href=&quot;https://github.com/ghostty-org/ghostty/discussions/9546&quot; class=&quot;external-link&quot;&gt;supported opening hyperlinks in a terminal application&lt;/a&gt;, but it doesn&#x27;t.&lt;/p&gt;
&lt;p&gt;I use this feature constantly so I want to explain a bit about why I find it so valuable. A common workflow looks like this:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;I &lt;code&gt;cd&lt;/code&gt; to a project directory&lt;/li&gt;
&lt;li&gt;I either remember a file name or a string I can search for to find where I want to work
&lt;ul&gt;
&lt;li&gt;In the former case, I do &lt;code&gt;fd &amp;lt;filename&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;In the latter, I do &lt;code&gt;rg &amp;lt;string&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Then I click on the filename or line number to open that file or jump to that line number in a file&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Here&#x27;s a video demonstrating how it works:&lt;/p&gt;
&lt;p&gt;&lt;video controls&gt;&lt;source src=&quot;/images/Screen Recording 2026-01-03 at 2.07.35 PM.mov&quot; type=&quot;video/quicktime&quot; /&gt;&lt;a href=&quot;/images/Screen Recording 2026-01-03 at 2.07.35 PM.mov&quot;&gt;download&lt;/a&gt;&lt;/video&gt;&lt;/p&gt;
&lt;p&gt;The kitty actions are configured in &lt;a href=&quot;https://github.com/llimllib/personal_code/blob/master/homedir/.config/kitty/open-actions.conf&quot; class=&quot;external-link&quot;&gt;this file&lt;/a&gt; - the idea is that you connect a mime type or file extension to an action; in this case it&#x27;s &lt;code&gt;$EDITOR &amp;lt;filename&amp;gt;&lt;/code&gt; for a filename without a line number, and &lt;code&gt;$EDITOR +${FRAGMENT} &amp;lt;filename&amp;gt;&lt;/code&gt; if it has a line number.&lt;/p&gt;
&lt;h3 id=&quot;terminal-software&quot;&gt;terminal software&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;I love &lt;a href=&quot;/computer_usage/asdf/mise.html&quot; class=&quot;internal-link&quot;&gt;mise&lt;/a&gt; for managing versions of programming environments. I use it for node, terraform, go, python, etc etc
&lt;ul&gt;
&lt;li&gt;I have &lt;code&gt;mise.toml&lt;/code&gt; files in most of my projects which set important environment variables, so it has replaced &lt;a href=&quot;/computer_usage/cli_tips_and_tools/direnv.html&quot; class=&quot;internal-link&quot;&gt;direnv&lt;/a&gt; for me as well&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/computer_usage/cli_tips_and_tools/fd.html&quot; class=&quot;internal-link&quot;&gt;fd&lt;/a&gt; for finding files, a better &lt;a href=&quot;/computer_usage/cli_tips_and_tools/find.html&quot; class=&quot;internal-link&quot;&gt;find&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/computer_usage/cli_tips_and_tools/ripgrep.html&quot; class=&quot;internal-link&quot;&gt;ripgrep&lt;/a&gt; for grepping&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/programming/bash/atuin.html&quot; class=&quot;internal-link&quot;&gt;atuin&lt;/a&gt; for recording my command line history&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/programming/make/GNU_Make.html&quot; class=&quot;internal-link&quot;&gt;GNU Make&lt;/a&gt; and occasionally &lt;a href=&quot;/programming/task_runner/Just.html&quot; class=&quot;internal-link&quot;&gt;Just&lt;/a&gt; for running tasks
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;make&lt;/code&gt; is broken and annoying in old, predictable ways; but I know how it works and it&#x27;s available everywhere
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/llimllib/node-esbuild-executable/blob/main/Makefile&quot; class=&quot;external-link&quot;&gt;here&#x27;s an example makefile&lt;/a&gt; I made for a modern js project&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;just&lt;/code&gt; is modern in important ways but also doesn&#x27;t support output file targets, which is a feature I commonly use in &lt;code&gt;make&lt;/code&gt;; see the above file for an example&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/computer_usage/git/gh.html&quot; class=&quot;internal-link&quot;&gt;gh&lt;/a&gt; for interacting with github. I particularly use &lt;a href=&quot;https://github.com/llimllib/personal_code/blob/master/homedir/.zshrc#L303&quot; class=&quot;external-link&quot;&gt;my &lt;code&gt;pr&lt;/code&gt; alias&lt;/a&gt; for &lt;code&gt;gh pr create&lt;/code&gt; quite a lot to open pull requests&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/programming/json/jq.html&quot; class=&quot;internal-link&quot;&gt;jq&lt;/a&gt; for manipulating json&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/computer_usage/llm/llm.html&quot; class=&quot;internal-link&quot;&gt;llm&lt;/a&gt; for interacting with LLMs from the command line; see &lt;a href=&quot;/blog/2025/07/An_AI_tool_I_find_useful.html&quot; class=&quot;internal-link&quot;&gt;An AI tool I find useful&lt;/a&gt; for an example&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;browser&quot;&gt;Browser&lt;/h2&gt;
&lt;p&gt;I switched from &lt;a href=&quot;/computer_usage/browsers/Firefox.html&quot; class=&quot;internal-link&quot;&gt;Firefox&lt;/a&gt; to &lt;a href=&quot;/computer_usage/browsers/Orion.html&quot; class=&quot;internal-link&quot;&gt;Orion&lt;/a&gt; recently, mostly because I get the urge to switch browsers every six months or so when each of their annoyances accumulate.&lt;/p&gt;
&lt;p&gt;Orion definitely has bugs, and is slow in places, and Safari&#x27;s inspector isn&#x27;t nearly as nice as Firefox or Chrome. I wouldn&#x27;t recommend other people switch, even though I&#x27;m currently enjoying it.&lt;/p&gt;
&lt;h3 id=&quot;browser-plugins&quot;&gt;browser plugins&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;/computer_usage/password_managers/Bitwarden.html&quot; class=&quot;internal-link&quot;&gt;Bitwarden&lt;/a&gt; - I dunno, it&#x27;s fine, it mostly doesn&#x27;t annoy me&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/computer_usage/ad_blockers/uBlock_origin.html&quot; class=&quot;internal-link&quot;&gt;uBlock origin&lt;/a&gt; - I&#x27;m very happy with it as an ad blocker&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That&#x27;s it, I don&#x27;t use any more. Browser plugins have a terrible security story and should be avoided as much as possible.&lt;/p&gt;
&lt;h2 id=&quot;note-taking&quot;&gt;Note Taking&lt;/h2&gt;
&lt;p&gt;I use &lt;a href=&quot;/computer_usage/obsidian/Obsidian.html&quot; class=&quot;internal-link&quot;&gt;Obsidian&lt;/a&gt;, which I publish to the web with &lt;a href=&quot;https://github.com/llimllib/obsidian_notes&quot; class=&quot;external-link&quot;&gt;the code here&lt;/a&gt; (see &lt;a href=&quot;/computer_usage/obsidian/generating_HTML.html&quot; class=&quot;internal-link&quot;&gt;generating HTML&lt;/a&gt;))&lt;/p&gt;
&lt;p&gt;It&#x27;s not clear to me why I like using obsidian rather than just editing markdown files in vim, but I&#x27;m very happy with it.&lt;/p&gt;
&lt;h2 id=&quot;mail&quot;&gt;Mail&lt;/h2&gt;
&lt;p&gt;I use Apple&#x27;s Mail.app. It&#x27;s... fine enough I guess. I also occasionally use the fastmail web app, and it&#x27;s alright too.&lt;/p&gt;
&lt;p&gt;I am very happy with Fastmail as an email host, and glad I switched from gmail a decade or so ago.&lt;/p&gt;
&lt;h2 id=&quot;chat&quot;&gt;Chat&lt;/h2&gt;
&lt;p&gt;I use Slack for work chat and several friend group chats. I hate it even though it&#x27;s the best chat app I&#x27;ve ever used.&lt;/p&gt;
&lt;p&gt;I desperately want a chat app that doesn&#x27;t suck and isn&#x27;t beholden to Salesforce, but I hate Discord and IRC. I&#x27;ve made some minor attempts at replacing it with no success. It&#x27;s the piece of software I use day to day that I would most love to replace.&lt;/p&gt;
&lt;p&gt;I also use Messages.app for SMS and texts&lt;/p&gt;
&lt;h2 id=&quot;music&quot;&gt;Music&lt;/h2&gt;
&lt;p&gt;I switched in October from Spotify to Apple Music. I dislike Apple Music, but I also disliked Spotify ever since I switched from Rdio when it died.&lt;/p&gt;
&lt;p&gt;I&#x27;m still on the lookout for a good music listening app. Maybe I&#x27;ll try Qobuz or something? I don&#x27;t know.&lt;/p&gt;
&lt;p&gt;I&#x27;ve also used &lt;a href=&quot;/computer_usage/yt-dlp/yt-dlp.html&quot; class=&quot;internal-link&quot;&gt;yt-dlp&lt;/a&gt; to download a whole bunch of concerts and DJ sets from youtube (see &lt;a href=&quot;/music/youtube_concerts.html&quot; class=&quot;internal-link&quot;&gt;youtube concerts&lt;/a&gt; for a list of some of them) and I often listen to those.&lt;/p&gt;
&lt;h2 id=&quot;webcam&quot;&gt;Webcam&lt;/h2&gt;
&lt;p&gt;I have an old iPhone mounted to my desk, and use &lt;a href=&quot;/computer_usage/webcam/Reincubate_Camo.html&quot; class=&quot;internal-link&quot;&gt;Reincubate Camo&lt;/a&gt; to connect it to video apps.&lt;/p&gt;
&lt;p&gt;I occasionally use &lt;a href=&quot;/computer_usage/obs/OBS.html&quot; class=&quot;internal-link&quot;&gt;OBS&lt;/a&gt; to record a video or add a goofy overlay to video calls, but not that often.&lt;/p&gt;
&lt;h2 id=&quot;photos&quot;&gt;Photos&lt;/h2&gt;
&lt;p&gt;I use Adobe Lightroom to import photos from my Fuji X-T30 and Apple Photos to manage photos.&lt;/p&gt;
&lt;p&gt;With the demise of flickr, I really have no place to post my photos and I&#x27;ve considered adding something to my website but haven&#x27;t gotten it done.&lt;/p&gt;
&lt;h2 id=&quot;video&quot;&gt;Video&lt;/h2&gt;
&lt;p&gt;I use &lt;a href=&quot;/computer_usage/media_players/VLC.html&quot; class=&quot;internal-link&quot;&gt;vlc&lt;/a&gt; and &lt;a href=&quot;/computer_usage/media_players/IINA.html&quot; class=&quot;internal-link&quot;&gt;IINA&lt;/a&gt; for playing videos, and &lt;a href=&quot;/computer_usage/ffmpeg/ffmpeg.html&quot; class=&quot;internal-link&quot;&gt;ffmpeg&lt;/a&gt; for chopping them up from the command line&lt;/p&gt;
</content>
  </entry>
  <entry>
    <id>https://notes.billmill.org/blog/2025/12/2025_in_review.html</id>
    <link type="text/html" href="https://notes.billmill.org/blog/2025/12/2025_in_review.html" />
    <title>2025 in review</title>
    <published>2025-12-31T14:54:50.522Z</published>
    <updated>2025-12-31T14:54:50.522Z</updated>
    <content type="html">&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#coding&quot;&gt;coding&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#tools&quot;&gt;tools&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#games&quot;&gt;games&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#travel&quot;&gt;travel&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#visualization&quot;&gt;visualization&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;coding&quot;&gt;coding&lt;/h2&gt;
&lt;p&gt;I didn&#x27;t do that much work in the open this year, unfortunately. I made a few small tools and polished my workflow scripts quite a bit though:&lt;/p&gt;
&lt;h3 id=&quot;review&quot;&gt;review&lt;/h3&gt;
&lt;p&gt;I use my &lt;a href=&quot;https://github.com/llimllib/personal_code/blob/master/homedir/.local/bin/review&quot; class=&quot;external-link&quot;&gt;review script&lt;/a&gt; to get feedback from an LLM on a pull request before I submit it, or to review other people&#x27;s PRs. I like this a lot more than tools that annotate a PR in github, because it lets me read it locally and privately, and decide for myself which of its suggestions are valuable.&lt;/p&gt;
&lt;p&gt;As I &lt;a href=&quot;https://notes.billmill.org/blog/2025/07/An_AI_tool_I_find_useful.html&quot; class=&quot;external-link&quot;&gt;wrote when I released the tool&lt;/a&gt;, I find that &lt;em&gt;most&lt;/em&gt; of its suggestions are not useful, but having a list of suggestions to evaluate has proved a very valuable resource for me and has prevented me from making quite a few obvious errors.&lt;/p&gt;
&lt;h3 id=&quot;worktree&quot;&gt;worktree&lt;/h3&gt;
&lt;p&gt;My &lt;a href=&quot;https://github.com/llimllib/personal_code/blob/master/homedir/.local/bin/worktree&quot; class=&quot;external-link&quot;&gt;worktree script&lt;/a&gt; helps me &lt;a href=&quot;https://notes.billmill.org/blog/2024/03/How_I_use_git_worktrees.html&quot; class=&quot;external-link&quot;&gt;use git worktrees&lt;/a&gt; in a large node.js monorepo at work without so much pain. This year I:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;added submodule support&lt;/li&gt;
&lt;li&gt;added support for github fork notation (&lt;code&gt;user/repository&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;added the ability to store its config in git global config&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I did play with &lt;a href=&quot;https://notes.billmill.org/computer_usage/jujutsu/notes_on_giving_jj_a_try.html&quot; class=&quot;external-link&quot;&gt;&lt;code&gt;jj&lt;/code&gt; a bit this year&lt;/a&gt;, but I&#x27;m so used to git that it&#x27;s not a pain point for me and it seems like a waste of time, though I remain interested in it.&lt;/p&gt;
&lt;h3 id=&quot;time&quot;&gt;time&lt;/h3&gt;
&lt;p&gt;My favorite &lt;a href=&quot;https://github.com/llimllib/personal_code/blob/master/homedir/.local/bin/%2Ctime&quot; class=&quot;external-link&quot;&gt;tiny script&lt;/a&gt; of the year is my &lt;code&gt;time&lt;/code&gt; script, which I wrote because I often have to compare times in different timezones with different time formats. For example, sometimes I&#x27;m reading timestamps from a log message in milliseconds since the epoch, and I need to know when that was both in my local time and in UTC.&lt;/p&gt;
&lt;p&gt;It has two functions:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;when called without arguments, prints out the current time in a variety of formats, useful for comparing time between timezones or formats:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-console&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$ &lt;/span&gt;,time
&lt;span class=&quot;go&quot;&gt;ts seconds: 1767193930.0&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;ts ms:      1767193930000&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Local:      2025-12-31 10:12:10 EST&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;UTC:        2025-12-31 15:12:10 UTC&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;if passed an argument, will do its best to parse the time and display it in those same formats:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-console&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$ &lt;/span&gt;,time&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1756082819&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;ts seconds: 1756082819.0&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;ts ms:      1756082819000&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Local:      2025-08-24 20:46:59 EDT&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;UTC:        2025-08-25 00:46:59 UTC&lt;/span&gt;

&lt;span class=&quot;gp&quot;&gt;$ &lt;/span&gt;,time&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;Dec&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;m&quot;&gt;31&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;m&quot;&gt;2024&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;ts seconds: 1735621200.0&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;ts ms:      1735621200000&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Local:      2024-12-31 00:00:00 EST&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;UTC:        2024-12-31 05:00:00 UTC&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;gp&quot;&gt;gp&lt;/h3&gt;
&lt;p&gt;I have the rights to push to the main branch of a bunch of git repositories, and I found myself occasionally pushing directly to main by accident instead of creating a branch.&lt;/p&gt;
&lt;p&gt;To solve that problem, I replaced my &lt;code&gt;git push&lt;/code&gt; alias with &lt;a href=&quot;https://github.com/llimllib/personal_code/blob/master/homedir/.local/bin/gp&quot; class=&quot;external-link&quot;&gt;a script&lt;/a&gt; that checks if I&#x27;m pushing to a main branch by accident, and displays a message to the console with the ability to do the push if it was indeed intentional.&lt;/p&gt;
&lt;h2 id=&quot;tools&quot;&gt;tools&lt;/h2&gt;
&lt;p&gt;I still spend most of my life in neovim in the terminal, and have no desire to change either of those.&lt;/p&gt;
&lt;p&gt;I&#x27;d like to switch to &lt;a href=&quot;https://ghostty.org&quot; class=&quot;external-link&quot;&gt;ghostty&lt;/a&gt;, but it doesn&#x27;t support opening hyperlinks in terminal applications (&lt;a href=&quot;https://github.com/ghostty-org/ghostty/discussions/9546&quot; class=&quot;external-link&quot;&gt;github discussion&lt;/a&gt;). I find this workflow incredibly useful and don&#x27;t want to live without it, so I&#x27;m still using &lt;a href=&quot;https://sw.kovidgoyal.net/kitty/&quot; class=&quot;external-link&quot;&gt;kitty&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I&#x27;ve been using Obsidian to make this website, and remain very happy with it.&lt;/p&gt;
&lt;p&gt;I&#x27;ve updated the &lt;a href=&quot;https://github.com/llimllib/obsidian_notes/blob/main/run.py&quot; class=&quot;external-link&quot;&gt;script that builds the website&lt;/a&gt; in minor ways throughout the year. It&#x27;s slow and I dislike the &lt;a href=&quot;https://pypi.org/project/markdown-it-py/&quot; class=&quot;external-link&quot;&gt;markdown library it uses&lt;/a&gt;, but it also gets the job done and I don&#x27;t have to think about it.&lt;/p&gt;
&lt;div class=&quot;admon-tip&quot;&gt;&lt;p class=&quot;tip-title&quot;&gt;I added hacky callout support&lt;/p&gt;&lt;p class=&quot;tip-content&quot;&gt;But it was hacky and painful to do, so it goes&lt;/p&gt;&lt;/div&gt;
&lt;p&gt;I switched from Firefox to &lt;a href=&quot;https://orionbrowser.com&quot; class=&quot;external-link&quot;&gt;Orion&lt;/a&gt; browser, which is working well enough though it definitely has some bugs. I tend to switch browsers every six months as I accumulate dissatisfactions with all of them.&lt;/p&gt;
&lt;h2 id=&quot;games&quot;&gt;games&lt;/h2&gt;
&lt;p&gt;For a long time, I&#x27;ve vaguely wanted to play games that are Windows-only, but I also refuse to install Windows and I don&#x27;t have a Linux gaming computer set up. A couple weeks ago, I finally bit the bullet and purchased &lt;a href=&quot;https://www.codeweavers.com&quot; class=&quot;external-link&quot;&gt;CrossOver&lt;/a&gt; and have started playing a few games that are Windows-only.&lt;/p&gt;
&lt;p&gt;Some games I or my kids enjoyed this year:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;We all enjoyed &lt;a href=&quot;https://www.metanetsoftware.com/games/nplusplus&quot; class=&quot;external-link&quot;&gt;N++&lt;/a&gt;, a platformer stripped down to the absolute basics. I remember playing &lt;a href=&quot;https://www.metanetsoftware.com/games/n&quot; class=&quot;external-link&quot;&gt;n&lt;/a&gt; as a flash game so many years ago, and its older sibling is still a blast.&lt;/li&gt;
&lt;li&gt;I enjoyed playing &lt;a href=&quot;https://www.animalwell.net&quot; class=&quot;external-link&quot;&gt;Animal Well&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Because &lt;a href=&quot;https://hollowknightsilksong.com&quot; class=&quot;external-link&quot;&gt;Silksong&lt;/a&gt; came out this year, I went back and tried to finish &lt;a href=&quot;https://www.hollowknight.com&quot; class=&quot;external-link&quot;&gt;Hollow Knight&lt;/a&gt;. I managed to get to the last boss, but couldn&#x27;t beat him, and didn&#x27;t end up buying Siksong. Maybe next year&lt;/li&gt;
&lt;li&gt;My older son really enjoyed &lt;a href=&quot;https://www.ballxpit.com&quot; class=&quot;external-link&quot;&gt;Ball x Pit&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;My younger son got old enough to play &lt;a href=&quot;https://www.nintendo.com/us/store/products/the-legend-of-zelda-tears-of-the-kingdom-switch/&quot; class=&quot;external-link&quot;&gt;Tears of the Kingdom&lt;/a&gt; for himself, and enjoys coming up with silly weapon combinations more than actually playing the story, which is fun&lt;/li&gt;
&lt;li&gt;We had fun playing some &lt;a href=&quot;https://subsetgames.com/ftl.html&quot; class=&quot;external-link&quot;&gt;FTL&lt;/a&gt; runs together&lt;/li&gt;
&lt;li&gt;We all had fun with &lt;a href=&quot;https://deadcells.com&quot; class=&quot;external-link&quot;&gt;Dead Cells&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;travel&quot;&gt;travel&lt;/h2&gt;
&lt;p&gt;Working for a &lt;a href=&quot;https://readme.com&quot; class=&quot;external-link&quot;&gt;distributed company&lt;/a&gt; means traveling for meetups, and we had them in particularly fun locations this year. I was fortunate to travel to Colorado, Montana, and Mexico for work this year.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;/images/0698C573-F6A1-4854-9697-CB3AC2CCB32B_1_105_c.jpeg&quot;&gt;&lt;img class=&quot;bodyimg&quot; src=&quot;/images/0698C573-F6A1-4854-9697-CB3AC2CCB32B_1_105_c.jpeg&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;/images/58CEAC05-38D6-4FC5-B259-72794BB0932D_1_105_c.jpeg&quot;&gt;&lt;img class=&quot;bodyimg&quot; src=&quot;/images/58CEAC05-38D6-4FC5-B259-72794BB0932D_1_105_c.jpeg&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;/images/E9BBB1DE-DF26-4BE3-9B67-DC56400E5492_1_105_c.jpeg&quot;&gt;&lt;img class=&quot;bodyimg&quot; src=&quot;/images/E9BBB1DE-DF26-4BE3-9B67-DC56400E5492_1_105_c.jpeg&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;My family and I spent two weeks in Lecco, Italy, which was gorgeous. I&#x27;d never been to Italy before and it was great&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;/images/6ED76249-39D2-496A-BBD1-5551659B3E9B_1_105_c.jpeg&quot;&gt;&lt;img class=&quot;bodyimg&quot; src=&quot;/images/6ED76249-39D2-496A-BBD1-5551659B3E9B_1_105_c.jpeg&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;visualization&quot;&gt;visualization&lt;/h2&gt;
&lt;p&gt;I continued toying around with different visualizations of NBA data this year. My son asked me the other day if it was work, and I told him that I make graphs in the same way that other guys build models or go fishing&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;I kept up my &lt;a href=&quot;https://github.com/llimllib/nba_data&quot; class=&quot;external-link&quot;&gt;nba_data&lt;/a&gt; archive, and added a few data sources to it. I particularly enjoy Dean Oliver&#x27;s &lt;a href=&quot;https://espnanalytics.com/nba-daily-summary?date=20251230&quot; class=&quot;external-link&quot;&gt;analytics data&lt;/a&gt;, and have found some good uses for it&lt;/li&gt;
&lt;li&gt;Lately I&#x27;ve been playing around with tables and trying to make them especially expressive; &lt;a href=&quot;https://billmill.org/nba/table&quot; class=&quot;external-link&quot;&gt;this one&lt;/a&gt; has a bar graph background that changes with the column you&#x27;ve sorted by, and has interactive filtering:&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a href=&quot;/images/Pasted image 20251231111825.png&quot;&gt;&lt;img class=&quot;bodyimg&quot; src=&quot;/images/Pasted image 20251231111825.png&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Due to a &lt;a href=&quot;https://github.com/orgs/community/discussions/178318&quot; class=&quot;external-link&quot;&gt;particularly irritating github pages bug&lt;/a&gt;, I moved my observable notebook off of github pages and over to &lt;a href=&quot;https://billmill.org/nba&quot; class=&quot;external-link&quot;&gt;https://billmill.org/nba&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;I post about this work on my &lt;a href=&quot;https://bsky.app/profile/billmill.org&quot; class=&quot;external-link&quot;&gt;bluesky account&lt;/a&gt;, and occasionally post a graph or progress on other work&lt;/li&gt;
&lt;/ul&gt;
</content>
  </entry>
  <entry>
    <id>https://notes.billmill.org/blog/2025/12/Advent_of_Code_2025.html</id>
    <link type="text/html" href="https://notes.billmill.org/blog/2025/12/Advent_of_Code_2025.html" />
    <title>Advent of Code 2025</title>
    <published>2025-12-03T00:43:06.843Z</published>
    <updated>2025-12-03T00:43:06.843Z</updated>
    <content type="html">&lt;p&gt;This year I&#x27;ve decided to do Advent of Code in &lt;a href=&quot;/programming/gleam/gleam.html&quot; class=&quot;internal-link&quot;&gt;Gleam&lt;/a&gt;, a programming language I know nothing about and have never written a line of code in.&lt;/p&gt;
&lt;p&gt;Previously: &lt;a href=&quot;/blog/2024/12/Advent_of_Code_2024.html&quot; class=&quot;internal-link&quot;&gt;Advent of Code 2024&lt;/a&gt;, &lt;a href=&quot;/programming/advent_of_code/2023_problem_log.html&quot; class=&quot;internal-link&quot;&gt;2023 problem log&lt;/a&gt;, and incomplete answers back to 2015 &lt;a href=&quot;https://github.com/llimllib/personal_code/tree/master/misc/advent/&quot; class=&quot;external-link&quot;&gt;in github&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;/blog/2025/12/Advent_of_Code_2025.html#day-1&quot; class=&quot;internal-link&quot;&gt;day 1&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/blog/2025/12/Advent_of_Code_2025.html#day-2&quot; class=&quot;internal-link&quot;&gt;day 2&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;day-1&quot;&gt;Day 1&lt;/h2&gt;
&lt;p&gt;Not the easiest day 1! The problem centered around a &lt;a href=&quot;https://en.wikipedia.org/wiki/Ring_(mathematics)&quot; class=&quot;external-link&quot;&gt;ring&lt;/a&gt; of the numbers 0-99.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;My answer for both parts is &lt;a href=&quot;https://github.com/llimllib/personal_code/blob/master/misc/advent/2025/advent/src/day01/a.gleam&quot; class=&quot;external-link&quot;&gt;here&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;I struggled with coming up with a simple way to express the crossings of zero for the second part, and I&#x27;m not super happy with how that came out&lt;/li&gt;
&lt;li&gt;You need a library to read files in gleam! &lt;code&gt;gleam add simplifile&lt;/code&gt; to get the standard sync library it seems&lt;/li&gt;
&lt;li&gt;&lt;code&gt;echo&lt;/code&gt; is super handy for debugging pipelines - just stick it between &lt;code&gt;|&amp;gt;&lt;/code&gt; braces and it will show you what&#x27;s going on&lt;/li&gt;
&lt;li&gt;I ought to figure out a better way to handle errors than just &lt;code&gt;unwrap&lt;/code&gt;ing them and picking a random default value, it&#x27;s definitely going to bite me eventually if I don&#x27;t panic. I&#x27;m surprised there&#x27;s not an &lt;code&gt;unwrap_or_panic&lt;/code&gt; or something like that! Maybe I&#x27;ll write it as &lt;code&gt;must&lt;/code&gt; or something, but for today I just got by&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;day-2&quot;&gt;Day 2&lt;/h2&gt;
&lt;p&gt;It seems that the creator of gleam doesn&#x27;t like tuples, so today I tried not to use any. I started with a custom type to represent a range:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-gleam&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;pub&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Range&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
&lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Range&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For part 1, I used a generative approach where I iterated from the start of each range through the end, and checked if the number was of the form &lt;code&gt;&amp;lt;x&amp;gt;&amp;lt;x&amp;gt;&lt;/code&gt;. The heart of it is this function:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-gleam&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;pub&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;fn&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;acc_dubs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;front&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Range&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;acc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
&lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dub&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;
&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;to_string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;front&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;to_string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;front&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;|&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;parse&lt;/span&gt;
&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;|&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;unwrap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;case&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dub&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dub&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;acc_dubs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;front&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dub&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;acc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;False&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;acc&lt;/span&gt;
&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;False&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;acc_dubs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;front&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;acc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;False&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;False&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;panic&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;as&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;impossible&amp;quot;&lt;/span&gt;
&lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For part 2, I got in loops thinking about how to generate all possible combinations of numbers, so I instead took a filtering approach. This function checks if a number is symmetric in the way the problem states (assuming that there are no numbers with more than 10 digits):&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-gleam&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;pub&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;fn&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;is_sym&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
&lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ns&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;|&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;to_string&lt;/span&gt;
&lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;l&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ns&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;|&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;length&lt;/span&gt;
&lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;any&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
&lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;atoi&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;repeat&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;slice&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ns&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;max&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;l&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))),&lt;/span&gt;
&lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;atoi&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;repeat&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;slice&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ns&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;max&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;l&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))),&lt;/span&gt;
&lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;atoi&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;repeat&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;slice&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ns&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;max&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;l&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))),&lt;/span&gt;
&lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;atoi&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;repeat&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;slice&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ns&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;max&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;l&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))),&lt;/span&gt;
&lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;atoi&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;repeat&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;slice&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ns&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;max&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;l&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))),&lt;/span&gt;
&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;fn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;==&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;9&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
&lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I find that pleasing!&lt;/p&gt;
&lt;p&gt;Then I iterated over every number in each range and checked it for symmetry, and summed the result:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-gleam&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;pub&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;fn&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;part_b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
&lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ranges&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;parse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fold&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;flat_map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ranges&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;fn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;acc_nubs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[])&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}),&lt;/span&gt;
&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;pub&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;fn&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;acc_nubs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;acc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
&lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;case&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;is_sym&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;acc&lt;/span&gt;
&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;acc_nubs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;acc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;False&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;acc_nubs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;acc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I didn&#x27;t do any better at handling errors today! I think I will make that an emphasis tomorrow.&lt;/p&gt;
&lt;p&gt;Custom types were really simple, and I haven&#x27;t yet hit any big gleam roadblocks. It takes me a minute to think of how to phrase problems recursively, but I like that it kind of forces you to break your code into small, clear functions.&lt;/p&gt;
&lt;p&gt;I wish there were better inline testing functionality in gleam, I don&#x27;t want to put my tests out into a test directory. I&#x27;m kind of annoyed generally by how picky it is about the project structure - but I also see how it fits into the zen of the language, which minimizes your options to keep complexity under control.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <id>https://notes.billmill.org/blog/2025/11/Licensing_will_not_save_us.html</id>
    <link type="text/html" href="https://notes.billmill.org/blog/2025/11/Licensing_will_not_save_us.html" />
    <title>Licensing will not save us</title>
    <published>2025-11-16T13:33:25.613Z</published>
    <updated>2025-11-16T13:33:25.613Z</updated>
    <content type="html">&lt;p&gt;I enjoy &lt;a href=&quot;https://blog.muni.town/open-source-power/&quot; class=&quot;external-link&quot;&gt;this piece&lt;/a&gt; by Erlend Sogge Heggen which argues that we, the open source developers, ought not to freely give away our work because it advantages the capitalists and fascists that are leveraging the fruits of our labor to make untold millions for themselves without giving back to the community they&#x27;re building off or the world at large.&lt;/p&gt;
&lt;p&gt;I&#x27;ve been using the computer long enough to remember how hard it was to get your hands on software that was interesting and useful. Without open source software, I&#x27;m certain that computing would not have progressed as far as it has, and that I and many others would not have the careers we enjoy because we wouldn&#x27;t have found a way in.&lt;/p&gt;
&lt;p&gt;The class of business leaders who have built on open source software (and who often started as developers themselves) has taken a heavy toll on the world without returning the value they owe, but I also fear returning to a world where a privileged class of people has access to the source code for every important application, and interested people have to choose whether to break the law to satisfy their curiosity.&lt;/p&gt;
&lt;p&gt;More and more developers are playing around with licensing to try and defend themselves from the predatory practices of the tech elite, and I&#x27;m here for it - but that cannot be the whole solution.&lt;/p&gt;
&lt;p&gt;Only organization and community can protect the fruits of OSS (or a future OSS-like?) labor from explotation. It can&#x27;t be one community, because there are many different aims, cultures, and viewpoints, but there can be many interlinked communities sharing tools, knowledge and practice.&lt;/p&gt;
&lt;p&gt;The sooner we start building the practices, habits and (less importantly) software to make it easy to build, maintain, and own communities of practice, the sooner we will make it possible to share the fruits of our labor in a less-destructive manner. Licensing alone won&#x27;t save us, we need to build stable social organizations and learn how to empower them.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <id>https://notes.billmill.org/blog/2025/09/Writing_good_technical_articles_is_hard__we_should_do_more_of_it_anyway.html</id>
    <link type="text/html" href="https://notes.billmill.org/blog/2025/09/Writing_good_technical_articles_is_hard__we_should_do_more_of_it_anyway.html" />
    <title>Writing good technical articles is hard, we should do more of it anyway</title>
    <published>2025-09-24T10:55:59.621Z</published>
    <updated>2025-09-24T10:55:59.621Z</updated>
    <content type="html">&lt;p&gt;An &lt;a href=&quot;https://anniemueller.com/posts/how-i-a-non-developer-read-the-tutorial-you-a-developer-wrote-for-me-a-beginner&quot; class=&quot;external-link&quot;&gt;article&lt;/a&gt; is making the rounds about what it feels like to read a technical article when you don&#x27;t understand the terminology&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Why do people feel OK putting a partially-assed project on github, but have a much higher bar for when they release a technical article?&lt;/li&gt;
&lt;li&gt;It&#x27;s &lt;em&gt;fractally&lt;/em&gt; difficult to write technical tutorials and documentation
&lt;ul&gt;
&lt;li&gt;Everybody comes to it with a different skillset&lt;/li&gt;
&lt;li&gt;You&#x27;re writing for &lt;em&gt;everybody in the world&lt;/em&gt; speaking every language&lt;/li&gt;
&lt;li&gt;Every single thing you write about&lt;/li&gt;
&lt;li&gt;Computers are so new that there&#x27;s very little common baseline for everyone&lt;/li&gt;
&lt;li&gt;we don&#x27;t even know what the fields are yet!&lt;/li&gt;
&lt;li&gt;There&#x27;s little-to-no feedback&lt;/li&gt;
&lt;li&gt;People feel very free to give harsh criticism
&lt;ul&gt;
&lt;li&gt;That leaves only the thickest-skinned authors able to write&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;The worst part for me is the complete lack of feedback
&lt;ul&gt;
&lt;li&gt;You chuck an article out into the ether, and either you hear &lt;em&gt;nothing&lt;/em&gt; or the feedback you get ranges from mildly critical to harshly critical&lt;/li&gt;
&lt;li&gt;There&#x27;s neither reason nor incentive to leave positive things about it. Who cares that you enjoyed the article&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;h2 id=&quot;lets-try-to-create-a-world-of-constructive-dialogue&quot;&gt;Let&#x27;s try to create a world of constructive dialogue&lt;/h2&gt;
&lt;/li&gt;
&lt;/ul&gt;
</content>
  </entry>
  <entry>
    <id>https://notes.billmill.org/blog/2025/07/An_AI_tool_I_find_useful.html</id>
    <link type="text/html" href="https://notes.billmill.org/blog/2025/07/An_AI_tool_I_find_useful.html" />
    <title>An AI tool I find useful</title>
    <published>2025-07-27T14:31:07.68Z</published>
    <updated>2025-07-28T19:22:46.652Z</updated>
    <content type="html">&lt;p&gt;One of the tasks that I do most often is to review code. I&#x27;ve written a &lt;a href=&quot;https://github.com/llimllib/personal_code/blob/032c597d4c1f805a0fb6030723e22fcf4349b2ef/homedir/.local/bin/review&quot; class=&quot;external-link&quot;&gt;review command&lt;/a&gt; that asks an AI to review a code sample, and I&#x27;ve gotten a lot of value out of it.&lt;/p&gt;
&lt;p&gt;I ignore &lt;em&gt;most&lt;/em&gt; of the suggestions that the tool outputs, but it has already saved me often enough from painful errors that I wanted to share it in the hope that others might find it useful.&lt;/p&gt;
&lt;h2 id=&quot;how-to-install-it&quot;&gt;How to install it&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;install &lt;a href=&quot;https://github.com/simonw/llm&quot; class=&quot;external-link&quot;&gt;llm&lt;/a&gt; and configure it to use the provider and model you&#x27;d like to use
&lt;ul&gt;
&lt;li&gt;on a mac, &lt;code&gt;brew install llm&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;optionally install &lt;a href=&quot;https://github.com/sharkdp/bat&quot; class=&quot;external-link&quot;&gt;bat&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;on a mac, &lt;code&gt;brew install bat&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;save the &lt;a href=&quot;https://github.com/llimllib/personal_code/blob/c85d6b2106e1369339d045e8be0e43484149848e/homedir/.local/bin/review&quot; class=&quot;external-link&quot;&gt;review script&lt;/a&gt; anywhere on your path and make it executable&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;how-it-works&quot;&gt;How it works&lt;/h2&gt;
&lt;p&gt;The main job of the script is to generate context from a git diff and pass it to &lt;a href=&quot;https://github.com/simonw/llm&quot; class=&quot;external-link&quot;&gt;llm&lt;/a&gt; for code review.&lt;/p&gt;
&lt;p&gt;If you run &lt;code&gt;review&lt;/code&gt; with no arguments, it will:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;run &lt;code&gt;git diff -U10&lt;/code&gt;
&lt;ul&gt;
&lt;li&gt;the &lt;code&gt;-U&lt;/code&gt; argument changes the amount of context given in a git diff&lt;/li&gt;
&lt;li&gt;any additional arguments you pass will be forwarded directly to &lt;code&gt;git diff&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;estimate the tokens in the output and check if that fits within the context window
&lt;ul&gt;
&lt;li&gt;shrink the context window if necessary and re-run &lt;code&gt;git diff&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;if you want to manually expand the context beyond 10 lines
&lt;ul&gt;
&lt;li&gt;you can append to the system prompt with the &lt;code&gt;--context&lt;/code&gt; flag&lt;/li&gt;
&lt;li&gt;you can expand the diff context to 100 by passing &lt;code&gt;-U100&lt;/code&gt; or any number you prefer&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;pipe the context to &lt;code&gt;llm&lt;/code&gt;
&lt;ul&gt;
&lt;li&gt;You can configure &lt;code&gt;llm&lt;/code&gt; to use whichever model or AI company you prefer&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;highlight the output with &lt;code&gt;bat&lt;/code&gt; if available
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/sharkdp/bat&quot; class=&quot;external-link&quot;&gt;bat&lt;/a&gt; is great and I highly recommend using it if you don&#x27;t already. &lt;code&gt;brew install bat&lt;/code&gt; on a mac will install it&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The result looks like this in my terminal:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;/images/Pasted image 20250727105305.png&quot;&gt;&lt;img class=&quot;bodyimg&quot; src=&quot;/images/Pasted image 20250727105305.png&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;how-i-use-it&quot;&gt;How I use it&lt;/h2&gt;
&lt;p&gt;My main use of the command is to review a PR I&#x27;m preparing before I file it. The &lt;strong&gt;biggest value&lt;/strong&gt; I&#x27;ve gotten out of it is that it frequently catches embarrassing errors before I file a PR - misspellings, &lt;code&gt;DELETEME&lt;/code&gt;s I forgot to remove, and occasionally logic errors.&lt;/p&gt;
&lt;p&gt;It also often suggests features that make sense to add before finishing the PR, or as next steps.&lt;/p&gt;
&lt;p&gt;It is very important to &lt;strong&gt;use it intelligently&lt;/strong&gt;! The LLM is just an LLM, and it also may be missing context. The screenshot above has two examples of mistaken suggestions that I read and ignored; you have to apply your own understanding and taste to its output.&lt;/p&gt;
&lt;p&gt;Keep in mind that it is tasked via its system prompt with finding problems and making suggestions; no matter how good your code is it will try to find and suggest something.&lt;/p&gt;
&lt;p&gt;I also use it for reviewing other people&#x27;s PRs, with &lt;code&gt;review origin/main origin/some-feature-branch&lt;/code&gt;. In these cases, I really am just using it for clues as to some things that I may need to investigate with my actual human brain. Please &lt;strong&gt;do not just dump llm suggestions into a PR&lt;/strong&gt;! That&#x27;s both rude and likely to be unhelpful.&lt;/p&gt;
&lt;h2 id=&quot;how-it-differs&quot;&gt;How it differs&lt;/h2&gt;
&lt;p&gt;That last point brings me to why I prefer this tool to github&#x27;s own copilot review tool.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;I can use my &lt;code&gt;review&lt;/code&gt; tool in private, and evaluate its suggestions in private&lt;/li&gt;
&lt;li&gt;I can run it repeatedly as I change the code&lt;/li&gt;
&lt;li&gt;The separation of my terminal from the code review tool provides a space for me to apply critical thinking&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;areas-for-improvement&quot;&gt;Areas for improvement&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;A lot of things are hard-coded into the script, because I&#x27;m its only user
&lt;ul&gt;
&lt;li&gt;If you find use in it, please let me know!&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;the system prompt seems to work fine, but the range of possible system prompts is so large that I&#x27;m sure it could be better&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;postscript&quot;&gt;Postscript&lt;/h2&gt;
&lt;p&gt;Thanks to &lt;a href=&quot;https://lobste.rs/c/mx0moq&quot; class=&quot;external-link&quot;&gt;a suggestion&lt;/a&gt; on &lt;a href=&quot;http://lobste.rs&quot; class=&quot;external-link&quot;&gt;lobste.rs&lt;/a&gt; from &lt;code&gt;davidcrespo&lt;/code&gt;, I &lt;a href=&quot;https://github.com/llimllib/personal_code/commit/032c597d4c1f805a0fb6030723e22fcf4349b2ef&quot; class=&quot;external-link&quot;&gt;added the ability&lt;/a&gt; to provide context via stdin. Thanks for the suggestion!&lt;/p&gt;
</content>
  </entry>
  <entry>
    <id>https://notes.billmill.org/blog/2025/07/A_pretty_decent_retry__and_not_a_library.html</id>
    <link type="text/html" href="https://notes.billmill.org/blog/2025/07/A_pretty_decent_retry__and_not_a_library.html" />
    <title>A pretty decent retry, and not a library</title>
    <published>2025-07-19T15:14:41.434Z</published>
    <updated>2025-07-21T15:12:06.849Z</updated>
    <content type="html">&lt;p&gt;I&#x27;m pretty happy with the &lt;a href=&quot;https://github.com/llimllib/nba_data/blob/dbbed16c1c05a118b98e92da099267f3ffb7a8cd/src/stats.py#L37-L72&quot; class=&quot;external-link&quot;&gt;retry function&lt;/a&gt; I&#x27;ve ended up with for my &lt;a href=&quot;https://github.com/llimllib/nba_data/&quot; class=&quot;external-link&quot;&gt;nba stats downloader&lt;/a&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;G&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TypeVar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;G&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;retry&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Callable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;G&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;**&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;kwargs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;G&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;sd&quot;&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class=&quot;sd&quot;&gt;    Retry a function call with backoff and print out the exceptions raised&lt;/span&gt;
&lt;span class=&quot;sd&quot;&gt;    unless they are a timeout.&lt;/span&gt;
&lt;span class=&quot;sd&quot;&gt;    &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;**&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;kwargs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;except&lt;/span&gt; &lt;span class=&quot;ne&quot;&gt;Exception&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;11&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;raise&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RetryLimitExceeded&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;vm&quot;&gt;__name__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;kwargs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;timeout&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;15&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;20&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;25&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;25&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;25&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;50&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;50&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
            &lt;span class=&quot;nb&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sa&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;failed &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;vm&quot;&gt;__name__&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;kwargs&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;), sleeping &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;timeout&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;exc&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;# print the traceback unless it&amp;#39;s a timeout&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;isinstance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;exc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ReadTimeout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ReadTimeoutError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ne&quot;&gt;TimeoutError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)):&lt;/span&gt;
                &lt;span class=&quot;nb&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;traceback&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;format_exc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;sleep&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;timeout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# The LSP doesn&amp;#39;t detect that this is unreachable: appease it&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;raise&lt;/span&gt; &lt;span class=&quot;ne&quot;&gt;Exception&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;this should never happen&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;be-explicit&quot;&gt;Be explicit&lt;/h2&gt;
&lt;p&gt;One thing I like about it is that it doesn&#x27;t calculate a backoff value - it picks from an explicit list of backoffs.&lt;/p&gt;
&lt;p&gt;Before I wrote this one, I had written a bunch of retry functions and always tried to come up with some clever exponential algorithm and do a bit of math. One day I was working with backoffs for my day job in a database context and I decided to go look at &lt;a href=&quot;https://github.com/sqlite/sqlite/blob/1f436ad563c4f16b94060a1982ae41abac015053/src/main.c#L1709-L1737&quot; class=&quot;external-link&quot;&gt;what sqlite uses&lt;/a&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;static&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;u8&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;delays&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;15&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;20&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;25&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;25&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;25&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;50&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;50&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;delay&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;delays&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// ed: That&amp;#39;s &amp;quot;sqlite3 OS sleep&amp;quot;, not &amp;quot;sqlite 30s sleep&amp;quot;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;sqlite3OsSleep&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pVfs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;delay&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It&#x27;s pleasantly &lt;a href=&quot;https://grugbrain.dev&quot; class=&quot;external-link&quot;&gt;grug-brained&lt;/a&gt;; why use clever code when simple code do trick?&lt;/p&gt;
&lt;p&gt;So I stole the idea and I use it whenever I need a retry function now.&lt;/p&gt;
&lt;h2 id=&quot;dont-be-generic&quot;&gt;Don&#x27;t be generic&lt;/h2&gt;
&lt;p&gt;The retry function does not allow you to specify your timeouts or configure the list of exceptions that don&#x27;t get tracebacks or configure the message that gets thrown.&lt;/p&gt;
&lt;p&gt;It doesn&#x27;t support regular args because I only need kwargs in the context I&#x27;m using it in.&lt;/p&gt;
&lt;p&gt;If I needed to use it again, I&#x27;d probably have to modify one or both of those choices. That&#x27;s OK!&lt;/p&gt;
&lt;p&gt;This leads to:&lt;/p&gt;
&lt;h2 id=&quot;dont-create-a-library-for-small-stuff&quot;&gt;Don&#x27;t create a library for small stuff&lt;/h2&gt;
&lt;p&gt;One instinct for a free software developer, when they have a pleasant battle-tested function, is to make it into a library. The Javascript ecosystem is full of libraries this small or smaller - I could go look and I&#x27;m sure I&#x27;d find seventeen similar examples of retry libraries.&lt;/p&gt;
&lt;p&gt;It feels nice, like you&#x27;re giving back to the community that you&#x27;ve drawn value from.&lt;/p&gt;
&lt;p&gt;But it&#x27;s a mirage!&lt;/p&gt;
&lt;p&gt;The value of having a small function like this in your codebase, where you can see it, read it, and tailor it to suit your needs is greater than the value of having a highly configurable library that&#x27;s difficult to read and debug.&lt;/p&gt;
&lt;p&gt;As the code gets more complex, the weight of that complexity can make it such that it starts to make sense to isolate the code into a library and share it.&lt;/p&gt;
&lt;p&gt;I&#x27;d like to advocate that people usually underweight how much value there is in having the code included in your application by tailoring it to your application exactly rather than abstracting it into a library.&lt;/p&gt;
&lt;h2 id=&quot;toolboxes-not-libraries&quot;&gt;Toolboxes, not libraries&lt;/h2&gt;
&lt;p&gt;For small code tasks like this, it&#x27;s better to have a toolbox of examples you can pull from and customize.&lt;/p&gt;
&lt;p&gt;If I needed retry in a golang or rust program tomorrow, I could easily remember that I&#x27;ve written this code and translate it for use. If I&#x27;d instead used a retry library, I&#x27;d have to find one in that ecosystem and figure out which library was best, how to use it, and add an external dependency.&lt;/p&gt;
&lt;p&gt;This is much of the value that StackOverflow has provided to the world: a giant set of comments containing small, customizable code examples such as this one that solve small problems, which people can copy and modify as they wish.&lt;/p&gt;
&lt;p&gt;Given that StackOverflow seems to be dying, I wonder how we could do a better job supporting this &amp;quot;toolbox&amp;quot; type of code?&lt;/p&gt;
&lt;p&gt;Would it make sense for language ecosystems to host toolboxes? What if python had a &lt;code&gt;python.org/examples&lt;/code&gt; library where people can paste code examples with a description and usage guide, and perhaps star them when they find them useful?&lt;/p&gt;
&lt;p&gt;Or perhaps we could try to have a sort of &amp;quot;codex&amp;quot; by problem type, where &lt;code&gt;retry&lt;/code&gt; had a thousand examples in different languages, with different techniques, usages, and lineages?&lt;/p&gt;
&lt;h2 id=&quot;postscript&quot;&gt;postscript&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Kartik Agaram &lt;a href=&quot;https://merveilles.town/@akkartik/114880743779511619&quot; class=&quot;external-link&quot;&gt;points to&lt;/a&gt; &lt;a href=&quot;https://akkartik.name/post/four-repos&quot; class=&quot;external-link&quot;&gt;this excellent post&lt;/a&gt; about four repositories he&#x27;s created and uses, that he calls &amp;quot;template repositories&amp;quot;, similar to the &amp;quot;toolbox&amp;quot; idea&lt;/li&gt;
&lt;li&gt;Konrad Hinsen &lt;a href=&quot;https://scholar.social/@khinsen/114891340725097727&quot; class=&quot;external-link&quot;&gt;links&lt;/a&gt; this &lt;a href=&quot;https://osf.io/preprints/metaarxiv/nt96q_v1&quot; class=&quot;external-link&quot;&gt;preprint article&lt;/a&gt; about what it means to establish trust in scientific code. I like this:&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;The evolution of software in such a universe is very different from what we see today. There is no official repository, no development timeline, no releases. There is only a network of many variants of some code, connected by forking relations. Centralized maintenance as we know it today does not exist. Instead, the community of scientists using the code improves it in small steps, with each team taking over improvements from other forks if they consider them advantageous. Improvement thus happens by small-step evolution rather than by large-scale design. While this may look strange toanyone used to today’s software development practices, it is very similar to how scientific models and theories have evolved in the pre-digital era&lt;/p&gt;
&lt;/blockquote&gt;
</content>
  </entry>
  <entry>
    <id>https://notes.billmill.org/blog/2024/09/Comparing_golang_sqlite_to_C_sqlite.html</id>
    <link type="text/html" href="https://notes.billmill.org/blog/2024/09/Comparing_golang_sqlite_to_C_sqlite.html" />
    <title>Comparing golang sqlite to C sqlite</title>
    <published>2024-10-01T12:19:11.675Z</published>
    <updated>2025-06-19T21:20:17.704Z</updated>
    <content type="html">&lt;p&gt;Following a &lt;a href=&quot;https://www.reddit.com/r/golang/comments/1ft4udf/performance_write_intensive_sqlite_mattngosqlite3/&quot; class=&quot;external-link&quot;&gt;question on reddit&lt;/a&gt;, I got curious about how well golang sqlite bindings (and translations, and WASM embeddings) compare to using C directly.&lt;/p&gt;
&lt;p&gt;I&#x27;m competent with C, but no expert, so I used the help of an LLM to translate a &lt;a href=&quot;/programming/golang/database_libraries/go-sqlite-bench.html&quot; class=&quot;internal-link&quot;&gt;golang sqlite benchmark&lt;/a&gt; to C and tried to make it as equivalent to the golang as possible in terms of the work it was doing.&lt;/p&gt;
&lt;p&gt;The &lt;a href=&quot;https://github.com/cvilsmeier/go-sqlite-bench/blob/b0e7d08b69d685fc20fcbf4ac4de5f57b73c3720/app/app.go#L93&quot; class=&quot;external-link&quot;&gt;test I copied&lt;/a&gt; is pretty simple: insert a million users into a database, then query the users table and create user structs for each.&lt;/p&gt;
&lt;p&gt;The result is just one test, but it suggests that all sqlite bindings (or translations) have trouble keeping up with sqlite in C. All times here are in milliseconds, and I picked the best of two runs:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;library&lt;/th&gt;
&lt;th&gt;insert (ms)&lt;/th&gt;
&lt;th&gt;ratio&lt;/th&gt;
&lt;th&gt;query (ms)&lt;/th&gt;
&lt;th&gt;ratio&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;C sqlite3&lt;/td&gt;
&lt;td&gt;737&lt;/td&gt;
&lt;td&gt;1.00&lt;/td&gt;
&lt;td&gt;84&lt;/td&gt;
&lt;td&gt;1.00&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;mattn/sqlite3&lt;/td&gt;
&lt;td&gt;1380&lt;/td&gt;
&lt;td&gt;1.87&lt;/td&gt;
&lt;td&gt;1036&lt;/td&gt;
&lt;td&gt;12.33&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;http://crawshaw.io/sqlite&quot; class=&quot;external-link&quot;&gt;crawshaw.io/sqlite&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;1097&lt;/td&gt;
&lt;td&gt;1.49&lt;/td&gt;
&lt;td&gt;542&lt;/td&gt;
&lt;td&gt;6.45&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;http://modernc.org/sqlite&quot; class=&quot;external-link&quot;&gt;modernc.org/sqlite&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;4148&lt;/td&gt;
&lt;td&gt;5.63&lt;/td&gt;
&lt;td&gt;966&lt;/td&gt;
&lt;td&gt;11.50&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;eatonphil/gosqlite&lt;/td&gt;
&lt;td&gt;1017&lt;/td&gt;
&lt;td&gt;1.38&lt;/td&gt;
&lt;td&gt;651&lt;/td&gt;
&lt;td&gt;7.75&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ncruces/go-sqlite3&lt;/td&gt;
&lt;td&gt;2178&lt;/td&gt;
&lt;td&gt;2.96&lt;/td&gt;
&lt;td&gt;611&lt;/td&gt;
&lt;td&gt;7.27&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;zombiezen/go-sqlite&lt;/td&gt;
&lt;td&gt;1403&lt;/td&gt;
&lt;td&gt;1.90&lt;/td&gt;
&lt;td&gt;211&lt;/td&gt;
&lt;td&gt;2.51&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;python 3.12.2&lt;/td&gt;
&lt;td&gt;1992&lt;/td&gt;
&lt;td&gt;2.70&lt;/td&gt;
&lt;td&gt;1426&lt;/td&gt;
&lt;td&gt;16.98&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;a href=&quot;/images/Pasted image 20241001084907.png&quot;&gt;&lt;img class=&quot;bodyimg&quot; src=&quot;/images/Pasted image 20241001084907.png&quot;&gt;&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;/images/Pasted image 20241001084951.png&quot;&gt;&lt;img class=&quot;bodyimg&quot; src=&quot;/images/Pasted image 20241001084951.png&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The tests were run with go 1.23.1 and clang 16.0.0 on an apple m1 mac with 32gb of ram.&lt;/p&gt;
&lt;p&gt;This is a deeply non-serious benchmark - I was browsing the web while I was running it, for starters. Still, the magnitude of the results indicates that sqlite in C is still quite a bit faster than any of the alternatives in go.&lt;/p&gt;
&lt;p&gt;Somebody on mastodon asked me how this compares to previous versions of go, so I did a brief test of mattn and crawshaw on go 1.19, and found that they were in the range of 10-15% slower, so real progress on making cgo faster has been made in a pretty short timeframe.&lt;/p&gt;
&lt;p&gt;The code is available &lt;a href=&quot;https://gist.github.com/llimllib/4cb06c5fe7439aa7f3cb67a818fa230d#file-sqlite-c&quot; class=&quot;external-link&quot;&gt;here&lt;/a&gt; in a gist.&lt;/p&gt;
&lt;h2 id=&quot;update&quot;&gt;update&lt;/h2&gt;
&lt;p&gt;For kicks, I added a python version to the table above; source is &lt;a href=&quot;https://gist.github.com/llimllib/4cb06c5fe7439aa7f3cb67a818fa230d#file-bench-py&quot; class=&quot;external-link&quot;&gt;in the same gist&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;2025 Jun 19&lt;/em&gt;: noticed that the python ratios were not correct and fixed them; I did not re-run any tests&lt;/p&gt;
</content>
  </entry>
  <entry>
    <id>https://notes.billmill.org/blog/2025/06/Federation_is_extremely_expensive.html</id>
    <link type="text/html" href="https://notes.billmill.org/blog/2025/06/Federation_is_extremely_expensive.html" />
    <title>Federation is extremely expensive</title>
    <published>2025-06-19T14:59:05.557Z</published>
    <updated>2025-06-19T16:04:16.19Z</updated>
    <content type="html">&lt;p&gt;There is a certain feeling among the developers of messaging software that if you want to create something worthwhile, you need to create a federated system.&lt;/p&gt;
&lt;p&gt;This is how you get &lt;a href=&quot;https://en.wikipedia.org/wiki/ActivityPub&quot; class=&quot;external-link&quot;&gt;ActivityPub&lt;/a&gt;, &lt;a href=&quot;https://atproto.blue/en/latest/readme.html&quot; class=&quot;external-link&quot;&gt;atproto&lt;/a&gt;, &lt;a href=&quot;https://matrix.org/docs/chat_basics/matrix-for-im/&quot; class=&quot;external-link&quot;&gt;matrix&lt;/a&gt;, &lt;a href=&quot;https://xmpp.org&quot; class=&quot;external-link&quot;&gt;xmpp&lt;/a&gt;, &lt;a href=&quot;https://nostr.com&quot; class=&quot;external-link&quot;&gt;nostr&lt;/a&gt;, and on and on and on. A series of protocols that aim to recreate the successes of email and IRC.&lt;/p&gt;
&lt;h3 id=&quot;the-successes-are-overblown&quot;&gt;The successes are overblown&lt;/h3&gt;
&lt;p&gt;Email and IRC have huge problems, many of which are caused by their own decentralization.&lt;/p&gt;
&lt;p&gt;Both protocols suffer from the extreme difficulty of upgrades - it&#x27;s nearly impossible to organize a whole herd of people running servers to upgrade at the same time. The effect is that once a decentralized protocol has stabilized, it is extremely resistant to upgrades.&lt;/p&gt;
&lt;p&gt;Both protocols suffer from the challenge of preventing spam. It has proven so difficult for email that is has de facto centralized around Google Mail and a few other major providers.&lt;/p&gt;
&lt;p&gt;Email&#x27;s design is extremely substandard by modern standards, but despite the quasi-centralization of providers, we have no hope of changing it because there are still too many providers and consumers to make modernization possible. This leaves us with the worst of both worlds, a sub-standard protocol with a mostly-centralized structure.&lt;/p&gt;
&lt;h3 id=&quot;moderation-is-impossible&quot;&gt;Moderation is ~impossible&lt;/h3&gt;
&lt;p&gt;A key lesson of the modern internet is that a well moderated service is essential to providing a decent experience for users who are not trolls.&lt;/p&gt;
&lt;p&gt;If your service is truly decentralized, consistent moderation is impossible by design.&lt;/p&gt;
&lt;p&gt;Taking on federation means taking on the Herculean task of coordinating decentralized moderation. I don&#x27;t think it&#x27;s &lt;em&gt;impossible&lt;/em&gt; to do an OK job of it, but it&#x27;s a huge task and an extreme social challenge.&lt;/p&gt;
&lt;h3 id=&quot;ownership-is-important&quot;&gt;Ownership is important&lt;/h3&gt;
&lt;p&gt;I think a lot of the desire for federated systems comes down to a desire for &lt;strong&gt;ownership&lt;/strong&gt;; the ability of a person or a group of people to own some piece of the system. It feels like in a decentralized system, even if you don&#x27;t, you &lt;em&gt;could&lt;/em&gt; own a piece of it. You could stand up an email server, or a mastodon server, and do things your own way if you had to.&lt;/p&gt;
&lt;p&gt;This is an excellent impulse, and we should nurture it!&lt;/p&gt;
&lt;p&gt;But federation is not the only way to achieve ownership. We can make open software that allows people to own their own computation in meaningful ways a lot faster by &lt;strong&gt;skipping federation&lt;/strong&gt; and focusing instead on building software that delivers a stronger user interface and user experience while providing data ownership.&lt;/p&gt;
&lt;p&gt;I want to see is a world in which we can deliver more open software to develop communities more rapidly, and I think we might get there faster if we accept that the cost of federation is so high that we ought to consider avoiding it.&lt;/p&gt;
&lt;h3 id=&quot;postscript&quot;&gt;postscript&lt;/h3&gt;
&lt;p&gt;This post follows a conversation I had on a private Slack. Part of the reason why I think that conversation happened on Slack instead of on an open chat system is that we have spent so much effort building federated systems that we&#x27;ve failed to deliver working open software to replace it.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <id>https://notes.billmill.org/blog/2024/12/Triple_Storyline_for_Ethical_Design.html</id>
    <link type="text/html" href="https://notes.billmill.org/blog/2024/12/Triple_Storyline_for_Ethical_Design.html" />
    <title>Triple Storyline for Ethical Design</title>
    <published>2024-12-07T02:01:11.598Z</published>
    <updated>2024-12-07T02:01:11.598Z</updated>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;A good user experience is only as good as the action it enables. Designing a system that makes it easy to &lt;a href=&quot;https://www.nytimes.com/2018/03/27/nyregion/facebook-housing-ads-discrimination-lawsuit.html&quot; class=&quot;external-link&quot;&gt;do bad things&lt;/a&gt; is bad. A system that automates discrimination is, by Spinoza’s light, evil.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;[Erika Hall](A good user experience is only as good as the action it enables. Designing a system that makes it easy to &lt;a href=&quot;https://www.nytimes.com/2018/03/27/nyregion/facebook-housing-ads-discrimination-lawsuit.html&quot; class=&quot;external-link&quot;&gt;do bad things&lt;/a&gt; is bad. A system that automates discrimination is, by Spinoza’s light, evil.) via &lt;a href=&quot;https://mastodon.social/@RuthMalan/113608939667606692&quot; class=&quot;external-link&quot;&gt;mastodon&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
  </entry>
  <entry>
    <id>https://notes.billmill.org/blog/2024/12/Advent_of_Code_2024.html</id>
    <link type="text/html" href="https://notes.billmill.org/blog/2024/12/Advent_of_Code_2024.html" />
    <title>Advent of Code 2024</title>
    <published>2024-12-06T21:41:44.068Z</published>
    <updated>2024-12-06T21:41:44.068Z</updated>
    <content type="html">&lt;p&gt;Last year I kept my advent of code log in a note on this site: &lt;a href=&quot;/programming/advent_of_code/2023_problem_log.html&quot; class=&quot;internal-link&quot;&gt;2023 problem log&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;This year I wanted to make it easier for me to generate and share visualizations, and break the code into more logical explanations, so I&#x27;m doing it at &lt;a href=&quot;https://llimllib.github.io/advent2024/&quot; class=&quot;external-link&quot;&gt;https://llimllib.github.io/advent2024/&lt;/a&gt; in an &lt;a href=&quot;https://observablehq.com/framework/&quot; class=&quot;external-link&quot;&gt;observable framework&lt;/a&gt; notebook.&lt;/p&gt;
&lt;p&gt;The source code is &lt;a href=&quot;https://github.com/llimllib/advent2024&quot; class=&quot;external-link&quot;&gt;all on github&lt;/a&gt;, but hopefully the notebook provides an easy reading experience.&lt;/p&gt;
&lt;p&gt;I&#x27;ve tried to do visualizations for anything that can be remotely visualized, and plan to play around with it some more and learn more about framework and plot.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <id>https://notes.billmill.org/blog/2024/08/Colophon.html</id>
    <link type="text/html" href="https://notes.billmill.org/blog/2024/08/Colophon.html" />
    <title>Colophon</title>
    <published>2024-08-12T20:54:18.364Z</published>
    <updated>2024-08-12T20:54:18.364Z</updated>
    <content type="html">&lt;p&gt;To add to this blog, I type out a note in Obsidian; I use Obsidian with very few plugins.&lt;/p&gt;
&lt;p&gt;&lt;video controls&gt;&lt;source src=&quot;/images/typenote.mp4&quot; type=&quot;video/mp4&quot; /&gt;&lt;a href=&quot;/images/typenote.mp4&quot;&gt;download&lt;/a&gt;&lt;/video&gt;&lt;/p&gt;
&lt;p&gt;When it&#x27;s reasonably done, I back it up with the &lt;a href=&quot;https://github.com/Vinzent03/obsidian-git&quot; class=&quot;external-link&quot;&gt;obsidian git&lt;/a&gt; plugin by clicking the &amp;quot;backup&amp;quot; button&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;/images/Pasted image 20240812170051.png&quot;&gt;&lt;img class=&quot;bodyimg&quot; src=&quot;/images/Pasted image 20240812170051.png&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;That backs it up to a private github repository.&lt;/p&gt;
&lt;p&gt;That repository has a &lt;a href=&quot;https://gist.github.com/llimllib/19bbe748d13da49233d501da04e63fb8&quot; class=&quot;external-link&quot;&gt;github action&lt;/a&gt; that runs &lt;a href=&quot;https://github.com/llimllib/obsidian_notes/blob/d745eae34ac9f261baa7dbba093240b73a71945c/run.py&quot; class=&quot;external-link&quot;&gt;run.py&lt;/a&gt; from my &lt;a href=&quot;https://github.com/llimllib/obsidian_notes&quot; class=&quot;external-link&quot;&gt;obsidian_notes repository&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;That script converts the big pile of Obsidian-flavored markdown into a big pile of HTML.&lt;/p&gt;
&lt;p&gt;The github action uploads that big pile of HTML to a &lt;a href=&quot;https://www.digitalocean.com/products/spaces&quot; class=&quot;external-link&quot;&gt;Digital Ocean space&lt;/a&gt; using &lt;a href=&quot;/computer_usage/AWS_cli/s3cmd.html&quot; class=&quot;internal-link&quot;&gt;s3cmd&lt;/a&gt;. (I &lt;a href=&quot;https://hachyderm.io/@llimllib/112759682584953702&quot; class=&quot;external-link&quot;&gt;don&#x27;t recommend&lt;/a&gt; Digital Ocean spaces, but that&#x27;s what I use for now)&lt;/p&gt;
&lt;p&gt;To serve &lt;code&gt;notes.billmill.org&lt;/code&gt;, I have &lt;a href=&quot;/programming/web_serving/caddy.html&quot; class=&quot;internal-link&quot;&gt;caddy&lt;/a&gt; installed on my web server, proxying requests to &lt;code&gt;cdn.billmill.org&lt;/code&gt;, which is a &lt;code&gt;CNAME&lt;/code&gt; to the digital ocean space.&lt;/p&gt;
&lt;p&gt;That&#x27;s it! Feel free to ask me questions on mastodon.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <id>https://notes.billmill.org/blog/2024/08/How_to_debug_git_with_visual_studio_code_on_a_mac.html</id>
    <link type="text/html" href="https://notes.billmill.org/blog/2024/08/How_to_debug_git_with_visual_studio_code_on_a_mac.html" />
    <title>How to debug git with visual studio code on a mac</title>
    <published>2024-08-09T13:26:26.812Z</published>
    <updated>2024-08-09T13:26:26.812Z</updated>
    <content type="html">&lt;p&gt;I needed to figure out how to debug the &lt;code&gt;git&lt;/code&gt; binary to figure out exactly what it was doing, and I thought I&#x27;d share the setup because it took me a little while to get going.&lt;/p&gt;
&lt;h1 id=&quot;prerequisites&quot;&gt;Prerequisites&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;install &lt;a href=&quot;https://code.visualstudio.com/&quot; class=&quot;external-link&quot;&gt;visual studio code&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;install the &lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=ms-vscode.cpptools&quot; class=&quot;external-link&quot;&gt;microsoft C/C++ extension&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;you will need to have &lt;code&gt;lldb&lt;/code&gt;, the &lt;a href=&quot;/programming/compilers/llvm.html&quot; class=&quot;internal-link&quot;&gt;llvm&lt;/a&gt; debugger available on your system.
&lt;ul&gt;
&lt;li&gt;To see if you have &lt;code&gt;lldb&lt;/code&gt; available, run &lt;code&gt;which lldb&lt;/code&gt;; as long as it prints a path and not &lt;code&gt;lldb not found&lt;/code&gt;, you&#x27;re good to go&lt;/li&gt;
&lt;li&gt;The most common way to get it installed is to install &lt;code&gt;Xcode&lt;/code&gt; from the app store or the &lt;code&gt;Xcode Command Line Tools&lt;/code&gt; which can be installed with &lt;code&gt;xcode-select --install&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;You may also install it with homebrew by doing &lt;code&gt;brew install llvm&lt;/code&gt;. Be aware though that brew will not link it by default, so you&#x27;ll have to figure out how to tell vs code to use it&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&quot;building-git&quot;&gt;Building git&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;Clone the git repository: &lt;code&gt;git clone https://github.com/git/git&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Change into the cloned directory: &lt;code&gt;cd git&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Set some build variables by creating a file called &lt;code&gt;config.mak&lt;/code&gt;. It should have exactly these contents:&lt;pre&gt;&lt;code class=&quot;language-makefile&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;DEVELOPER&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;CFLAGS&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;-O0
&lt;/pre&gt;&lt;/div&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;A command you might use to make this file: &lt;code&gt;printf &#x27;DEVELOPER=1\nCFLAGS+= -O0&#x27; &amp;gt; config.mak&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;see &lt;a href=&quot;https://github.com/git/git/blob/25673b1c476756ec0587fb0596ab3c22b96dc52a/Makefile#L556&quot; class=&quot;external-link&quot;&gt;the Makefile&lt;/a&gt; for more info on what &lt;code&gt;DEVELOPER&lt;/code&gt; does&lt;/li&gt;
&lt;li&gt;the &lt;code&gt;CFLAGS&lt;/code&gt; variable provides a flag to the C compiler to build a binary with no compiler optimizations; traditionally &lt;code&gt;O3&lt;/code&gt; means &amp;quot;the most optimizations&amp;quot; and &lt;code&gt;O0&lt;/code&gt; means &amp;quot;no optimizations&amp;quot;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://clang.llvm.org/docs/CommandGuide/clang.html#code-generation-options&quot; class=&quot;external-link&quot;&gt;clang documentation&lt;/a&gt; on optimization options&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;make -j4&lt;/code&gt;
&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;-j&lt;/code&gt; argument to make tells it how much parallelism it can use. It speeds up the build by increasing parallelism; you can use more if you have more CPUs, or less if you have fewer. The build doesn&#x27;t take very long though so it&#x27;s not a big deal.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;When this completes successfully, you should have a bunch of new binaries in your git directory, such as &lt;code&gt;git&lt;/code&gt;, &lt;code&gt;git-add&lt;/code&gt;, &lt;code&gt;git-am&lt;/code&gt;, etc&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&quot;debugging-git&quot;&gt;Debugging git&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;make a &lt;code&gt;.vscode&lt;/code&gt; directory: &lt;code&gt;mkdir .vscode&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;open the git directory with visual studio code: &lt;code&gt;code .&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;create a file in the &lt;code&gt;.vscode&lt;/code&gt; directory called &lt;code&gt;launch.json&lt;/code&gt; that tells vs code how to start &lt;code&gt;git&lt;/code&gt; for debugging. Its contents should be:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
&lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;quot;configurations&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
&lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;quot;name&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;debug git&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;quot;type&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;cppdbg&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;quot;request&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;launch&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;quot;program&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;${workspaceFolder}/git&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;quot;args&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;status&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
&lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;quot;cwd&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;${workspaceFolder}&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;quot;externalConsole&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;quot;MIMode&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;lldb&amp;quot;&lt;/span&gt;
&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Note that we&#x27;re passing &lt;code&gt;status&lt;/code&gt; as an argument; the first command we&#x27;re going to debug is &lt;code&gt;git status&lt;/code&gt;. To debug other commands, you&#x27;ll want to change the &lt;code&gt;args&lt;/code&gt; option&lt;/li&gt;
&lt;li&gt;See the &lt;a href=&quot;https://code.visualstudio.com/docs/cpp/launch-json-reference&quot; class=&quot;external-link&quot;&gt;documentation&lt;/a&gt; for information on these configuration options and what additional flags are available&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Open the &lt;code&gt;builtins/commit.c&lt;/code&gt; file in the editor&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Set a breakpoint on the first line inside the &lt;code&gt;cmd_status&lt;/code&gt; function; in my current version of git, that&#x27;s located at line 1504&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;To set a breakpoint, click on the left gutter, just to the left of the line numbers; you should see a red dot appear. Once a breakpoint is set, the debugger will stop when it reaches that location, allowing you to control execution of the program
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://code.visualstudio.com/docs/editor/debugging#_breakpoints&quot; class=&quot;external-link&quot;&gt;here is VS Code&#x27;s documentation&lt;/a&gt; on how to set breakpoints&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Annoyingly, setting a breakpoint at the function name doesn&#x27;t seem to work for me, so I have to set a breakpoint &lt;em&gt;inside&lt;/em&gt; the function or else the program doesn&#x27;t break
&lt;ul&gt;
&lt;li&gt;If you really want to do this, in the command palette there&#x27;s an action called &lt;code&gt;Add Function Breakpoint&lt;/code&gt;, which created a function breakpoint that I could type &lt;code&gt;cmd_status&lt;/code&gt; into, and that did break on the function invocation&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;builtins&lt;/code&gt; directory contains most of the high-level git UI code&lt;/li&gt;
&lt;li&gt;git subcommands will have a function name &lt;code&gt;cmd_&amp;lt;command name&amp;gt;&lt;/code&gt;; check out &lt;code&gt;cmd_log&lt;/code&gt; or &lt;code&gt;cmd_commit&lt;/code&gt; for other examples&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Switch to the &lt;code&gt;Run and Debug&lt;/code&gt; tab of VS code&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;click on &lt;code&gt;start debugging&lt;/code&gt;, the green arrow located at the top left of the window, or press F5 to do the same thing.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;alternatively, open your command palette with &lt;code&gt;⇧⌘P&lt;/code&gt;, type &lt;code&gt;Debug&lt;/code&gt;, and select &lt;code&gt;Debug: Select and Start Debugging&lt;/code&gt;, then select &lt;code&gt;debug git&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If all goes well, you should now be controlling the execution of git inside VS code! Go ahead and dig in to try and understand what&#x27;s going on.&lt;/p&gt;
&lt;p&gt;I might write more about what it is that&#x27;s going on in there, but I&#x27;m not sure - let me know if you would like to understand some part of it better.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <id>https://notes.billmill.org/blog/2024/07/A_command_to_print_terminal_color_codes.html</id>
    <link type="text/html" href="https://notes.billmill.org/blog/2024/07/A_command_to_print_terminal_color_codes.html" />
    <title>A command to print terminal color codes</title>
    <published>2024-07-19T13:05:13.988Z</published>
    <updated>2024-07-19T13:05:13.988Z</updated>
    <content type="html">&lt;p&gt;I write shell scripts that use terminal colors often enough that it&#x27;s handy to know a bit about them, but not often enough that I remember how they work without looking it up.&lt;/p&gt;
&lt;p&gt;So, this morning, I wrote &lt;code&gt;colors&lt;/code&gt;, a shell script that prints out tables of the basic ANSI colors and text effects, and demonstrates what they look like on your terminal.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/llimllib/personal_code/blob/master/homedir/.local/bin/colors&quot; class=&quot;external-link&quot;&gt;Here&#x27;s the script&lt;/a&gt;, and here&#x27;s what it looks like on a mostly-stock apple terminal.app with a dark color scheme:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;/images/Pasted image 20240719095744.png&quot;&gt;&lt;img class=&quot;bodyimg&quot; src=&quot;/images/Pasted image 20240719095744.png&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;You can pass &lt;code&gt;-v&lt;/code&gt; to the script to get a table of the 8-bit colors, in case you want to get real fancy with your script:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;/images/Pasted image 20240719094123.png&quot;&gt;&lt;img class=&quot;bodyimg&quot; src=&quot;/images/Pasted image 20240719094123.png&quot;&gt;&lt;/a&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <id>https://notes.billmill.org/blog/2024/06/Serving_a_billion_web_requests_with_boring_code.html</id>
    <link type="text/html" href="https://notes.billmill.org/blog/2024/06/Serving_a_billion_web_requests_with_boring_code.html" />
    <title>Serving a billion web requests with boring code</title>
    <published>2024-06-24T17:32:45.949Z</published>
    <updated>2024-06-24T17:32:45.949Z</updated>
    <content type="html">&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#high-level&quot;&gt;High level&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#boring-uber-alles&quot;&gt;Boring über alles&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#the-boring-bits&quot;&gt;The boring bits&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#why-react&quot;&gt;Why React?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#golang&quot;&gt;golang&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#the-innovation-tokens&quot;&gt;The innovation tokens&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#modular-backend&quot;&gt;Modular backend&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#grpc&quot;&gt;gRPC&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#strict-backwards-compatibility&quot;&gt;Strict backwards compatibility&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#faceted-search&quot;&gt;Faceted search&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#database&quot;&gt;Database&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#creation&quot;&gt;creation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#etl&quot;&gt;ETL&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#models&quot;&gt;models&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#testing&quot;&gt;testing&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#local-database-for-development&quot;&gt;Local database for development&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#miscellaneous-tooling&quot;&gt;Miscellaneous tooling&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#logging&quot;&gt;Logging&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#documentation&quot;&gt;Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#runtime-integrations&quot;&gt;Runtime integrations&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#and-more&quot;&gt;And more&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;When I worked as a contractor to the US government at &lt;a href=&quot;adhocteam.us&quot; class=&quot;external-link&quot;&gt;ad hoc&lt;/a&gt;, I was fortunate enough to get the opportunity to design large parts of a relaunch of &lt;a href=&quot;https://www.medicare.gov/plan-compare/&quot; class=&quot;external-link&quot;&gt;medicare plan compare&lt;/a&gt;, the US government site through which hundreds of thousands of medicare recipients purchase their health care plans each year.&lt;/p&gt;
&lt;p&gt;We released after about a year of development, and were able to help millions of people find and purchase health care - if you&#x27;re in the US, you are pretty likely to know somebody who used this system.&lt;/p&gt;
&lt;p&gt;Though the US health care system is incredibly awful in many respects, I&#x27;m very proud of what the team I worked with was able to build in a short time frame and under a lot of constraints.&lt;/p&gt;
&lt;p&gt;The team of people that I worked with - managers, designers, engineers and business analysts - were excellent from top to bottom. I&#x27;ve never before experienced such a dedicated, collaborative, trusting team, and I learned a tremendous amount from them.&lt;/p&gt;
&lt;p&gt;I especially want to share this story because I want to show that quality software can get written under government constraints. It can! And if we all start believing that it can, we&#x27;re more likely to produce it.&lt;/p&gt;
&lt;h2 id=&quot;high-level&quot;&gt;High level&lt;/h2&gt;
&lt;p&gt;I worked on this system for about two and a half years, from the very first commit through two open enrollment periods.&lt;/p&gt;
&lt;p&gt;The API system served about 5 million requests on a normal weekday, with &amp;lt; 10 millisecond average request latency and a 95th percentile latency of less than 100 milliseconds.&lt;/p&gt;
&lt;p&gt;It served a very low baseline rate of errors, mostly spurious errors due to vulnerability scrapers. I&#x27;m proud that I can count the number of times an engineer was woken up by an emergency page on one hand.&lt;/p&gt;
&lt;p&gt;I was amazed at how far you can get by leaning on postgres and golang, keeping your system as organized and simple as possible, and putting in work every day for a long period of time.&lt;/p&gt;
&lt;h2 id=&quot;boring-über-alles&quot;&gt;Boring über alles&lt;/h2&gt;
&lt;p&gt;My north star when building the system was to keep it as boring as possible, in the &lt;a href=&quot;https://mcfunley.com/choose-boring-technology&quot; class=&quot;external-link&quot;&gt;Dan McKinley sense&lt;/a&gt;. (Go read &amp;quot;Choose Boring Technology&amp;quot; if you haven&#x27;t yet, it&#x27;s required reading. It&#x27;s better than this article.)&lt;/p&gt;
&lt;p&gt;There is a concept of &amp;quot;Innovation tokens&amp;quot; in that article, and I was explicit in choosing the pieces I used to build the site with how I spent them.&lt;/p&gt;
&lt;h2 id=&quot;the-boring-bits&quot;&gt;The boring bits&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;postgres&lt;/strong&gt; - The rock on which I prefer to build my systems&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;golang&lt;/strong&gt; - I had experience with it from working on &lt;a href=&quot;https://changelog.com/gotime/154&quot; class=&quot;external-link&quot;&gt;healthcare.gov&lt;/a&gt;, and felt extremely confident in its reliability and relative simplicity&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;react&lt;/strong&gt; - React is boring in the sense we&#x27;re discussing here, it&#x27;s extremely widely deployed and was known well by my teammates&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;why-react&quot;&gt;Why React?&lt;/h3&gt;
&lt;p&gt;There are many valid criticisms of react; &lt;a href=&quot;https://infrequently.org/2021/03/the-performance-inequality-gap/&quot; class=&quot;external-link&quot;&gt;this piece&lt;/a&gt; is an example, and I was aware of the issues already in 2018 when I was building the site. The main thrust is that it tends towards large application bundles, which take a long time to download and execute, especially on the cheap mobile phones that are the main link to the internet for so many people.&lt;/p&gt;
&lt;p&gt;In building a piece of infrastructure for the government, it was especially concerning that the application be available to as many people as possible. We took accessibility seriously both in the sense that the site needed to have proper design for users with disabilities and also in the sense that people with many devices needed to connect to it.&lt;/p&gt;
&lt;p&gt;Nevertheless, I chose an SPA architecture and react for the site.&lt;/p&gt;
&lt;p&gt;I would have loved to have done differently, but I worried that choosing to use a multi-page architecture or a different library would have slowed us down enough that we wouldn&#x27;t have delivered on our tight timeline. I didn&#x27;t have enough trust in any of the alternatives available to me at the time to make me believe we could choose them safely enough.&lt;/p&gt;
&lt;p&gt;The result fell prey after a few years to a common failure mode of react apps, and became quite heavy and loaded somewhat slowly.&lt;/p&gt;
&lt;p&gt;I still think I made the right choice at the time, but it&#x27;s unfortunate that I felt I had to make it and I wish I had known of a nice clean way to avoid it.&lt;/p&gt;
&lt;h3 id=&quot;golang&quot;&gt;golang&lt;/h3&gt;
&lt;p&gt;Golang was overall a joy to build this project in. It runs efficiently both at build time and at run time, and having binary executable artifacts that build quickly makes it easy to deploy rapidly.&lt;/p&gt;
&lt;p&gt;Developers new to the language (our team of engineers grew from 2 to 15) were able to get onboard quickly and understand the language with no trouble.&lt;/p&gt;
&lt;p&gt;Error handling being both immediate and verbose is, in my opinion, a great feature for building systems that are resilient. Every time you do something that might fail, you are faced with handling the error case, and once you develop patterns they are consistent and predictable. (I know this is a big topic, I should probably write more about it)&lt;/p&gt;
&lt;p&gt;The day that we were able to switch to go modules, a big pain point went away for us. We hit a few bumps in the road as very early adopters but it was worth it.&lt;/p&gt;
&lt;p&gt;My biggest gripe with the golang ecosystem was that the documentation generation for projects that are not public sucks. For a long time, the documentation generator didn&#x27;t even support projects that used modules.&lt;/p&gt;
&lt;p&gt;That said, I was overwhelmingly happy with my choice here and never regretted it.&lt;/p&gt;
&lt;h2 id=&quot;the-innovation-tokens&quot;&gt;The innovation tokens&lt;/h2&gt;
&lt;p&gt;I made two architectural bets that I was less confident of than the others:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Modular backend&lt;/strong&gt; - I designed the backend neither as microservices, nor as a monolith, but into three large modules&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;gRPC&lt;/strong&gt; - The backend services communicated with each other via gRPC, and with web clients via &lt;a href=&quot;https://github.com/grpc-ecosystem/grpc-gateway&quot; class=&quot;external-link&quot;&gt;grpc-gateway&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;modular-backend&quot;&gt;Modular backend&lt;/h3&gt;
&lt;p&gt;I split the backend up into three parts; they all lived in the same repository but were designed such that they could be pulled apart and given to a new team if necessary.&lt;/p&gt;
&lt;p&gt;Each component had its own postgres database (which were physically co-located, but never intertwined) and strictly used gRPC to communicate between themselves.&lt;/p&gt;
&lt;p&gt;The split was largely based around data access patterns:&lt;/p&gt;
&lt;h4 id=&quot;drug-pricing-aka-druginfo&quot;&gt;drug pricing (aka druginfo)&lt;/h4&gt;
&lt;p&gt;One thing the site needed to be able to do was estimate the cost of any packaging variation of any drug at any pharmacy on any health insurance plan. Easy, right?&lt;/p&gt;
&lt;p&gt;This combinatorial explosion (I once calculated how many trillions of possibilities this was) necessitated a very carefully-indexed database, a large amount of preprocessing, and a commitment to engineering with performance in mind.&lt;/p&gt;
&lt;p&gt;It took a long time and a ton of government health system reverse engineering to figure out how to get even close to right with this part. I&#x27;m forever indebted to my colleagues who dove deep into the depths of &lt;a href=&quot;https://www.cms.gov/&quot; class=&quot;external-link&quot;&gt;CMS&lt;/a&gt;  bureaucracy, and then turned it into documentation and code.&lt;/p&gt;
&lt;h4 id=&quot;plan-search-aka-planinfo&quot;&gt;plan search (aka planinfo)&lt;/h4&gt;
&lt;p&gt;The main purpose of the site was for people to search for and purchase medicare part C and part D health care plans.&lt;/p&gt;
&lt;p&gt;Every day, we&#x27;d get a new dump of detailed health care plan information from CMS; this module would load the information into a new postgres database, and then we&#x27;d deploy a new version pointing at the new day&#x27;s data.&lt;/p&gt;
&lt;p&gt;Both &lt;code&gt;planinfo&lt;/code&gt; and &lt;code&gt;druginfo&lt;/code&gt; had entirely immutable databases in this way; their only job was to serve an API based on the most recent data for each.&lt;/p&gt;
&lt;h4 id=&quot;beneficiary-information-aka-beneinfo&quot;&gt;beneficiary information (aka beneinfo)&lt;/h4&gt;
&lt;p&gt;In the insurance argot, a person on a health care plan is a &amp;quot;beneficiary&amp;quot; of the plan. It sounds a little self-important to me, but that&#x27;s just what it is I suppose.&lt;/p&gt;
&lt;p&gt;(One thing I tried to do throughout my work on this project was to use the proper industry jargon wherever possible rather than something more familiar to myself. I felt it was part of the commitment to boringness to keep the jargon friction down to a minimum.)&lt;/p&gt;
&lt;p&gt;The job of the &lt;code&gt;beneinfo&lt;/code&gt; module was to store information about plan customers, and was the only part of the application where the database was long-lived and mutable.&lt;/p&gt;
&lt;p&gt;We strove to store as little data as possible here, to minimize the risk should there be any data leakage, but there was no way around storing a very scary amount of Personally Identifiable Information (PII). We were as serious as possible about this data, and the risk of losing control of it kept me nervous at all times.&lt;/p&gt;
&lt;h3 id=&quot;grpc&quot;&gt;gRPC&lt;/h3&gt;
&lt;p&gt;Overall, gRPC was not as great for us as I&#x27;d hoped when beginning the project.&lt;/p&gt;
&lt;p&gt;The best benefit of using it, and the driver behind my choice to use it, was that every interface was specified in code. This was very useful; we could generate tools and interfaces that changed in lockstep.&lt;/p&gt;
&lt;p&gt;The biggest pain points were all related to the tooling. I maintained a set of very hairy makefiles with eldritch &lt;code&gt;protoc&lt;/code&gt; commands to build all the interfaces into go files that we could use, and debugging those was always painful.&lt;/p&gt;
&lt;p&gt;Not being able to curl the system, as we would if it were a JSON API, was a pain in the butt. &lt;code&gt;grpcurl&lt;/code&gt; existed, and we used it, but was not nearly as nice.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/grpc-ecosystem/grpc-gateway&quot; class=&quot;external-link&quot;&gt;grpc-gateway&lt;/a&gt; was the best part of the ecosystem I used, it served more than a billion requests for us and was never once the source of a problem. It enabled us to do what gRPC ought to have been able to do from the start, serve requests to web clients.&lt;/p&gt;
&lt;p&gt;I loved having interface schemas, but we used so few of gRPC&#x27;s features and the code generation was so complicated that we probably would have been slightly better off without it.&lt;/p&gt;
&lt;h2 id=&quot;strict-backwards-compatibility&quot;&gt;Strict backwards compatibility&lt;/h2&gt;
&lt;p&gt;We followed a strict backwards-compatibility requirement, and only added and never removed fields from our interfaces. Once a field was exposed in the public API, it would be exposed forever unless it became a security problem (which, thankfully, never happened to us in the years I worked on this project).&lt;/p&gt;
&lt;p&gt;We did the same with the databases as the API; columns were added and rarely removed. If a column actually merited removal, the process was to add a column, remove all references to the old one, &lt;em&gt;wait a few weeks to make sure we didn&#x27;t need to roll back&lt;/em&gt;, then finally remove the column from the database.&lt;/p&gt;
&lt;p&gt;Our discipline with backwards compatibility gave us freedom to keep up a high rate of changes and maintain confidence that local changes would not have negative downstream consequences.&lt;/p&gt;
&lt;h2 id=&quot;faceted-search&quot;&gt;Faceted search&lt;/h2&gt;
&lt;p&gt;A core principle of the app was to rely on postgres whenever possible, and also to be stupid instead of clever whenever possible.&lt;/p&gt;
&lt;p&gt;Faceted search was an excellent example of both of those properties. We could have reached for elasticsearch, and we also could have tried to use an ORM, or built a little faceting language.&lt;/p&gt;
&lt;p&gt;We implemented faceted search by having a well-indexed table of plans, and building a SQL query string by tacking on conditions based on a long series of conditions.&lt;/p&gt;
&lt;p&gt;The function &lt;code&gt;buildQuery&lt;/code&gt; which implemented the core part of this scheme is a single 250 line function, heavily commented, which lays out the logic in a nearly flat way. The focus is kept squarely on business requirements, instead of on fancy code.&lt;/p&gt;
&lt;h2 id=&quot;database&quot;&gt;Database&lt;/h2&gt;
&lt;h3 id=&quot;creation&quot;&gt;creation&lt;/h3&gt;
&lt;p&gt;We stored the database schemas in a series of &lt;code&gt;.sql&lt;/code&gt; files with leading numbers, so that they could be loaded in order at database creation time.&lt;/p&gt;
&lt;p&gt;For &lt;code&gt;planinfo&lt;/code&gt; and &lt;code&gt;beneinfo&lt;/code&gt;, there were no migrations, because the database was recreated every day. Instead, there was a &lt;code&gt;version&lt;/code&gt; number stored in both the database and the application. Given that we (tried very hard to) never make backwards incompatible changes to the database, the apps would check at startup that their database schema version number was greater than or equal to the database version number stored in the database, and refuse to start if they were not.&lt;/p&gt;
&lt;p&gt;This was &lt;strong&gt;a general pattern&lt;/strong&gt;: If an app encountered any unexpected or missing configuration, it refused to start and threw noticeable, hopefully clear, errors.&lt;/p&gt;
&lt;p&gt;I tried hard to make it so that if the apps actually started up, they would have everything they needed to run properly.&lt;/p&gt;
&lt;p&gt;There were occasional instances where we accidentally rolled out backwards-incompatible changes to the database, and in those cases we generally rolled back the data update and rebuilt it.&lt;/p&gt;
&lt;h3 id=&quot;etl&quot;&gt;ETL&lt;/h3&gt;
&lt;p&gt;The part of the system I&#x27;m most proud of, and on which I spent the most effort, is the ETL process.&lt;/p&gt;
&lt;p&gt;We had a series of shell scripts for each data source we ingested (there were many), which would pull the data and put it in an s3 bucket.&lt;/p&gt;
&lt;p&gt;Then, early in the morning, a cron job would spin up an EC2 instance, which would pull in the latest ETL code and all the data files. It would spin up a new database in our RDS instance, and begin the ETL process.&lt;/p&gt;
&lt;p&gt;If things went well, right about the time the east coasters got into work, a new database would be rotating into service.&lt;/p&gt;
&lt;p&gt;My recollections are not exact, but it took something like two to four hours to generate a new database with more than 250 million rows out of several gigabytes of text files in various formats.&lt;/p&gt;
&lt;p&gt;The code to insert data into the database heavily utilized &lt;a href=&quot;https://www.postgresql.org/docs/current/sql-copy.html&quot; class=&quot;external-link&quot;&gt;postgres&#x27; &lt;code&gt;COPY&lt;/code&gt;&lt;/a&gt; statement, avoiding &lt;code&gt;INSERT&lt;/code&gt;s as much as possible in favor of generating batches of collections that could be &lt;code&gt;COPY&lt;/code&gt;ed into the database.&lt;/p&gt;
&lt;h3 id=&quot;models&quot;&gt;models&lt;/h3&gt;
&lt;p&gt;We used the &lt;a href=&quot;https://github.com/xo/xo&quot; class=&quot;external-link&quot;&gt;xo&lt;/a&gt; library to connect to the database and generate models, along with heavily customized templates.&lt;/p&gt;
&lt;p&gt;The templates themselves, and the code to create the models from them was hairy. Thankfully it mostly only had to be written once and occasionally edited.&lt;/p&gt;
&lt;h3 id=&quot;testing&quot;&gt;testing&lt;/h3&gt;
&lt;p&gt;Here was &lt;strong&gt;my biggest mistake&lt;/strong&gt;: I invested a great deal of time and effort in creating &lt;a href=&quot;https://github.com/DATA-DOG/go-sqlmock&quot; class=&quot;external-link&quot;&gt;sql-mock&lt;/a&gt; tests for data that changed regularly. These tests needed constant, tedious maintenance.&lt;/p&gt;
&lt;p&gt;I should instead have tested against a live database, especially given that we were working mostly with immutable databases and wouldn&#x27;t have had to deal with recreating it for each test.&lt;/p&gt;
&lt;h3 id=&quot;local-database-for-development&quot;&gt;Local database for development&lt;/h3&gt;
&lt;p&gt;Each table in the database had an accompanying script that would generate a subset of the data for use in local development, since the final database was too large to run on a developer&#x27;s machine.&lt;/p&gt;
&lt;p&gt;This let each developer work with a live, local, copy of the database and enabled efficient development of changes.&lt;/p&gt;
&lt;p&gt;I highly recommend building in this tooling from the start, it saves you from either trying to add it in once your database grows large, or having your team connect to a remote database, making development slower.&lt;/p&gt;
&lt;h2 id=&quot;miscellaneous-tooling&quot;&gt;Miscellaneous tooling&lt;/h2&gt;
&lt;p&gt;We had a CLI tool, written mostly as a bunch of shell scripts, with a ton of available commands that performed all kinds of utility functions related to observability and operations.&lt;/p&gt;
&lt;p&gt;It was mostly written by an excellent coworker, and working with it is where I learned to write effective shell scripts. (Thanks to Nathan and &lt;a href=&quot;https://www.shellcheck.net/&quot; class=&quot;external-link&quot;&gt;shellcheck&lt;/a&gt; for this vital skill).&lt;/p&gt;
&lt;p&gt;Having this available from the start served as a really useful place for utility features to coalesce; without it they tend to scatter to more places further afield, or just live in a developer&#x27;s shell history.&lt;/p&gt;
&lt;p&gt;One fun bit of tooling I built was the ability to generate graphs from splunk (our log aggregation service) via slack commands, which was particularly helpful in incident handling, as you could easily share graphs with your coworkers.&lt;/p&gt;
&lt;h2 id=&quot;logging&quot;&gt;Logging&lt;/h2&gt;
&lt;p&gt;Every request that entered the backend got a request id as soon as it hit the system, and that request id was carried around with it wherever it went. Middleware gave the request its id, and then another middleware constructed a sub-logger with the request id embedded into it that was attached to the request context, so that all logs always had the request ID attached.&lt;/p&gt;
&lt;p&gt;The system logged on entry and exit, with as much detail as was safe. Any other logging that was above the debug level was supposed to be exceptional, although we weren&#x27;t super strict about that.&lt;/p&gt;
&lt;p&gt;We used &lt;a href=&quot;https://pkg.go.dev/github.com/rs/zerolog&quot; class=&quot;external-link&quot;&gt;zerolog&lt;/a&gt;, and it worked great for us.&lt;/p&gt;
&lt;h2 id=&quot;documentation&quot;&gt;Documentation&lt;/h2&gt;
&lt;p&gt;At some point, I converted the markdown docs I and others had written in github into a book about how the system worked, using &lt;a href=&quot;https://sphinx-book-theme.readthedocs.io/en/stable/tutorials/get-started.html&quot; class=&quot;external-link&quot;&gt;sphinx-book-theme&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Miraculously, this book gained traction, and I got great contributions from teammates and everybody knew where to look to find system documentation.&lt;/p&gt;
&lt;p&gt;I have started documentation websites for many other projects, and no other ones have ever worked as successfully, and I wish that I had any idea why that was but I don&#x27;t.&lt;/p&gt;
&lt;p&gt;It proudly featured our mascot (the corgi) showing off its most notable feature&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;/images/Pasted image 20240627221817.png&quot;&gt;&lt;img class=&quot;bodyimg&quot; src=&quot;/images/Pasted image 20240627221817.png&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;runtime-integrations&quot;&gt;Runtime integrations&lt;/h2&gt;
&lt;p&gt;Our client frequently wanted us to add queries that would operate from the browser, and I was fortunate to be able to push back and turn many of those into build-time requests instead.&lt;/p&gt;
&lt;p&gt;One place where our performance got killed at our clients&#x27; request was with render-blocking analytics scripts; it seemed every team wanted a different script run to get the analytics that they &amp;quot;needed&amp;quot;. I advised them against it and tried to demonstrate the performance and download size problems they incurred, but the client was not interested in my arguments.&lt;/p&gt;
&lt;h2 id=&quot;and-more&quot;&gt;And more&lt;/h2&gt;
&lt;p&gt;There are so many more parts of a system like this that I haven&#x27;t covered here; I mostly wanted to write down a bit about a bunch of the pieces while I still remember what they are.&lt;/p&gt;
&lt;p&gt;I was very fortunate to be able to work with such a positive, creative, and engaged team that made the space for such a successful project to be possible. An article about the social factors and personalities that made the team go, and the site happen, would be a second article as long as this one is.&lt;/p&gt;
</content>
  </entry>
</feed>