<?xml version="1.0" encoding="UTF-8"?><feed xmlns="http://www.w3.org/2005/Atom" xmlns:fh="http://purl.org/syndication/history/1.0" xml:base="/feed.atom" xml:lang="en-CA"><title>Kevin Cox's Blog</title><id>https://kevincox.ca/</id><link rel="alternate" href="https://kevincox.ca/"/><link rel="hub" href="https://pubsubhubbub.appspot.com/"/><link href="https://kevincox.ca/feed.atom" rel="self"/><link href="https://kevincox.ca/feed.atom" rel="first"/><link href="https://kevincox.ca/feed/2013-04-12.atom" rel="last"/><link href="https://kevincox.ca/feed/2024-10-01.atom" rel="next"/><fh:archive/><link href="https://kevincox.ca/feed.atom" rel="current"/><link href="https://kevincox.ca/feed/2024-10-01.atom" rel="prev-archive"/><icon>/logo.svg</icon><updated>2026-08-07T20:05:00-04:00</updated><author><name>Kevin Cox</name><uri>https://kevincox.ca</uri><email>kevincox@kevincox.ca</email></author><entry xml:base="/2026/08/07/lost-inputs/"><title>Lost Inputs</title><link rel="alternate" type="text/html" href="https://kevincox.ca/2026/08/07/lost-inputs/"/><id>https://kevincox.ca/2026/08/07/lost-inputs/</id><published>2026-08-07T20:05:00-04:00</published><updated>2026-08-07T20:05:00-04:00</updated><category term="UX"/><content type="html"><![CDATA[<main class=_0 style="--gap:1em;display:grid;gap:var(--gap);grid:none/1fr minmax(0,36em) 1fr;margin:0;overflow:auto;padding:0"><p style=grid-column:2;margin:0;padding:0>A UI shouldn’t lose your inputs.</p><ul class="_1 _2" style="grid-column:2;list-style:disc;list-style:&quot;- &quot;;margin:0;padding:0;padding-inline-start:2em"><li style=margin:0;padding:0>If you are typing, each and every key should result in a letter in order. No characters should e mssig or rerodeerd.</li><li style=margin:0;padding:0>If I switch windows with <kbd class=_3 style="border:2px solid light-dark(#ccc,#555);border-radius:.2em;box-shadow:2px 2px light-dark(#999,#333);color:light-dark(#000,#fff);margin:0 1px;padding:0 .1em">Alt+Tab</kbd> and then type, the events must always go to the newly focused window.</li><li style=margin:0;padding:0>If I close a tab with <kbd class=_3 style="border:2px solid light-dark(#ccc,#555);border-radius:.2em;box-shadow:2px 2px light-dark(#999,#333);color:light-dark(#000,#fff);margin:0 1px;padding:0 .1em">Ctrl+w</kbd> and then type, the events must go to the newly focused tab.</li></ul><p style=grid-column:2;margin:0;padding:0>This may seem obvious, but I often see applications that behave incorrectly if your inputs come too fast. One common situation where I see interfaces falter is keyboard shortcuts that bring you to a text input. Keypresses after the shortcut should always land in that input.</p><h2 id=examples class=_4 style="grid-column:2;margin:.4em 0 0;padding:0"><a href=https://kevincox.ca/2026/08/07/lost-inputs/#examples style=color:inherit!important;margin:0;padding:0;text-decoration:inherit;text-shadow:inherit!important>Examples</a></h2><h3 id=app-launchers class=_4 style="grid-column:2;margin:.4em 0 0;padding:0"><a href=https://kevincox.ca/2026/08/07/lost-inputs/#app-launchers style=color:inherit!important;margin:0;padding:0;text-decoration:inherit;text-shadow:inherit!important>App Launchers</a></h3><p style=grid-column:2;margin:0;padding:0>A common example is an app launcher. On GNOME I tap <kbd class=_3 style="border:2px solid light-dark(#ccc,#555);border-radius:.2em;box-shadow:2px 2px light-dark(#999,#333);color:light-dark(#000,#fff);margin:0 1px;padding:0 .1em">Super</kbd> and type to search my apps. If I type <kbd class=_3 style="border:2px solid light-dark(#ccc,#555);border-radius:.2em;box-shadow:2px 2px light-dark(#999,#333);color:light-dark(#000,#fff);margin:0 1px;padding:0 .1em">Super</kbd><kbd class=_3 style="border:2px solid light-dark(#ccc,#555);border-radius:.2em;box-shadow:2px 2px light-dark(#999,#333);color:light-dark(#000,#fff);margin:0 1px;padding:0 .1em">f</kbd><kbd class=_3 style="border:2px solid light-dark(#ccc,#555);border-radius:.2em;box-shadow:2px 2px light-dark(#999,#333);color:light-dark(#000,#fff);margin:0 1px;padding:0 .1em">o</kbd><kbd class=_3 style="border:2px solid light-dark(#ccc,#555);border-radius:.2em;box-shadow:2px 2px light-dark(#999,#333);color:light-dark(#000,#fff);margin:0 1px;padding:0 .1em">o</kbd> it should search for “foo” every time. GNOME works correctly here. No matter how overloaded and laggy my system is, and no matter how fast I type, the search is accurately captured. KDE’s KRunner does not pass this test. Oftentimes the first few characters of my search will land in the previously-focused application. I think this is because it is implemented as a separate process, and it can take a moment to start up and capture keyboard focus. macOS’s Spotlight also fails, although the failure window seems much smaller than KDE’s and required significant system load to hit.</p><h3 id=clipboard class=_4 style="grid-column:2;margin:.4em 0 0;padding:0"><a href=https://kevincox.ca/2026/08/07/lost-inputs/#clipboard style=color:inherit!important;margin:0;padding:0;text-decoration:inherit;text-shadow:inherit!important>Clipboard</a></h3><p style=grid-column:2;margin:0;padding:0><a href=https://ghostty.org/ class=_5 style=color:inherit;margin:0;padding:0;text-decoration:underline;-webkit-text-decoration-color:gray;text-decoration-color:gray>Ghostty</a> handles paste events asynchronously. Typing <kbd class=_3 style="border:2px solid light-dark(#ccc,#555);border-radius:.2em;box-shadow:2px 2px light-dark(#999,#333);color:light-dark(#000,#fff);margin:0 1px;padding:0 .1em">Ctrl+Shift+v</kbd><kbd class=_3 style="border:2px solid light-dark(#ccc,#555);border-radius:.2em;box-shadow:2px 2px light-dark(#999,#333);color:light-dark(#000,#fff);margin:0 1px;padding:0 .1em">Enter</kbd> can trigger the <kbd class=_3 style="border:2px solid light-dark(#ccc,#555);border-radius:.2em;box-shadow:2px 2px light-dark(#999,#333);color:light-dark(#000,#fff);margin:0 1px;padding:0 .1em">Enter</kbd> first, then the pasted text lands on the next line.</p><h3 id=web-apps class=_4 style="grid-column:2;margin:.4em 0 0;padding:0"><a href=https://kevincox.ca/2026/08/07/lost-inputs/#web-apps style=color:inherit!important;margin:0;padding:0;text-decoration:inherit;text-shadow:inherit!important>Web Apps</a></h3><p style=grid-column:2;margin:0;padding:0>This occurs on web apps too. I’ll pick on <a href=https://linear.app/ class=_5 style=color:inherit;margin:0;padding:0;text-decoration:underline;-webkit-text-decoration-color:gray;text-decoration-color:gray>Linear</a> as they appear to have thought about this to some degree. Some shortcuts like <kbd class=_3 style="border:2px solid light-dark(#ccc,#555);border-radius:.2em;box-shadow:2px 2px light-dark(#999,#333);color:light-dark(#000,#fff);margin:0 1px;padding:0 .1em">a</kbd><kbd class=_3 style="border:2px solid light-dark(#ccc,#555);border-radius:.2em;box-shadow:2px 2px light-dark(#999,#333);color:light-dark(#000,#fff);margin:0 1px;padding:0 .1em">1</kbd> will reliably self-assign the ticket no matter how fast I type or how locked up my system is. Even complex interactions like <kbd class=_3 style="border:2px solid light-dark(#ccc,#555);border-radius:.2em;box-shadow:2px 2px light-dark(#999,#333);color:light-dark(#000,#fff);margin:0 1px;padding:0 .1em">m</kbd><kbd class=_3 style="border:2px solid light-dark(#ccc,#555);border-radius:.2em;box-shadow:2px 2px light-dark(#999,#333);color:light-dark(#000,#fff);margin:0 1px;padding:0 .1em">b</kbd><kbd class=_3 style="border:2px solid light-dark(#ccc,#555);border-radius:.2em;box-shadow:2px 2px light-dark(#999,#333);color:light-dark(#000,#fff);margin:0 1px;padding:0 .1em">Ctrl+V</kbd><kbd class=_3 style="border:2px solid light-dark(#ccc,#555);border-radius:.2em;box-shadow:2px 2px light-dark(#999,#333);color:light-dark(#000,#fff);margin:0 1px;padding:0 .1em">Enter</kbd> will <kbd class=_3 style="border:2px solid light-dark(#ccc,#555);border-radius:.2em;box-shadow:2px 2px light-dark(#999,#333);color:light-dark(#000,#fff);margin:0 1px;padding:0 .1em">m</kbd>ark the current issue as <kbd class=_3 style="border:2px solid light-dark(#ccc,#555);border-radius:.2em;box-shadow:2px 2px light-dark(#999,#333);color:light-dark(#000,#fff);margin:0 1px;padding:0 .1em">b</kbd>locked by the issue in my clipboard. That is how it should work!</p><p style=grid-column:2;margin:0;padding:0>However, they aren’t perfect. <kbd class=_3 style="border:2px solid light-dark(#ccc,#555);border-radius:.2em;box-shadow:2px 2px light-dark(#999,#333);color:light-dark(#000,#fff);margin:0 1px;padding:0 .1em">/</kbd><kbd class=_3 style="border:2px solid light-dark(#ccc,#555);border-radius:.2em;box-shadow:2px 2px light-dark(#999,#333);color:light-dark(#000,#fff);margin:0 1px;padding:0 .1em">f</kbd><kbd class=_3 style="border:2px solid light-dark(#ccc,#555);border-radius:.2em;box-shadow:2px 2px light-dark(#999,#333);color:light-dark(#000,#fff);margin:0 1px;padding:0 .1em">o</kbd><kbd class=_3 style="border:2px solid light-dark(#ccc,#555);border-radius:.2em;box-shadow:2px 2px light-dark(#999,#333);color:light-dark(#000,#fff);margin:0 1px;padding:0 .1em">o</kbd> can have various behaviours depending on how fast you type. It should search for “foo” every time, but I have seen a few failures:</p><ul class="_1 _2" style="grid-column:2;list-style:disc;list-style:&quot;- &quot;;margin:0;padding:0;padding-inline-start:2em"><li style=margin:0;padding:0>A prefix lands in the search box, but then it loses focus and future inputs are treated as global shortcuts again.</li><li style=margin:0;padding:0>Reordering such as “oof”. The “f” lands in the search box, but the cursor then resets to the start of the search box before I type “oo”.</li></ul><h3 id=mouse-events class=_4 style="grid-column:2;margin:.4em 0 0;padding:0"><a href=https://kevincox.ca/2026/08/07/lost-inputs/#mouse-events style=color:inherit!important;margin:0;padding:0;text-decoration:inherit;text-shadow:inherit!important>Mouse Events</a></h3><p style=grid-column:2;margin:0;padding:0>This can happen for mouse events too. For example Firefox has an excellent feature where after clicking the close button on a tab, the next tab’s close button will always land in the exact same spot. Even if the tabs were previously compressed because too many are open, they will wait until you move your mouse away to expand again. This way you can just keep clicking in the same location to close many tabs with ease.</p><p style=grid-column:2;margin:0;padding:0>But the tab closing animation ruins this! There is a gap in time where the old tab hasn’t gone away and clicks just disappear. So you can’t simply click 4 times to close 4 tabs, it may close fewer than you intended. You instead need to wait until the previous click is fully processed before performing the next click. This isn’t the worst failure mode but can make workflows where you close lots of tabs unnecessarily slow and annoying. <kbd class=_3 style="border:2px solid light-dark(#ccc,#555);border-radius:.2em;box-shadow:2px 2px light-dark(#999,#333);color:light-dark(#000,#fff);margin:0 1px;padding:0 .1em">Ctrl+W</kbd> doesn’t appear to suffer this issue and always closes the right number of tabs.</p><p style=grid-column:2;margin:0;padding:0>This reminds me of the classic moving click target UX failure. This is the opposite of that: instead of the target jumping away as you try to click, the predictable target is instead not ready yet.</p><h2 id=difficulty class=_4 style="grid-column:2;margin:.4em 0 0;padding:0"><a href=https://kevincox.ca/2026/08/07/lost-inputs/#difficulty style=color:inherit!important;margin:0;padding:0;text-decoration:inherit;text-shadow:inherit!important>Difficulty</a></h2><p style=grid-column:2;margin:0;padding:0>The main difficulty in implementing this correctly is that you need some sort of global input routing that buffers events until the application is ready for them. This can be quite difficult in browser applications as there is no way to “defer” input events other than completely blocking the main thread. So if follow-up keypresses are required you need to synchronously adjust focus, which may be difficult in your framework. This typically also precludes any form of lazy loading. Alternatively you would need to implement your own focus and input event handling logic, which is <a href=https://kevincox.ca/2021/08/18/fake-links/ class=_5 style=color:inherit;margin:0;padding:0;text-decoration:underline;-webkit-text-decoration-color:gray;text-decoration-color:gray>almost certainly a bad idea</a>.</p><p style=grid-column:2;margin:0;padding:0>Asynchronous work is a complicated case in general, because buffering inputs for an extended period is confusing to the user. What if that work can fail? Do you drop the buffered input events? Or feed it to the previously focused input control? Do you need some sort of timeout? Luckily I find that this case is very rare. (Although the clipboard example above is one of these cases.)</p><p style=grid-column:2;margin:0;padding:0>If you have any sort of UI automation, this is easy to test: feed the keys in as a single batch and be sure that your app handles them properly!</p><h2 id=why class=_4 style="grid-column:2;margin:.4em 0 0;padding:0"><a href=https://kevincox.ca/2026/08/07/lost-inputs/#why style=color:inherit!important;margin:0;padding:0;text-decoration:inherit;text-shadow:inherit!important>Why?</a></h2><p style=grid-column:2;margin:0;padding:0>Typing speed shouldn’t affect functionality! Keyboard shortcuts are designed for people to go fast, nothing will harm your productivity more than familiar keyboard shortcuts doing something unexpected! (Bonus points if those inputs aren’t lost, but trigger a completely different action like “delete”.) It is easy to say that it is a 2 millisecond gap and no one will hit a window that small, but that gap may be much larger on a low-end machine swapping like crazy. Or maybe my local LLM is hogging my GPU for a second or two. Don’t let fingers race your UI, just close the gap.</p></main>]]></content></entry><entry xml:base="/2025/11/18/digdoku/"><title>Digdoku</title><link rel="alternate" type="text/html" href="https://kevincox.ca/2025/11/18/digdoku/"/><id>https://kevincox.ca/2025/11/18/digdoku/</id><published>2025-11-18T20:45:00-05:00</published><updated>2025-11-18T20:45:00-05:00</updated><content type="html"><![CDATA[<main class=_0 style="--gap:1em;display:grid;gap:var(--gap);grid:none/1fr minmax(0,36em) 1fr;margin:0;overflow:auto;padding:0"><p style=grid-column:2;margin:0;padding:0>I randomly had the idea that it would be interesting to “dig into” all possible Sudoku puzzles. So I created <a href=https://digdoku.kevincox.ca/ class=_1 style=color:inherit;margin:0;padding:0;text-decoration:underline;-webkit-text-decoration-color:gray;text-decoration-color:gray>Digdoku</a>. The landing page is an “empty” puzzle, each link leads to a puzzle with one additional square filled. Values that are not possible are excluded. So you can never get an invalid puzzle by clicking links (but you can by editing the URL).</p><p style=grid-column:2;margin:0;padding:0>The concept is simple. Basically for every unset cell it attempts to solve the puzzle with every number from 1-9. If any solution is found that value is possible, otherwise it is omitted.</p><p style=grid-column:2;margin:0;padding:0>This “eager” removal of impossible values means that it functions as a solver as well. By the time you click all of the provided squares (or often sooner) there will be only one possible grid, and it will be revealed.</p><p style=grid-column:2;margin:0;padding:0>However, implementing this simple solution is fairly slow. So some optimizations were necessary. These are primarily around skipping options that are easily determined to be impossible or were already found to be possible. The solver is still mostly a brute-force depth-first solver with some very basic rules put on top (such as eliminating duplicate values in a box/column/row group and if a value can only be in one place as a group it must be that).</p><p style=grid-column:2;margin:0;padding:0>The slowest case I am aware of takes about 100ms on my desktop. It could almost certainly be much faster, I suspect another order of magnitude would be fairly easy to achieve. But in the first version some cases took over six minutes, so I’m calling it good enough for now.</p><p style=grid-column:2;margin:0;padding:0>One downside is that the URL only keeps minimal state, so a lot of options need to be re-eliminated every step of the way. Maybe compiling to WASM and running an incremental solve client-side would be better. Might be something interesting to try. It would also avoid the risk that my single-core VPS gets overloaded if this gets popular. I do have caching enabled, but I suspect after a few clicks hits will become quite rare.</p><p style=grid-column:2;margin:0;padding:0>There is also something about a simple HTML response that I am very fond of. Theoretically you could pre-render every possible grid into a static site. But according to Wikipedia there are 6.6e27 valid Sudoku grids, so I’ll hold off on the pre-render for now.</p></main>]]></content></entry><entry xml:base="/2025/08/24/nixos-postgres-upgrade/"><title>NixOS PostgreSQL Major Version Upgrade</title><link rel="alternate" type="text/html" href="https://kevincox.ca/2025/08/24/nixos-postgres-upgrade/"/><id>https://kevincox.ca/2025/08/24/nixos-postgres-upgrade/</id><published>2025-08-24T10:55:00-04:00</published><updated>2025-08-24T10:55:00-04:00</updated><category term="PostgreSQL"/><category term="NixOS"/><content type="html"><![CDATA[<main class=_0 style="--gap:1em;display:grid;gap:var(--gap);grid:none/1fr minmax(0,36em) 1fr;margin:0;overflow:auto;padding:0"><p style=grid-column:2;margin:0;padding:0>PostgreSQL major version upgrades are something that NixOS currently doesn’t handle for you. This is my process, documented mostly so that I can remember it next time I want to upgrade.</p><aside class="_1 _2" style="background:#600;box-shadow:4px 4px 8px #000;color:#fff;color-scheme:dark;display:grid;gap:.4em;grid-column:2;grid-template-columns:0 minmax(0,1fr) 0;margin:0;padding:.4em 0"><b style=grid-column:2;margin:0;padding:0>Warning</b><p style=grid-column:2;margin:0;padding:0>I highly recommend that you read the <a href=https://www.postgresql.org/docs/17/upgrading.html class=_3 style=color:inherit;margin:0;padding:0;text-decoration:underline;-webkit-text-decoration-color:gray;text-decoration-color:gray>PostgreSQL Upgrading documentation</a>. It is well written and gives important caveats. It would be risky to follow through these steps without understanding the process.</p></aside><h2 id=configuration class=_4 style="grid-column:2;margin:.4em 0 0;padding:0"><a href=https://kevincox.ca/2025/08/24/nixos-postgres-upgrade/#configuration style=color:inherit!important;margin:0;padding:0;text-decoration:inherit;text-shadow:inherit!important>Configuration</a></h2><p style=grid-column:2;margin:0;padding:0>I use the standard NixOS module for Postgres. My main config looks like this:</p><pre class=_5 style="background:light-dark(#ddd,#222);border-block:1px solid light-dark(#aaa,#444);display:grid;grid-column:2;grid-column:1/4;grid-template-columns:subgrid;justify-content:flex-start;margin:0;overflow:auto hidden;padding:.4em 0"><code class="_6 _7 language-nix" style=color:light-dark(#222,#ddd);grid-column:2;margin:0;padding:0;-moz-tab-size:2;-o-tab-size:2;tab-size:2;white-space:pre>{ <span class=_8 style=color:light-dark(#47a,#fff);font-weight:700;margin:0;padding:0>lib</span><span class=_9 style=color:light-dark(#85a,#f90);margin:0;padding:0>,</span> <span class=_8 style=color:light-dark(#47a,#fff);font-weight:700;margin:0;padding:0>pkgs</span><span class=_9 style=color:light-dark(#85a,#f90);margin:0;padding:0>,</span> <span class=_9 style=color:light-dark(#85a,#f90);margin:0;padding:0>... </span>}: {
	<span class=_a style=color:light-dark(#47a,#fff);font-weight:500;margin:0;padding:0>services</span>.<span class=_a style=color:light-dark(#47a,#fff);font-weight:500;margin:0;padding:0>postgresql</span> <span class=_9 style=color:light-dark(#85a,#f90);margin:0;padding:0>=</span> {
		<span class=_a style=color:light-dark(#47a,#fff);font-weight:500;margin:0;padding:0>enable</span> <span class=_9 style=color:light-dark(#85a,#f90);margin:0;padding:0>=</span> <span class=_b style=color:light-dark(#85a,#f90);font-weight:500;margin:0;padding:0>true</span>;
		<span class=_a style=color:light-dark(#47a,#fff);font-weight:500;margin:0;padding:0>package</span> <span class=_9 style=color:light-dark(#85a,#f90);margin:0;padding:0>=</span> <span class=_c style=color:light-dark(#111,#eee);margin:0;padding:0>pkgs</span><span class=_9 style=color:light-dark(#85a,#f90);margin:0;padding:0>.</span><span class=_c style=color:light-dark(#111,#eee);margin:0;padding:0>postgresql_17</span>;
		<span class=_a style=color:light-dark(#47a,#fff);font-weight:500;margin:0;padding:0>initdbArgs</span> <span class=_9 style=color:light-dark(#85a,#f90);margin:0;padding:0>=</span> [
			<span class=_d style=color:light-dark(#780,#bd3);margin:0;padding:0><span class=_d style=color:light-dark(#780,#bd3);margin:0;padding:0>"</span>--encoding=UTF8<span class=_d style=color:light-dark(#780,#bd3);margin:0;padding:0>"</span></span>
			<span class=_d style=color:light-dark(#780,#bd3);margin:0;padding:0><span class=_d style=color:light-dark(#780,#bd3);margin:0;padding:0>"</span>--no-locale<span class=_d style=color:light-dark(#780,#bd3);margin:0;padding:0>"</span></span>
		];
		<span class=_a style=color:light-dark(#47a,#fff);font-weight:500;margin:0;padding:0>ensureUsers</span> <span class=_9 style=color:light-dark(#85a,#f90);margin:0;padding:0>=</span> [{
			<span class=_a style=color:light-dark(#47a,#fff);font-weight:500;margin:0;padding:0>name</span> <span class=_9 style=color:light-dark(#85a,#f90);margin:0;padding:0>=</span> <span class=_d style=color:light-dark(#780,#bd3);margin:0;padding:0><span class=_d style=color:light-dark(#780,#bd3);margin:0;padding:0>"</span>kevincox<span class=_d style=color:light-dark(#780,#bd3);margin:0;padding:0>"</span></span>;
		} {
			<span class=_a style=color:light-dark(#47a,#fff);font-weight:500;margin:0;padding:0>name</span> <span class=_9 style=color:light-dark(#85a,#f90);margin:0;padding:0>=</span> <span class=_d style=color:light-dark(#780,#bd3);margin:0;padding:0><span class=_d style=color:light-dark(#780,#bd3);margin:0;padding:0>"</span>root<span class=_d style=color:light-dark(#780,#bd3);margin:0;padding:0>"</span></span>;
		}];
	};

	<span class=_a style=color:light-dark(#47a,#fff);font-weight:500;margin:0;padding:0>systemd</span>.<span class=_a style=color:light-dark(#47a,#fff);font-weight:500;margin:0;padding:0>services</span>.<span class=_a style=color:light-dark(#47a,#fff);font-weight:500;margin:0;padding:0>postgresql</span> <span class=_9 style=color:light-dark(#85a,#f90);margin:0;padding:0>=</span> {
		<span class=_a style=color:light-dark(#47a,#fff);font-weight:500;margin:0;padding:0>postStart</span> <span class=_9 style=color:light-dark(#85a,#f90);margin:0;padding:0>=</span> <span class=_c style=color:light-dark(#111,#eee);margin:0;padding:0>lib</span><span class=_9 style=color:light-dark(#85a,#f90);margin:0;padding:0>.</span><span class=_c style=color:light-dark(#111,#eee);margin:0;padding:0>mkAfter</span> <span class=_d style=color:light-dark(#780,#bd3);margin:0;padding:0><span class=_d style=color:light-dark(#780,#bd3);margin:0;padding:0>''</span></span>
<span class=_d style=color:light-dark(#780,#bd3);margin:0;padding:0>			$PSQL -tAc "ALTER ROLE kevincox with SUPERUSER"</span>
<span class=_d style=color:light-dark(#780,#bd3);margin:0;padding:0>			$PSQL -tAc "ALTER ROLE root with SUPERUSER"</span>
<span class=_d style=color:light-dark(#780,#bd3);margin:0;padding:0>		<span class=_d style=color:light-dark(#780,#bd3);margin:0;padding:0>''</span></span>;
		<span class=_a style=color:light-dark(#47a,#fff);font-weight:500;margin:0;padding:0>serviceConfig</span> <span class=_9 style=color:light-dark(#85a,#f90);margin:0;padding:0>=</span> {
			<span class=_a style=color:light-dark(#47a,#fff);font-weight:500;margin:0;padding:0>TimeoutStartSec</span> <span class=_9 style=color:light-dark(#85a,#f90);margin:0;padding:0>=</span> <span class=_d style=color:light-dark(#780,#bd3);margin:0;padding:0><span class=_d style=color:light-dark(#780,#bd3);margin:0;padding:0>"</span>30min<span class=_d style=color:light-dark(#780,#bd3);margin:0;padding:0>"</span></span>;
		};
	};
}
</code></pre><p style=grid-column:2;margin:0;padding:0>Notable features:</p><ol class="_e _f" style=grid-column:2;margin:0;padding:0;padding-inline-start:2em><li style=margin:0;padding:0>I pin the version of Postgres, to ensure that I upgrade when I want to.</li><li style=margin:0;padding:0>I give myself and root superuser.</li></ol><h2 id=preparation class=_4 style="grid-column:2;margin:.4em 0 0;padding:0"><a href=https://kevincox.ca/2025/08/24/nixos-postgres-upgrade/#preparation style=color:inherit!important;margin:0;padding:0;text-decoration:inherit;text-shadow:inherit!important>Preparation</a></h2><ol class="_e _f" style=grid-column:2;margin:0;padding:0;padding-inline-start:2em><li style=margin:0;padding:0>Update your NixOS config but <strong style=margin:0;padding:0>don’t</strong> deploy it.</li><li style=margin:0;padding:0>Build the config to make sure everything is ready to go.</li></ol><h2 id=upgrading class=_4 style="grid-column:2;margin:.4em 0 0;padding:0"><a href=https://kevincox.ca/2025/08/24/nixos-postgres-upgrade/#upgrading style=color:inherit!important;margin:0;padding:0;text-decoration:inherit;text-shadow:inherit!important>Upgrading</a></h2><ol class="_e _f" style=grid-column:2;margin:0;padding:0;padding-inline-start:2em><li style=margin:0;padding:0><p style=margin:0;padding:0>Log in as the <code class="_g language-zsh" style="background:light-dark(#ddd,#000);border:1px solid light-dark(#999,#555);border-radius:.3em;color:light-dark(#000,#fff);margin:0;padding:0 .2em;-moz-tab-size:4;-o-tab-size:4;tab-size:4;white-space:pre-wrap">postgres</code> user. (<code class="_g language-zsh" style="background:light-dark(#ddd,#000);border:1px solid light-dark(#999,#555);border-radius:.3em;color:light-dark(#000,#fff);margin:0;padding:0 .2em;-moz-tab-size:4;-o-tab-size:4;tab-size:4;white-space:pre-wrap">sudo -su postgres</code>)</p></li><li style=margin:0;padding:0><p style=margin:0;padding:0>Define the old version <code class="_g language-zsh" style="background:light-dark(#ddd,#000);border:1px solid light-dark(#999,#555);border-radius:.3em;color:light-dark(#000,#fff);margin:0;padding:0 .2em;-moz-tab-size:4;-o-tab-size:4;tab-size:4;white-space:pre-wrap">old=16</code></p></li><li style=margin:0;padding:0><p style=margin:0;padding:0>Get the old and new versions:</p><pre class=_5 style="background:light-dark(#ddd,#222);border-block:1px solid light-dark(#aaa,#444);display:grid;grid-column:1/4;grid-template-columns:subgrid;justify-content:flex-start;margin:0;overflow:auto hidden;padding:.4em 0"><code class="_g language-sh" style=color:light-dark(#222,#ddd);grid-column:2;margin:0;padding:0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;white-space:pre><span class=_b style=color:light-dark(#85a,#f90);font-weight:500;margin:0;padding:0>cd</span> /var/lib/postgresql/
pg_old=<span class=_d style=color:light-dark(#780,#bd3);margin:0;padding:0><span class=_d style=color:light-dark(#780,#bd3);margin:0;padding:0>$(</span>nix-build --no-out-link -A postgresql_<span class=_8 style=color:light-dark(#47a,#fff);font-weight:700;margin:0;padding:0>${old</span><span class=_9 style=color:light-dark(#85a,#f90);margin:0;padding:0>:?</span><span class=_8 style=color:light-dark(#47a,#fff);font-weight:700;margin:0;padding:0>}</span> <span class=_d style=color:light-dark(#780,#bd3);margin:0;padding:0>'</span>&lt;nixpkgs&gt;<span class=_d style=color:light-dark(#780,#bd3);margin:0;padding:0>')</span></span>
pg_new=<span class=_d style=color:light-dark(#780,#bd3);margin:0;padding:0><span class=_d style=color:light-dark(#780,#bd3);margin:0;padding:0>$(</span>nix-build --no-out-link -A postgresql_<span class=_d style=color:light-dark(#780,#bd3);margin:0;padding:0>$((</span>old<span class=_9 style=color:light-dark(#85a,#f90);margin:0;padding:0>+</span><span class=_b style=color:light-dark(#85a,#f90);font-weight:500;margin:0;padding:0>1</span><span class=_d style=color:light-dark(#780,#bd3);margin:0;padding:0>))</span> <span class=_d style=color:light-dark(#780,#bd3);margin:0;padding:0>'</span>&lt;nixpkgs&gt;<span class=_d style=color:light-dark(#780,#bd3);margin:0;padding:0>')</span></span>
</code></pre><aside class=_h style="background:#333;box-shadow:4px 4px 8px #000;color:#fff;color-scheme:dark;display:grid;gap:.4em;grid-template-columns:0 minmax(0,1fr) 0;margin:0;padding:.4em 0"><b style=grid-column:2;margin:0;padding:0>Note</b><p style=grid-column:2;margin:0;padding:0>If you are using extensions you will need them in <code class="_g language-zsh" style="background:light-dark(#ddd,#000);border:1px solid light-dark(#999,#555);border-radius:.3em;color:light-dark(#000,#fff);margin:0;padding:0 .2em;-moz-tab-size:4;-o-tab-size:4;tab-size:4;white-space:pre-wrap"><span class=_8 style=color:light-dark(#47a,#fff);font-weight:700;margin:0;padding:0>$pg_old</span></code> and <code class="_g language-zsh" style="background:light-dark(#ddd,#000);border:1px solid light-dark(#999,#555);border-radius:.3em;color:light-dark(#000,#fff);margin:0;padding:0 .2em;-moz-tab-size:4;-o-tab-size:4;tab-size:4;white-space:pre-wrap"><span class=_8 style=color:light-dark(#47a,#fff);font-weight:700;margin:0;padding:0>$pg_new</span></code>. If you are using the NixOS module you can make a package with <code class="_g language-zsh" style="background:light-dark(#ddd,#000);border:1px solid light-dark(#999,#555);border-radius:.3em;color:light-dark(#000,#fff);margin:0;padding:0 .2em;-moz-tab-size:4;-o-tab-size:4;tab-size:4;white-space:pre-wrap">nix-build <span class=_d style=color:light-dark(#780,#bd3);margin:0;padding:0><span class=_d style=color:light-dark(#780,#bd3);margin:0;padding:0>"</span>&lt;nixpkgs/nixos&gt;<span class=_d style=color:light-dark(#780,#bd3);margin:0;padding:0>"</span></span> -A config.services.postgresql.finalPackage</code>. If you are using <a href=https://colmena.cli.rs/ class=_3 style=color:inherit;margin:0;padding:0;text-decoration:underline;-webkit-text-decoration-color:gray;text-decoration-color:gray>colmena</a> you can do <code class="_g language-zsh" style="background:light-dark(#ddd,#000);border:1px solid light-dark(#999,#555);border-radius:.3em;color:light-dark(#000,#fff);margin:0;padding:0 .2em;-moz-tab-size:4;-o-tab-size:4;tab-size:4;white-space:pre-wrap">colmena <span class=_b style=color:light-dark(#85a,#f90);font-weight:500;margin:0;padding:0>eval</span> -E <span class=_d style=color:light-dark(#780,#bd3);margin:0;padding:0><span class=_d style=color:light-dark(#780,#bd3);margin:0;padding:0>"</span>{nodes, ...}: nodes.mynode.config.services.postgresql.finalPackage<span class=_d style=color:light-dark(#780,#bd3);margin:0;padding:0>"</span></span></code>.</p></aside></li><li style=margin:0;padding:0><p style=margin:0;padding:0>Initialize the new data directory. Be sure to use the arguments from your config!</p><pre class=_5 style="background:light-dark(#ddd,#222);border-block:1px solid light-dark(#aaa,#444);display:grid;grid-column:1/4;grid-template-columns:subgrid;justify-content:flex-start;margin:0;overflow:auto hidden;padding:.4em 0"><code class="_g language-sh" style=color:light-dark(#222,#ddd);grid-column:2;margin:0;padding:0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;white-space:pre><span class=_8 style=color:light-dark(#47a,#fff);font-weight:700;margin:0;padding:0>$pg_new</span>/bin/initdb --encoding=UTF8 --no-locale <span class=_d style=color:light-dark(#780,#bd3);margin:0;padding:0><span class=_d style=color:light-dark(#780,#bd3);margin:0;padding:0>$((</span>old<span class=_9 style=color:light-dark(#85a,#f90);margin:0;padding:0>+</span><span class=_b style=color:light-dark(#85a,#f90);font-weight:500;margin:0;padding:0>1</span><span class=_d style=color:light-dark(#780,#bd3);margin:0;padding:0>))</span></span>
</code></pre></li><li style=margin:0;padding:0><p style=margin:0;padding:0>Copy the configuration (this is really only needed if you have extensions that need to be loaded, the main value we care about is <code class="_g language-zsh" style="background:light-dark(#ddd,#000);border:1px solid light-dark(#999,#555);border-radius:.3em;color:light-dark(#000,#fff);margin:0;padding:0 .2em;-moz-tab-size:4;-o-tab-size:4;tab-size:4;white-space:pre-wrap">shared_preload_libraries</code>. But copying the whole file is easiest and the NixOS module will overwrite it anyways when starting the new version.).</p><pre class=_5 style="background:light-dark(#ddd,#222);border-block:1px solid light-dark(#aaa,#444);display:grid;grid-column:1/4;grid-template-columns:subgrid;justify-content:flex-start;margin:0;overflow:auto hidden;padding:.4em 0"><code class="_g language-sh" style=color:light-dark(#222,#ddd);grid-column:2;margin:0;padding:0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;white-space:pre>cp -v {<span class=_8 style=color:light-dark(#47a,#fff);font-weight:700;margin:0;padding:0>${old</span><span class=_9 style=color:light-dark(#85a,#f90);margin:0;padding:0>:?</span><span class=_8 style=color:light-dark(#47a,#fff);font-weight:700;margin:0;padding:0>}</span>,<span class=_d style=color:light-dark(#780,#bd3);margin:0;padding:0><span class=_d style=color:light-dark(#780,#bd3);margin:0;padding:0>$((</span>old<span class=_9 style=color:light-dark(#85a,#f90);margin:0;padding:0>+</span><span class=_b style=color:light-dark(#85a,#f90);font-weight:500;margin:0;padding:0>1</span><span class=_d style=color:light-dark(#780,#bd3);margin:0;padding:0>))</span></span>}/postgresql.conf
</code></pre></li><li style=margin:0;padding:0><p style=margin:0;padding:0>Run the check</p><pre class=_5 style="background:light-dark(#ddd,#222);border-block:1px solid light-dark(#aaa,#444);display:grid;grid-column:1/4;grid-template-columns:subgrid;justify-content:flex-start;margin:0;overflow:auto hidden;padding:.4em 0"><code class="_g language-sh" style=color:light-dark(#222,#ddd);grid-column:2;margin:0;padding:0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;white-space:pre><span class=_8 style=color:light-dark(#47a,#fff);font-weight:700;margin:0;padding:0>$pg_new</span>/bin/pg_upgrade \
	--old-bindir=<span class=_8 style=color:light-dark(#47a,#fff);font-weight:700;margin:0;padding:0>$pg_old</span>/bin \
	--new-bindir=<span class=_8 style=color:light-dark(#47a,#fff);font-weight:700;margin:0;padding:0>$pg_new</span>/bin \
	--old-datadir=/var/lib/postgresql/<span class=_8 style=color:light-dark(#47a,#fff);font-weight:700;margin:0;padding:0>${old</span><span class=_9 style=color:light-dark(#85a,#f90);margin:0;padding:0>:?</span><span class=_8 style=color:light-dark(#47a,#fff);font-weight:700;margin:0;padding:0>}</span> \
	--new-datadir=/var/lib/postgresql/<span class=_d style=color:light-dark(#780,#bd3);margin:0;padding:0><span class=_d style=color:light-dark(#780,#bd3);margin:0;padding:0>$((</span>old<span class=_9 style=color:light-dark(#85a,#f90);margin:0;padding:0>+</span><span class=_b style=color:light-dark(#85a,#f90);font-weight:500;margin:0;padding:0>1</span><span class=_d style=color:light-dark(#780,#bd3);margin:0;padding:0>))</span></span> \
	-j16 \
	--clone \
	--check
</code></pre><aside class=_h style="background:#333;box-shadow:4px 4px 8px #000;color:#fff;color-scheme:dark;display:grid;gap:.4em;grid-template-columns:0 minmax(0,1fr) 0;margin:0;padding:.4em 0"><b style=grid-column:2;margin:0;padding:0>Note</b><p style=grid-column:2;margin:0;padding:0>If <code class="_g language-zsh" style="background:light-dark(#ddd,#000);border:1px solid light-dark(#999,#555);border-radius:.3em;color:light-dark(#000,#fff);margin:0;padding:0 .2em;-moz-tab-size:4;-o-tab-size:4;tab-size:4;white-space:pre-wrap">--clone</code> doesn’t work on your system you can try <code class="_g language-zsh" style="background:light-dark(#ddd,#000);border:1px solid light-dark(#999,#555);border-radius:.3em;color:light-dark(#000,#fff);margin:0;padding:0 .2em;-moz-tab-size:4;-o-tab-size:4;tab-size:4;white-space:pre-wrap">--copy-file-range</code> which should basically always work or if not just skip the argument altogether.</p></aside></li><li style=margin:0;padding:0><p style=margin:0;padding:0>Stop the old Postgres. <code class="_g language-zsh" style="background:light-dark(#ddd,#000);border:1px solid light-dark(#999,#555);border-radius:.3em;color:light-dark(#000,#fff);margin:0;padding:0 .2em;-moz-tab-size:4;-o-tab-size:4;tab-size:4;white-space:pre-wrap">systemctl stop postgresql</code></p><aside class="_1 _2" style="background:#600;box-shadow:4px 4px 8px #000;color:#fff;color-scheme:dark;display:grid;gap:.4em;grid-template-columns:0 minmax(0,1fr) 0;margin:0;padding:.4em 0"><b style=grid-column:2;margin:0;padding:0>Warning</b><p style=grid-column:2;margin:0;padding:0>This is the point where we copy the data. If the old Postgres starts running again it will write new data to the old directory that will not be migrated. This will result in data loss. If that happens remove the new state directory and start again.</p></aside></li><li style=margin:0;padding:0><p style=margin:0;padding:0>Run the migration. This is the same command as the check without <code class="_g language-zsh" style="background:light-dark(#ddd,#000);border:1px solid light-dark(#999,#555);border-radius:.3em;color:light-dark(#000,#fff);margin:0;padding:0 .2em;-moz-tab-size:4;-o-tab-size:4;tab-size:4;white-space:pre-wrap">--check</code>.</p></li><li style=margin:0;padding:0><p style=margin:0;padding:0>Start the new PostgreSQL server. (This is typically done by activating the new configuration such as <code class="_g language-zsh" style="background:light-dark(#ddd,#000);border:1px solid light-dark(#999,#555);border-radius:.3em;color:light-dark(#000,#fff);margin:0;padding:0 .2em;-moz-tab-size:4;-o-tab-size:4;tab-size:4;white-space:pre-wrap">sudo nixos-rebuild switch</code>.)</p></li></ol><p style=grid-column:2;margin:0;padding:0>At this point the new version is running. Note that rolling back will cause data loss as new changes are being made only to the new data directory.</p><p style=grid-column:2;margin:0;padding:0>You may wish to rename the old directory so that if you do accidentally roll back it is more likely that things fail rather than losing data. But if you have insecure apps that let the first user be admin that may be a bad idea. Maybe you could do something like put a file there or a directory owned by root? But I haven’t tested any of these things.</p><h2 id=cleanup class=_4 style="grid-column:2;margin:.4em 0 0;padding:0"><a href=https://kevincox.ca/2025/08/24/nixos-postgres-upgrade/#cleanup style=color:inherit!important;margin:0;padding:0;text-decoration:inherit;text-shadow:inherit!important>Cleanup</a></h2><ol class="_e _f" style=grid-column:2;margin:0;padding:0;padding-inline-start:2em><li style=margin:0;padding:0><p style=margin:0;padding:0>As suggested by <code class="_g language-zsh" style="background:light-dark(#ddd,#000);border:1px solid light-dark(#999,#555);border-radius:.3em;color:light-dark(#000,#fff);margin:0;padding:0 .2em;-moz-tab-size:4;-o-tab-size:4;tab-size:4;white-space:pre-wrap">pg_upgrade</code> run <code class="_g language-zsh" style="background:light-dark(#ddd,#000);border:1px solid light-dark(#999,#555);border-radius:.3em;color:light-dark(#000,#fff);margin:0;padding:0 .2em;-moz-tab-size:4;-o-tab-size:4;tab-size:4;white-space:pre-wrap">vacuumdb --all --analyze-in-stages</code>.</p></li><li style=margin:0;padding:0><p style=margin:0;padding:0>At some point in the future once you are confident that the new DB is running fine (a few weeks doesn’t hurt) you can remove the old data.</p><pre class=_5 style="background:light-dark(#ddd,#222);border-block:1px solid light-dark(#aaa,#444);display:grid;grid-column:1/4;grid-template-columns:subgrid;justify-content:flex-start;margin:0;overflow:auto hidden;padding:.4em 0"><code class="_g language-sh" style=color:light-dark(#222,#ddd);grid-column:2;margin:0;padding:0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;white-space:pre><span class=_b style=color:light-dark(#85a,#f90);font-weight:500;margin:0;padding:0>cd</span> /var/lib/postgresql/
./delete_old_cluster.sh
rm -v delete_old_cluster.sh
</code></pre></li></ol></main>]]></content></entry><entry xml:base="/2025/08/05/losslessly-change-video-framerate/"><title>Losslessly Changing Video Framerate</title><link rel="alternate" type="text/html" href="https://kevincox.ca/2025/08/05/losslessly-change-video-framerate/"/><id>https://kevincox.ca/2025/08/05/losslessly-change-video-framerate/</id><published>2025-08-05T20:05:00-04:00</published><updated>2025-08-05T20:05:00-04:00</updated><content type="html"><![CDATA[<main class=_0 style="--gap:1em;display:grid;gap:var(--gap);grid:none/1fr minmax(0,36em) 1fr;margin:0;overflow:auto;padding:0"><p style=grid-column:2;margin:0;padding:0>I’ve been taking a few timelapse videos recently and as an amateur I’ve just been using the default phone camera apps. Google Camera defaults to “Auto” for the speedup, which keeps the resulting video between 15 and 30s no matter how long you record for (with minimum of 5x speed). It also offers explicit 5, 10, 30 and 120x speeds. The iPhone forgoes explicit options and only offers an auto mode with 20-40s output target and a 20x minimum speed.</p><p style=grid-column:2;margin:0;padding:0>Both of these apps have a limitation that they can only output 30 fps video. That seems like a missed opportunity since the faster than real time playback already tends to produce jumpy video. Getting 60 fps (or even 120 fps) makes a huge improvement to smoothness. The hardware can easily support these output framerates as the capture framerate will still be lower than regular speed videos.</p><p style=grid-column:2;margin:0;padding:0>My preferred approach is to record the video at “twice the duration” as I actually want, then to speed it up after the fact. Recording at a higher input framerate always gives you more flexibility as you can always speed it up later, and if you carefully pick your input framerate you can do this losslessly to avoid degrading the video quality.</p><p style=grid-column:2;margin:0;padding:0>For example, if I want to end up with a 1 minute 60 fps video covering 1 hour of real time. I need to record at <span class=katex style=margin:0;padding:0><math xmlns=http://www.w3.org/1998/Math/MathML style=margin:0;padding:0><semantics style=margin:0;padding:0><mrow style=margin:0;padding:0><mn style=margin:0;padding:0>60</mn><mtext style=margin:0;padding:0> </mtext><mi mathvariant=normal style=margin:0;padding:0>s</mi><mo style=margin:0;padding:0>×</mo><mn style=margin:0;padding:0>60</mn><mtext style=margin:0;padding:0> </mtext><mrow style=margin:0;padding:0><mi mathvariant=normal style=margin:0;padding:0>H</mi><mi mathvariant=normal style=margin:0;padding:0>z</mi></mrow><mo style=margin:0;padding:0>÷</mo><mn style=margin:0;padding:0>1</mn><mtext style=margin:0;padding:0> </mtext><mi mathvariant=normal style=margin:0;padding:0>h</mi></mrow><annotation encoding=application/x-tex style=margin:0;padding:0>60\u{s} \times 60\u{Hz} \div 1\u{h}</annotation></semantics></math></span>which is 1 fps. Google Camera doesn’t show you the framerate directly, but you can easily convert the input speeds to framerates. Since it always expects to output 30 fps the calculation is just <span class=katex style=margin:0;padding:0><math xmlns=http://www.w3.org/1998/Math/MathML style=margin:0;padding:0><semantics style=margin:0;padding:0><mrow style=margin:0;padding:0><mn style=margin:0;padding:0>30</mn><mtext style=margin:0;padding:0> </mtext><mrow style=margin:0;padding:0><mi mathvariant=normal style=margin:0;padding:0>f</mi><mi mathvariant=normal style=margin:0;padding:0>p</mi><mi mathvariant=normal style=margin:0;padding:0>s</mi></mrow><mo style=margin:0;padding:0>÷</mo><mtext style=margin:0;padding:0>speed</mtext></mrow><annotation encoding=application/x-tex style=margin:0;padding:0>30\u{fps} \div \v{speed}</annotation></semantics></math></span>, so to capture at 1 fps I need to select 30x speed.</p><p style=grid-column:2;margin:0;padding:0>To losslessly convert the video you can use ffmpeg bitstream filters.</p><pre class=_1 style="background:light-dark(#ddd,#222);border-block:1px solid light-dark(#aaa,#444);display:grid;grid-column:2;grid-column:1/4;grid-template-columns:subgrid;justify-content:flex-start;margin:0;overflow:auto hidden;padding:.4em 0"><code class="_2 language-sh" style=color:light-dark(#222,#ddd);grid-column:2;margin:0;padding:0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;white-space:pre>ffmpeg
	-i input-video.mp4 <span class=_3 style=color:light-dark(#555,#999);font-style:italic;margin:0;padding:0;white-space:pre-wrap># Input file.</span>
	-c copy <span class=_3 style=color:light-dark(#555,#999);font-style:italic;margin:0;padding:0;white-space:pre-wrap># Don't transcode, remain lossless.</span>
	-r 60 <span class=_3 style=color:light-dark(#555,#999);font-style:italic;margin:0;padding:0;white-space:pre-wrap># Output framerate, this is mostly informational for the whole-video metadata.</span>
	-fps_mode vfr <span class=_3 style=color:light-dark(#555,#999);font-style:italic;margin:0;padding:0;white-space:pre-wrap># Don't drop frames to match the expected framerate, just preserve all input frames.</span>
	-bsf:v <span class=_4 style=color:light-dark(#780,#bd3);margin:0;padding:0><span class=_4 style=color:light-dark(#780,#bd3);margin:0;padding:0>"</span>setts=ts=PTS/2<span class=_4 style=color:light-dark(#780,#bd3);margin:0;padding:0>"</span></span> <span class=_3 style=color:light-dark(#555,#999);font-style:italic;margin:0;padding:0;white-space:pre-wrap># Update the presentation time to 1/2 the previous one (play twice as fast).</span>
	output.mp4 <span class=_3 style=color:light-dark(#555,#999);font-style:italic;margin:0;padding:0;white-space:pre-wrap># Output file.</span>
</code></pre><p style=grid-column:2;margin:0;padding:0>This can easily be adjusted to other framerates by changing the <code class="_2 language-text" style="background:light-dark(#ddd,#000);border:1px solid light-dark(#999,#555);border-radius:.3em;color:light-dark(#000,#fff);margin:0;padding:0 .2em;-moz-tab-size:4;-o-tab-size:4;tab-size:4;white-space:pre-wrap">-r</code> and <code class="_2 language-text" style="background:light-dark(#ddd,#000);border:1px solid light-dark(#999,#555);border-radius:.3em;color:light-dark(#000,#fff);margin:0;padding:0 .2em;-moz-tab-size:4;-o-tab-size:4;tab-size:4;white-space:pre-wrap">-bsf:v</code> values.</p><aside class=_5 style="background:#333;box-shadow:4px 4px 8px #000;color:#fff;color-scheme:dark;display:grid;gap:.4em;grid-column:2;grid-template-columns:0 minmax(0,1fr) 0;margin:0;padding:.4em 0"><b style=grid-column:2;margin:0;padding:0>Note</b><p style=grid-column:2;margin:0;padding:0>The ffmpeg wiki has a page <a href=https://trac.ffmpeg.org/wiki/How%20to%20speed%20up%20/%20slow%20down%20a%20video class=_6 style=color:inherit;margin:0;padding:0;text-decoration:underline;-webkit-text-decoration-color:gray;text-decoration-color:gray>How to speed up / slow down a video</a>. However this page <a href=https://trac.ffmpeg.org/wiki/How%20to%20speed%20up%20/%20slow%20down%20a%20video#rawbitstreammethod class=_6 style=color:inherit;margin:0;padding:0;text-decoration:underline;-webkit-text-decoration-color:gray;text-decoration-color:gray>only has a lossless method for h264 and h265</a>. Furthermore this method makes it very difficult to preserve metadata, don’t handle variable frame rate properly and is multiple steps. I have found my method to be much easier and more effective.</p></aside></main>]]></content></entry><entry xml:base="/2025/07/20/no-imports/"><title>I Don't Like Imports</title><link rel="alternate" type="text/html" href="https://kevincox.ca/2025/07/20/no-imports/"/><id>https://kevincox.ca/2025/07/20/no-imports/</id><published>2025-07-20T18:15:00-04:00</published><updated>2025-07-20T18:15:00-04:00</updated><category term="Programming"/><category term="Programming Languages"/><category term="Rust"/><content type="html"><![CDATA[<main class=_0 style="--gap:1em;display:grid;gap:var(--gap);grid:none/1fr minmax(0,36em) 1fr;margin:0;overflow:auto;padding:0"><p style=grid-column:2;margin:0;padding:0>I think this is an unusual opinion, so I thought I would share.</p><p style=grid-column:2;margin:0;padding:0>I prefer not to import external symbols into the local scope. For example, I prefer:</p><pre class=_1 style="background:light-dark(#ddd,#222);border-block:1px solid light-dark(#aaa,#444);display:grid;grid-column:2;grid-column:1/4;grid-template-columns:subgrid;justify-content:flex-start;margin:0;overflow:auto hidden;padding:.4em 0"><code class="_2 language-rust" style=color:light-dark(#222,#ddd);grid-column:2;margin:0;padding:0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;white-space:pre><span class=_3 style=color:light-dark(#85a,#f90);margin:0;padding:0>let</span> <span class=_4 style=color:light-dark(#47a,#fff);font-weight:700;margin:0;padding:0>body</span> <span class=_3 style=color:light-dark(#85a,#f90);margin:0;padding:0>=</span> <span class=_5 style=color:light-dark(#47a,#fff);font-weight:500;margin:0;padding:0>reqwest</span><span class=_3 style=color:light-dark(#85a,#f90);margin:0;padding:0>::</span><span class=_5 style=color:light-dark(#47a,#fff);font-weight:500;margin:0;padding:0>get</span>(<span class=_6 style=color:light-dark(#780,#bd3);margin:0;padding:0><span class=_6 style=color:light-dark(#780,#bd3);margin:0;padding:0>"</span>https://kevincox.ca/feed.atom<span class=_6 style=color:light-dark(#780,#bd3);margin:0;padding:0>"</span></span>)<span class=_3 style=color:light-dark(#85a,#f90);margin:0;padding:0>.await?.</span><span class=_5 style=color:light-dark(#47a,#fff);font-weight:500;margin:0;padding:0>text</span>()<span class=_3 style=color:light-dark(#85a,#f90);margin:0;padding:0>.await?</span>;
</code></pre><p style=grid-column:2;margin:0;padding:0>over</p><pre class=_1 style="background:light-dark(#ddd,#222);border-block:1px solid light-dark(#aaa,#444);display:grid;grid-column:2;grid-column:1/4;grid-template-columns:subgrid;justify-content:flex-start;margin:0;overflow:auto hidden;padding:.4em 0"><code class="_2 language-rust" style=color:light-dark(#222,#ddd);grid-column:2;margin:0;padding:0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;white-space:pre><span class=_3 style=color:light-dark(#85a,#f90);margin:0;padding:0>use</span> <span class=_5 style=color:light-dark(#47a,#fff);font-weight:500;margin:0;padding:0>reqwest</span><span class=_3 style=color:light-dark(#85a,#f90);margin:0;padding:0>::</span>get; <span class=_7 style=color:light-dark(#555,#999);font-style:italic;margin:0;padding:0;white-space:pre-wrap>// At the top of the file.</span>

<span class=_7 style=color:light-dark(#555,#999);font-style:italic;margin:0;padding:0;white-space:pre-wrap>// Inside some function.</span>
<span class=_3 style=color:light-dark(#85a,#f90);margin:0;padding:0>let</span> <span class=_4 style=color:light-dark(#47a,#fff);font-weight:700;margin:0;padding:0>body</span> <span class=_3 style=color:light-dark(#85a,#f90);margin:0;padding:0>=</span> <span class=_5 style=color:light-dark(#47a,#fff);font-weight:500;margin:0;padding:0>get</span>(<span class=_6 style=color:light-dark(#780,#bd3);margin:0;padding:0><span class=_6 style=color:light-dark(#780,#bd3);margin:0;padding:0>"</span>https://kevincox.ca/feed.atom<span class=_6 style=color:light-dark(#780,#bd3);margin:0;padding:0>"</span></span>)<span class=_3 style=color:light-dark(#85a,#f90);margin:0;padding:0>.await?.</span><span class=_5 style=color:light-dark(#47a,#fff);font-weight:500;margin:0;padding:0>text</span>()<span class=_3 style=color:light-dark(#85a,#f90);margin:0;padding:0>.await?</span>;
</code></pre><h2 id=unambiguous class=_8 style="grid-column:2;margin:.4em 0 0;padding:0"><a href=https://kevincox.ca/2025/07/20/no-imports/#unambiguous style=color:inherit!important;margin:0;padding:0;text-decoration:inherit;text-shadow:inherit!important>Unambiguous</a></h2><p style=grid-column:2;margin:0;padding:0>There are various reasons for this, but all the most important ones boil down to being unambiguous. When I am reading code I see <code class="_2 language-rust" style="background:light-dark(#ddd,#000);border:1px solid light-dark(#999,#555);border-radius:.3em;color:light-dark(#000,#fff);margin:0;padding:0 .2em;-moz-tab-size:4;-o-tab-size:4;tab-size:4;white-space:pre-wrap"><span class=_5 style=color:light-dark(#47a,#fff);font-weight:500;margin:0;padding:0>library</span><span class=_3 style=color:light-dark(#85a,#f90);margin:0;padding:0>::</span><span class=_5 style=color:light-dark(#47a,#fff);font-weight:500;margin:0;padding:0>Foo</span></code> and know that it comes from <code class="_2 language-rust" style="background:light-dark(#ddd,#000);border:1px solid light-dark(#999,#555);border-radius:.3em;color:light-dark(#000,#fff);margin:0;padding:0 .2em;-moz-tab-size:4;-o-tab-size:4;tab-size:4;white-space:pre-wrap"><span class=_4 style=color:light-dark(#47a,#fff);font-weight:700;margin:0;padding:0>library</span></code>. I see <code class="_2 language-rust" style="background:light-dark(#ddd,#000);border:1px solid light-dark(#999,#555);border-radius:.3em;color:light-dark(#000,#fff);margin:0;padding:0 .2em;-moz-tab-size:4;-o-tab-size:4;tab-size:4;white-space:pre-wrap"><span class=_3 style=color:light-dark(#85a,#f90);margin:0;padding:0>crate::</span><span class=_5 style=color:light-dark(#47a,#fff);font-weight:500;margin:0;padding:0>Foo</span></code> and know it comes from elsewhere in my code and if I see just <code class="_2 language-rust" style="background:light-dark(#ddd,#000);border:1px solid light-dark(#999,#555);border-radius:.3em;color:light-dark(#000,#fff);margin:0;padding:0 .2em;-moz-tab-size:4;-o-tab-size:4;tab-size:4;white-space:pre-wrap"><span class=_5 style=color:light-dark(#47a,#fff);font-weight:500;margin:0;padding:0>Foo</span></code> I know it is from the current file (and possibly private, so I could be a bit more careful with how I use the API). I don’t need to guess or lookup what <code class="_2 language-rust" style="background:light-dark(#ddd,#000);border:1px solid light-dark(#999,#555);border-radius:.3em;color:light-dark(#000,#fff);margin:0;padding:0 .2em;-moz-tab-size:4;-o-tab-size:4;tab-size:4;white-space:pre-wrap"><span class=_5 style=color:light-dark(#47a,#fff);font-weight:500;margin:0;padding:0>Foo</span></code> I am working with. (And when I do inevitably guess I don’t need to have that background process asking “did I guess wrong?”)</p><p style=grid-column:2;margin:0;padding:0>I find when I am working on codebases where the convention is to import things I am very frequently tracking down the definition of some type or function as I don’t know whether it is local or from which library. This is especially painful when reading or reviewing code in a web interface. If you are reading a patch the imports are likely not expanded (if they didn’t change) and even if you have the whole file expanded you need to check if the referenced value is the global import from the top of the file or some local shadow.</p><h2 id=copy-paste class=_8 style="grid-column:2;margin:.4em 0 0;padding:0"><a href=https://kevincox.ca/2025/07/20/no-imports/#copy-paste style=color:inherit!important;margin:0;padding:0;text-decoration:inherit;text-shadow:inherit!important>Copy + Paste</a></h2><p style=grid-column:2;margin:0;padding:0>Another thing I love about fully qualified names is that I can just copy and paste code around or move it from file to file without needing to figure out which imports are needed in the new file (and inevitably importing the wrong <code class="_2 language-rust" style="background:light-dark(#ddd,#000);border:1px solid light-dark(#999,#555);border-radius:.3em;color:light-dark(#000,#fff);margin:0;padding:0 .2em;-moz-tab-size:4;-o-tab-size:4;tab-size:4;white-space:pre-wrap"><span class=_5 style=color:light-dark(#47a,#fff);font-weight:500;margin:0;padding:0>Error</span></code> at least a few times). For the most part code in other files and examples online can just be pasted into the file and work.</p><p style=grid-column:2;margin:0;padding:0>It’s not some dramatic productivity boon, but it certainly makes cleanups and refactors much smoother and a frustrating tedious step that is necessary when working with import-heavy code.</p><aside class=_9 style="background:#333;box-shadow:4px 4px 8px #000;color:#fff;color-scheme:dark;display:grid;gap:.4em;grid-column:2;grid-template-columns:0 minmax(0,1fr) 0;margin:0;padding:.4em 0"><p style=grid-column:2;margin:0;padding:0>This would be a great editor feature. If I am copying from one file to another in the same editor it should be able to also pull in the required imports. Technically it should be fairly straightforward to any editor with enough language services for precise autocomplete. The only real challenge would be coming up with a new name in the case of an identifier conflict.</p></aside><h2 id=long-names class=_8 style="grid-column:2;margin:.4em 0 0;padding:0"><a href=https://kevincox.ca/2025/07/20/no-imports/#long-names style=color:inherit!important;margin:0;padding:0;text-decoration:inherit;text-shadow:inherit!important>But The Names Are So Long!</a></h2><p style=grid-column:2;margin:0;padding:0>In some cases they are. But more often than not this is just a poor choice by the library author (in my opinion). For example in Rust the standard hash-map is <code class="_2 language-rust" style="background:light-dark(#ddd,#000);border:1px solid light-dark(#999,#555);border-radius:.3em;color:light-dark(#000,#fff);margin:0;padding:0 .2em;-moz-tab-size:4;-o-tab-size:4;tab-size:4;white-space:pre-wrap"><span class=_5 style=color:light-dark(#47a,#fff);font-weight:500;margin:0;padding:0>std</span><span class=_3 style=color:light-dark(#85a,#f90);margin:0;padding:0>::</span><span class=_5 style=color:light-dark(#47a,#fff);font-weight:500;margin:0;padding:0>collections</span><span class=_3 style=color:light-dark(#85a,#f90);margin:0;padding:0>::</span><span class=_5 style=color:light-dark(#47a,#fff);font-weight:500;margin:0;padding:0>HashMap</span></code>. But even that is just an alias. If you want to work on an <a href=https://doc.rust-lang.org/stable/std/collections/hash_map/enum.Entry.html class=_a style=color:inherit;margin:0;padding:0;text-decoration:underline;-webkit-text-decoration-color:gray;text-decoration-color:gray><code class="_2 language-rust" style="background:light-dark(#ddd,#000);border:1px solid light-dark(#999,#555);border-radius:.3em;color:light-dark(#000,#fff);margin:0;padding:0 .2em;-moz-tab-size:4;-o-tab-size:4;tab-size:4;white-space:pre-wrap"><span class=_5 style=color:light-dark(#47a,#fff);font-weight:500;margin:0;padding:0>Entry</span></code></a> you are now typing <code class="_2 language-rust" style="background:light-dark(#ddd,#000);border:1px solid light-dark(#999,#555);border-radius:.3em;color:light-dark(#000,#fff);margin:0;padding:0 .2em;-moz-tab-size:4;-o-tab-size:4;tab-size:4;white-space:pre-wrap"><span class=_5 style=color:light-dark(#47a,#fff);font-weight:500;margin:0;padding:0>std</span><span class=_3 style=color:light-dark(#85a,#f90);margin:0;padding:0>::</span><span class=_5 style=color:light-dark(#47a,#fff);font-weight:500;margin:0;padding:0>collections</span><span class=_3 style=color:light-dark(#85a,#f90);margin:0;padding:0>::</span><span class=_5 style=color:light-dark(#47a,#fff);font-weight:500;margin:0;padding:0>hash_map</span><span class=_3 style=color:light-dark(#85a,#f90);margin:0;padding:0>::</span><span class=_5 style=color:light-dark(#47a,#fff);font-weight:500;margin:0;padding:0>Entry</span></code>. I see no reason it couldn’t have been <code class="_2 language-rust" style="background:light-dark(#ddd,#000);border:1px solid light-dark(#999,#555);border-radius:.3em;color:light-dark(#000,#fff);margin:0;padding:0 .2em;-moz-tab-size:4;-o-tab-size:4;tab-size:4;white-space:pre-wrap"><span class=_5 style=color:light-dark(#47a,#fff);font-weight:500;margin:0;padding:0>std</span><span class=_3 style=color:light-dark(#85a,#f90);margin:0;padding:0>::</span><span class=_5 style=color:light-dark(#47a,#fff);font-weight:500;margin:0;padding:0>HashMap</span></code> and <code class="_2 language-rust" style="background:light-dark(#ddd,#000);border:1px solid light-dark(#999,#555);border-radius:.3em;color:light-dark(#000,#fff);margin:0;padding:0 .2em;-moz-tab-size:4;-o-tab-size:4;tab-size:4;white-space:pre-wrap"><span class=_5 style=color:light-dark(#47a,#fff);font-weight:500;margin:0;padding:0>std</span><span class=_3 style=color:light-dark(#85a,#f90);margin:0;padding:0>::</span><span class=_5 style=color:light-dark(#47a,#fff);font-weight:500;margin:0;padding:0>HashMapEntry</span></code> or <code class="_2 language-rust" style="background:light-dark(#ddd,#000);border:1px solid light-dark(#999,#555);border-radius:.3em;color:light-dark(#000,#fff);margin:0;padding:0 .2em;-moz-tab-size:4;-o-tab-size:4;tab-size:4;white-space:pre-wrap"><span class=_5 style=color:light-dark(#47a,#fff);font-weight:500;margin:0;padding:0>std</span><span class=_3 style=color:light-dark(#85a,#f90);margin:0;padding:0>::</span><span class=_5 style=color:light-dark(#47a,#fff);font-weight:500;margin:0;padding:0>HashMap</span><span class=_3 style=color:light-dark(#85a,#f90);margin:0;padding:0>::</span><span class=_5 style=color:light-dark(#47a,#fff);font-weight:500;margin:0;padding:0>Entry</span></code>. (I don’t mind a small amount of nesting when it is very strongly associated, but still don’t really see much point.)</p><p style=grid-column:2;margin:0;padding:0>I much prefer C++‘s style where almost everything is just under <code class="_2 language-rust" style="background:light-dark(#ddd,#000);border:1px solid light-dark(#999,#555);border-radius:.3em;color:light-dark(#000,#fff);margin:0;padding:0 .2em;-moz-tab-size:4;-o-tab-size:4;tab-size:4;white-space:pre-wrap"><span class=_4 style=color:light-dark(#47a,#fff);font-weight:700;margin:0;padding:0>std</span></code>. For example <code class="_2 language-rust" style="background:light-dark(#ddd,#000);border:1px solid light-dark(#999,#555);border-radius:.3em;color:light-dark(#000,#fff);margin:0;padding:0 .2em;-moz-tab-size:4;-o-tab-size:4;tab-size:4;white-space:pre-wrap"><span class=_5 style=color:light-dark(#47a,#fff);font-weight:500;margin:0;padding:0>std</span><span class=_3 style=color:light-dark(#85a,#f90);margin:0;padding:0>::</span><span class=_4 style=color:light-dark(#47a,#fff);font-weight:700;margin:0;padding:0>unordered_map</span></code>. An iterator is slightly nested at <code class="_2 language-rust" style="background:light-dark(#ddd,#000);border:1px solid light-dark(#999,#555);border-radius:.3em;color:light-dark(#000,#fff);margin:0;padding:0 .2em;-moz-tab-size:4;-o-tab-size:4;tab-size:4;white-space:pre-wrap"><span class=_5 style=color:light-dark(#47a,#fff);font-weight:500;margin:0;padding:0>std</span><span class=_3 style=color:light-dark(#85a,#f90);margin:0;padding:0>::</span><span class=_5 style=color:light-dark(#47a,#fff);font-weight:500;margin:0;padding:0>unordered_map</span><span class=_3 style=color:light-dark(#85a,#f90);margin:0;padding:0>::</span><span class=_4 style=color:light-dark(#47a,#fff);font-weight:700;margin:0;padding:0>iterator</span></code> but if you ask me that is still a pretty reasonable name. And I would much rather see that in code than wonder what <code class="_2 language-rust" style="background:light-dark(#ddd,#000);border:1px solid light-dark(#999,#555);border-radius:.3em;color:light-dark(#000,#fff);margin:0;padding:0 .2em;-moz-tab-size:4;-o-tab-size:4;tab-size:4;white-space:pre-wrap"><span class=_4 style=color:light-dark(#47a,#fff);font-weight:700;margin:0;padding:0>iterator</span></code> we are working with.</p><p style=grid-column:2;margin:0;padding:0>If it hurts stop hitting yourself, don’t find a way to numb the pain.</p><h2 id=conflicts class=_8 style="grid-column:2;margin:.4em 0 0;padding:0"><a href=https://kevincox.ca/2025/07/20/no-imports/#conflicts style=color:inherit!important;margin:0;padding:0;text-decoration:inherit;text-shadow:inherit!important>Conflicts</a></h2><p style=grid-column:2;margin:0;padding:0>I see this come up as an argument against, and I am pretty dumbfounded. Some people see that you have <code class="_2 language-rust" style="background:light-dark(#ddd,#000);border:1px solid light-dark(#999,#555);border-radius:.3em;color:light-dark(#000,#fff);margin:0;padding:0 .2em;-moz-tab-size:4;-o-tab-size:4;tab-size:4;white-space:pre-wrap"><span class=_5 style=color:light-dark(#47a,#fff);font-weight:500;margin:0;padding:0>std</span><span class=_3 style=color:light-dark(#85a,#f90);margin:0;padding:0>::</span><span class=_5 style=color:light-dark(#47a,#fff);font-weight:500;margin:0;padding:0>io</span><span class=_3 style=color:light-dark(#85a,#f90);margin:0;padding:0>::</span><span class=_5 style=color:light-dark(#47a,#fff);font-weight:500;margin:0;padding:0>Error</span></code> and <code class="_2 language-rust" style="background:light-dark(#ddd,#000);border:1px solid light-dark(#999,#555);border-radius:.3em;color:light-dark(#000,#fff);margin:0;padding:0 .2em;-moz-tab-size:4;-o-tab-size:4;tab-size:4;white-space:pre-wrap"><span class=_5 style=color:light-dark(#47a,#fff);font-weight:500;margin:0;padding:0>std</span><span class=_3 style=color:light-dark(#85a,#f90);margin:0;padding:0>::</span><span class=_5 style=color:light-dark(#47a,#fff);font-weight:500;margin:0;padding:0>fmt</span><span class=_3 style=color:light-dark(#85a,#f90);margin:0;padding:0>::</span><span class=_5 style=color:light-dark(#47a,#fff);font-weight:500;margin:0;padding:0>Error</span></code>. So they can’t both be <code class="_2 language-rust" style="background:light-dark(#ddd,#000);border:1px solid light-dark(#999,#555);border-radius:.3em;color:light-dark(#000,#fff);margin:0;padding:0 .2em;-moz-tab-size:4;-o-tab-size:4;tab-size:4;white-space:pre-wrap"><span class=_5 style=color:light-dark(#47a,#fff);font-weight:500;margin:0;padding:0>std</span><span class=_3 style=color:light-dark(#85a,#f90);margin:0;padding:0>::</span><span class=_5 style=color:light-dark(#47a,#fff);font-weight:500;margin:0;padding:0>Error</span></code>. But that is kind of my point! When I see code referencing <code class="_2 language-rust" style="background:light-dark(#ddd,#000);border:1px solid light-dark(#999,#555);border-radius:.3em;color:light-dark(#000,#fff);margin:0;padding:0 .2em;-moz-tab-size:4;-o-tab-size:4;tab-size:4;white-space:pre-wrap"><span class=_5 style=color:light-dark(#47a,#fff);font-weight:500;margin:0;padding:0>Error</span></code> I don’t know which error type from the dozens of libraries that the code might be using it is! That is what makes the <code class="_2 language-rust" style="background:light-dark(#ddd,#000);border:1px solid light-dark(#999,#555);border-radius:.3em;color:light-dark(#000,#fff);margin:0;padding:0 .2em;-moz-tab-size:4;-o-tab-size:4;tab-size:4;white-space:pre-wrap">{<span class=_4 style=color:light-dark(#47a,#fff);font-weight:700;margin:0;padding:0>library</span>}<span class=_3 style=color:light-dark(#85a,#f90);margin:0;padding:0>::</span>{<span class=_4 style=color:light-dark(#47a,#fff);font-weight:700;margin:0;padding:0>name</span>}</code> pattern so great, it is conflict free. If I use that throughout my code I never have to worry if <code class="_2 language-rust" style="background:light-dark(#ddd,#000);border:1px solid light-dark(#999,#555);border-radius:.3em;color:light-dark(#000,#fff);margin:0;padding:0 .2em;-moz-tab-size:4;-o-tab-size:4;tab-size:4;white-space:pre-wrap"><span class=_5 style=color:light-dark(#47a,#fff);font-weight:500;margin:0;padding:0>Write</span></code> is <code class="_2 language-rust" style="background:light-dark(#ddd,#000);border:1px solid light-dark(#999,#555);border-radius:.3em;color:light-dark(#000,#fff);margin:0;padding:0 .2em;-moz-tab-size:4;-o-tab-size:4;tab-size:4;white-space:pre-wrap"><span class=_5 style=color:light-dark(#47a,#fff);font-weight:500;margin:0;padding:0>std</span><span class=_3 style=color:light-dark(#85a,#f90);margin:0;padding:0>::</span><span class=_5 style=color:light-dark(#47a,#fff);font-weight:500;margin:0;padding:0>fmt</span><span class=_3 style=color:light-dark(#85a,#f90);margin:0;padding:0>::</span><span class=_5 style=color:light-dark(#47a,#fff);font-weight:500;margin:0;padding:0>Write</span></code>, <code class="_2 language-rust" style="background:light-dark(#ddd,#000);border:1px solid light-dark(#999,#555);border-radius:.3em;color:light-dark(#000,#fff);margin:0;padding:0 .2em;-moz-tab-size:4;-o-tab-size:4;tab-size:4;white-space:pre-wrap"><span class=_5 style=color:light-dark(#47a,#fff);font-weight:500;margin:0;padding:0>std</span><span class=_3 style=color:light-dark(#85a,#f90);margin:0;padding:0>::</span><span class=_5 style=color:light-dark(#47a,#fff);font-weight:500;margin:0;padding:0>io</span><span class=_3 style=color:light-dark(#85a,#f90);margin:0;padding:0>::</span><span class=_5 style=color:light-dark(#47a,#fff);font-weight:500;margin:0;padding:0>Write</span></code> or <code class="_2 language-rust" style="background:light-dark(#ddd,#000);border:1px solid light-dark(#999,#555);border-radius:.3em;color:light-dark(#000,#fff);margin:0;padding:0 .2em;-moz-tab-size:4;-o-tab-size:4;tab-size:4;white-space:pre-wrap"><span class=_3 style=color:light-dark(#85a,#f90);margin:0;padding:0>crate::</span><span class=_5 style=color:light-dark(#47a,#fff);font-weight:500;margin:0;padding:0>Write</span></code> because it is spelt out! I would much rather the types be actually named <code class="_2 language-rust" style="background:light-dark(#ddd,#000);border:1px solid light-dark(#999,#555);border-radius:.3em;color:light-dark(#000,#fff);margin:0;padding:0 .2em;-moz-tab-size:4;-o-tab-size:4;tab-size:4;white-space:pre-wrap"><span class=_5 style=color:light-dark(#47a,#fff);font-weight:500;margin:0;padding:0>std</span><span class=_3 style=color:light-dark(#85a,#f90);margin:0;padding:0>::</span><span class=_5 style=color:light-dark(#47a,#fff);font-weight:500;margin:0;padding:0>FmtWrite</span></code> and have everyone “agree” on the name (because they have no choice) than half the time having it called <code class="_2 language-rust" style="background:light-dark(#ddd,#000);border:1px solid light-dark(#999,#555);border-radius:.3em;color:light-dark(#000,#fff);margin:0;padding:0 .2em;-moz-tab-size:4;-o-tab-size:4;tab-size:4;white-space:pre-wrap"><span class=_5 style=color:light-dark(#47a,#fff);font-weight:500;margin:0;padding:0>Write</span></code> and half the time having it called <code class="_2 language-rust" style="background:light-dark(#ddd,#000);border:1px solid light-dark(#999,#555);border-radius:.3em;color:light-dark(#000,#fff);margin:0;padding:0 .2em;-moz-tab-size:4;-o-tab-size:4;tab-size:4;white-space:pre-wrap"><span class=_5 style=color:light-dark(#47a,#fff);font-weight:500;margin:0;padding:0>FmtWrite</span></code>, <code class="_2 language-rust" style="background:light-dark(#ddd,#000);border:1px solid light-dark(#999,#555);border-radius:.3em;color:light-dark(#000,#fff);margin:0;padding:0 .2em;-moz-tab-size:4;-o-tab-size:4;tab-size:4;white-space:pre-wrap"><span class=_5 style=color:light-dark(#47a,#fff);font-weight:500;margin:0;padding:0>WriteFmt</span></code> or <code class="_2 language-rust" style="background:light-dark(#ddd,#000);border:1px solid light-dark(#999,#555);border-radius:.3em;color:light-dark(#000,#fff);margin:0;padding:0 .2em;-moz-tab-size:4;-o-tab-size:4;tab-size:4;white-space:pre-wrap"><span class=_5 style=color:light-dark(#47a,#fff);font-weight:500;margin:0;padding:0>DebugWrite</span></code> (because some other <code class="_2 language-rust" style="background:light-dark(#ddd,#000);border:1px solid light-dark(#999,#555);border-radius:.3em;color:light-dark(#000,#fff);margin:0;padding:0 .2em;-moz-tab-size:4;-o-tab-size:4;tab-size:4;white-space:pre-wrap"><span class=_5 style=color:light-dark(#47a,#fff);font-weight:500;margin:0;padding:0>Write</span></code> was in scope and the programmer needed to come up with their own name).</p><p style=grid-column:2;margin:0;padding:0>This isn’t an imagined problem. I regularly find myself reading <a href=https://doc.rust-lang.org/rustdoc/what-is-rustdoc.html class=_a style=color:inherit;margin:0;padding:0;text-decoration:underline;-webkit-text-decoration-color:gray;text-decoration-color:gray>rustdoc</a> documentation and needing to hover over a type name so that my browser displays the fully qualified path in the link target! I feel that which <code class="_2 language-rust" style="background:light-dark(#ddd,#000);border:1px solid light-dark(#999,#555);border-radius:.3em;color:light-dark(#000,#fff);margin:0;padding:0 .2em;-moz-tab-size:4;-o-tab-size:4;tab-size:4;white-space:pre-wrap"><span class=_5 style=color:light-dark(#47a,#fff);font-weight:500;margin:0;padding:0>Error</span></code> or which <code class="_2 language-rust" style="background:light-dark(#ddd,#000);border:1px solid light-dark(#999,#555);border-radius:.3em;color:light-dark(#000,#fff);margin:0;padding:0 .2em;-moz-tab-size:4;-o-tab-size:4;tab-size:4;white-space:pre-wrap"><span class=_5 style=color:light-dark(#47a,#fff);font-weight:500;margin:0;padding:0>Write</span></code> is being used is important enough that it be immediately visible, in both code and generated documentation.</p><p style=grid-column:2;margin:0;padding:0>Avoiding imports means that the library authors are responsible for solving conflicts. And we are back to the fact that when I see a type or function it is unambiguous and I don’t have to waste brain cycles figuring out what <code class="_2 language-rust" style="background:light-dark(#ddd,#000);border:1px solid light-dark(#999,#555);border-radius:.3em;color:light-dark(#000,#fff);margin:0;padding:0 .2em;-moz-tab-size:4;-o-tab-size:4;tab-size:4;white-space:pre-wrap"><span class=_5 style=color:light-dark(#47a,#fff);font-weight:500;margin:0;padding:0>Error</span></code> I am looking at.</p><h2 id=exceptions class=_8 style="grid-column:2;margin:.4em 0 0;padding:0"><a href=https://kevincox.ca/2025/07/20/no-imports/#exceptions style=color:inherit!important;margin:0;padding:0;text-decoration:inherit;text-shadow:inherit!important>But Sometimes They Are <strong style=margin:0;padding:0>Really</strong> Long</a></h2><p style=grid-column:2;margin:0;padding:0>I’m not a stickler to this rule. There are some cases where I will use imports. Sometimes the names are just too long and repetitive. In that case I strongly prefer a local <code class="_2 language-rust" style="background:light-dark(#ddd,#000);border:1px solid light-dark(#999,#555);border-radius:.3em;color:light-dark(#000,#fff);margin:0;padding:0 .2em;-moz-tab-size:4;-o-tab-size:4;tab-size:4;white-space:pre-wrap"><span class=_3 style=color:light-dark(#85a,#f90);margin:0;padding:0>use</span></code> with a short scope. This ensures that I don’t need to look far to resolve the ambiguity.</p><pre class=_1 style="background:light-dark(#ddd,#222);border-block:1px solid light-dark(#aaa,#444);display:grid;grid-column:2;grid-column:1/4;grid-template-columns:subgrid;justify-content:flex-start;margin:0;overflow:auto hidden;padding:.4em 0"><code class="_2 language-rust" style=color:light-dark(#222,#ddd);grid-column:2;margin:0;padding:0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;white-space:pre><span class=_3 style=color:light-dark(#85a,#f90);margin:0;padding:0>use</span> <span class=_5 style=color:light-dark(#47a,#fff);font-weight:500;margin:0;padding:0>sentry</span><span class=_3 style=color:light-dark(#85a,#f90);margin:0;padding:0>::</span><span class=_5 style=color:light-dark(#47a,#fff);font-weight:500;margin:0;padding:0>integrations</span><span class=_3 style=color:light-dark(#85a,#f90);margin:0;padding:0>::</span><span class=_5 style=color:light-dark(#47a,#fff);font-weight:500;margin:0;padding:0>tracing</span><span class=_3 style=color:light-dark(#85a,#f90);margin:0;padding:0>::</span><span class=_5 style=color:light-dark(#47a,#fff);font-weight:500;margin:0;padding:0>EventFilter</span>;
<span class=_3 style=color:light-dark(#85a,#f90);margin:0;padding:0>match</span> <span class=_3 style=color:light-dark(#85a,#f90);margin:0;padding:0>*</span><span class=_4 style=color:light-dark(#47a,#fff);font-weight:700;margin:0;padding:0>meta</span><span class=_3 style=color:light-dark(#85a,#f90);margin:0;padding:0>.</span><span class=_5 style=color:light-dark(#47a,#fff);font-weight:500;margin:0;padding:0>level</span>() {
	<span class=_5 style=color:light-dark(#47a,#fff);font-weight:500;margin:0;padding:0>tracing</span><span class=_3 style=color:light-dark(#85a,#f90);margin:0;padding:0>::</span><span class=_5 style=color:light-dark(#47a,#fff);font-weight:500;margin:0;padding:0>Level</span><span class=_3 style=color:light-dark(#85a,#f90);margin:0;padding:0>::</span><span class=_b style=color:light-dark(#85a,#f90);font-weight:500;margin:0;padding:0>ERROR</span> <span class=_3 style=color:light-dark(#85a,#f90);margin:0;padding:0>=&gt;</span> <span class=_5 style=color:light-dark(#47a,#fff);font-weight:500;margin:0;padding:0>EventFilter</span><span class=_3 style=color:light-dark(#85a,#f90);margin:0;padding:0>::</span><span class=_5 style=color:light-dark(#47a,#fff);font-weight:500;margin:0;padding:0>Event</span>,
	<span class=_5 style=color:light-dark(#47a,#fff);font-weight:500;margin:0;padding:0>tracing</span><span class=_3 style=color:light-dark(#85a,#f90);margin:0;padding:0>::</span><span class=_5 style=color:light-dark(#47a,#fff);font-weight:500;margin:0;padding:0>Level</span><span class=_3 style=color:light-dark(#85a,#f90);margin:0;padding:0>::</span><span class=_b style=color:light-dark(#85a,#f90);font-weight:500;margin:0;padding:0>WARN</span> <span class=_3 style=color:light-dark(#85a,#f90);margin:0;padding:0>=&gt;</span> {
		<span class=_3 style=color:light-dark(#85a,#f90);margin:0;padding:0>if</span> <span class=_4 style=color:light-dark(#47a,#fff);font-weight:700;margin:0;padding:0>module</span><span class=_3 style=color:light-dark(#85a,#f90);margin:0;padding:0>.</span><span class=_5 style=color:light-dark(#47a,#fff);font-weight:500;margin:0;padding:0>starts_with</span>(<span class=_6 style=color:light-dark(#780,#bd3);margin:0;padding:0><span class=_6 style=color:light-dark(#780,#bd3);margin:0;padding:0>"</span>hickory_proto::<span class=_6 style=color:light-dark(#780,#bd3);margin:0;padding:0>"</span></span>) {
			<span class=_5 style=color:light-dark(#47a,#fff);font-weight:500;margin:0;padding:0>EventFilter</span><span class=_3 style=color:light-dark(#85a,#f90);margin:0;padding:0>::</span><span class=_5 style=color:light-dark(#47a,#fff);font-weight:500;margin:0;padding:0>Ignore</span>
		} <span class=_3 style=color:light-dark(#85a,#f90);margin:0;padding:0>else</span> {
			<span class=_5 style=color:light-dark(#47a,#fff);font-weight:500;margin:0;padding:0>EventFilter</span><span class=_3 style=color:light-dark(#85a,#f90);margin:0;padding:0>::</span><span class=_5 style=color:light-dark(#47a,#fff);font-weight:500;margin:0;padding:0>Event</span>
		}
	}
	<span class=_5 style=color:light-dark(#47a,#fff);font-weight:500;margin:0;padding:0>tracing</span><span class=_3 style=color:light-dark(#85a,#f90);margin:0;padding:0>::</span><span class=_5 style=color:light-dark(#47a,#fff);font-weight:500;margin:0;padding:0>Level</span><span class=_3 style=color:light-dark(#85a,#f90);margin:0;padding:0>::</span><span class=_b style=color:light-dark(#85a,#f90);font-weight:500;margin:0;padding:0>INFO</span> <span class=_3 style=color:light-dark(#85a,#f90);margin:0;padding:0>=&gt;</span> <span class=_5 style=color:light-dark(#47a,#fff);font-weight:500;margin:0;padding:0>EventFilter</span><span class=_3 style=color:light-dark(#85a,#f90);margin:0;padding:0>::</span><span class=_5 style=color:light-dark(#47a,#fff);font-weight:500;margin:0;padding:0>Ignore</span>,
	<span class=_5 style=color:light-dark(#47a,#fff);font-weight:500;margin:0;padding:0>tracing</span><span class=_3 style=color:light-dark(#85a,#f90);margin:0;padding:0>::</span><span class=_5 style=color:light-dark(#47a,#fff);font-weight:500;margin:0;padding:0>Level</span><span class=_3 style=color:light-dark(#85a,#f90);margin:0;padding:0>::</span><span class=_b style=color:light-dark(#85a,#f90);font-weight:500;margin:0;padding:0>DEBUG</span> <span class=_3 style=color:light-dark(#85a,#f90);margin:0;padding:0>=&gt;</span> <span class=_5 style=color:light-dark(#47a,#fff);font-weight:500;margin:0;padding:0>EventFilter</span><span class=_3 style=color:light-dark(#85a,#f90);margin:0;padding:0>::</span><span class=_5 style=color:light-dark(#47a,#fff);font-weight:500;margin:0;padding:0>Ignore</span>,
	<span class=_5 style=color:light-dark(#47a,#fff);font-weight:500;margin:0;padding:0>tracing</span><span class=_3 style=color:light-dark(#85a,#f90);margin:0;padding:0>::</span><span class=_5 style=color:light-dark(#47a,#fff);font-weight:500;margin:0;padding:0>Level</span><span class=_3 style=color:light-dark(#85a,#f90);margin:0;padding:0>::</span><span class=_b style=color:light-dark(#85a,#f90);font-weight:500;margin:0;padding:0>TRACE</span> <span class=_3 style=color:light-dark(#85a,#f90);margin:0;padding:0>=&gt;</span> <span class=_5 style=color:light-dark(#47a,#fff);font-weight:500;margin:0;padding:0>EventFilter</span><span class=_3 style=color:light-dark(#85a,#f90);margin:0;padding:0>::</span><span class=_5 style=color:light-dark(#47a,#fff);font-weight:500;margin:0;padding:0>Ignore</span>,
}
</code></pre><p style=grid-column:2;margin:0;padding:0>(And more often than not when I run into this case it isn’t because the name is intrinsically high-entropy, but because someone got carried away with hierarchy. <em style=margin:0;padding:0>Stop hitting yourself.</em>)</p><h2 id=rust class=_8 style="grid-column:2;margin:.4em 0 0;padding:0"><a href=https://kevincox.ca/2025/07/20/no-imports/#rust style=color:inherit!important;margin:0;padding:0;text-decoration:inherit;text-shadow:inherit!important>Rust-Specific Notes</a></h2><p style=grid-column:2;margin:0;padding:0>I’ve been using Rust as an example but been trying to avoid being Rust-specific. But it is my primary language, so I do have some thoughts to share. If you don’t care about Rust you can stop reading now.</p><p style=grid-column:2;margin:0;padding:0>Avoiding imports in Rust is going against the grain. I would recommend that all crates <a href=https://kevincox.ca/2025/07/20/no-imports/#rust-flat-api class=_a style=color:inherit;margin:0;padding:0;text-decoration:underline;-webkit-text-decoration-color:gray;text-decoration-color:gray>provide a flat API</a>. However, for many projects avoiding <code class="_2 language-rust" style="background:light-dark(#ddd,#000);border:1px solid light-dark(#999,#555);border-radius:.3em;color:light-dark(#000,#fff);margin:0;padding:0 .2em;-moz-tab-size:4;-o-tab-size:4;tab-size:4;white-space:pre-wrap"><span class=_3 style=color:light-dark(#85a,#f90);margin:0;padding:0>use</span></code> isn’t the right choice. Just follow the common coding convention and be happy. To see a real-world example of both a flat API and avoiding <code class="_2 language-rust" style="background:light-dark(#ddd,#000);border:1px solid light-dark(#999,#555);border-radius:.3em;color:light-dark(#000,#fff);margin:0;padding:0 .2em;-moz-tab-size:4;-o-tab-size:4;tab-size:4;white-space:pre-wrap"><span class=_3 style=color:light-dark(#85a,#f90);margin:0;padding:0>use</span></code> you can check out <a href=https://gitlab.com/kevincox/rl-core class=_a style=color:inherit;margin:0;padding:0;text-decoration:underline;-webkit-text-decoration-color:gray;text-decoration-color:gray>rl-core</a> which is a small project where I am quite glad I chose to break convention. A lot of my “for-me” or “internal” code is written in a similar style.</p><h3 id=rust-flat-api class=_8 style="grid-column:2;margin:.4em 0 0;padding:0"><a href=https://kevincox.ca/2025/07/20/no-imports/#rust-flat-api style=color:inherit!important;margin:0;padding:0;text-decoration:inherit;text-shadow:inherit!important>Provide a Flat API</a></h3><p style=grid-column:2;margin:0;padding:0>By default, Rust wants to ship your file structure as your exports (<a href=https://en.wikipedia.org/wiki/Conway%27s_law class=_a style=color:inherit;margin:0;padding:0;text-decoration:underline;-webkit-text-decoration-color:gray;text-decoration-color:gray>Conway’s law</a>). So for example <code class="_2 language-rust" style="background:light-dark(#ddd,#000);border:1px solid light-dark(#999,#555);border-radius:.3em;color:light-dark(#000,#fff);margin:0;padding:0 .2em;-moz-tab-size:4;-o-tab-size:4;tab-size:4;white-space:pre-wrap"><span class=_5 style=color:light-dark(#47a,#fff);font-weight:500;margin:0;padding:0>mylib</span><span class=_3 style=color:light-dark(#85a,#f90);margin:0;padding:0>::</span><span class=_5 style=color:light-dark(#47a,#fff);font-weight:500;margin:0;padding:0>Foo</span></code> needs to be defined in <code class="_2 language-text" style="background:light-dark(#ddd,#000);border:1px solid light-dark(#999,#555);border-radius:.3em;color:light-dark(#000,#fff);margin:0;padding:0 .2em;-moz-tab-size:4;-o-tab-size:4;tab-size:4;white-space:pre-wrap">src/lib.rs</code> and <code class="_2 language-rust" style="background:light-dark(#ddd,#000);border:1px solid light-dark(#999,#555);border-radius:.3em;color:light-dark(#000,#fff);margin:0;padding:0 .2em;-moz-tab-size:4;-o-tab-size:4;tab-size:4;white-space:pre-wrap"><span class=_5 style=color:light-dark(#47a,#fff);font-weight:500;margin:0;padding:0>mylib</span><span class=_3 style=color:light-dark(#85a,#f90);margin:0;padding:0>::</span><span class=_5 style=color:light-dark(#47a,#fff);font-weight:500;margin:0;padding:0>submodule</span><span class=_3 style=color:light-dark(#85a,#f90);margin:0;padding:0>::</span><span class=_5 style=color:light-dark(#47a,#fff);font-weight:500;margin:0;padding:0>Bar</span></code> needs to be defined in <code class="_2 language-text" style="background:light-dark(#ddd,#000);border:1px solid light-dark(#999,#555);border-radius:.3em;color:light-dark(#000,#fff);margin:0;padding:0 .2em;-moz-tab-size:4;-o-tab-size:4;tab-size:4;white-space:pre-wrap">src/submodule.rs</code> or <code class="_2 language-text" style="background:light-dark(#ddd,#000);border:1px solid light-dark(#999,#555);border-radius:.3em;color:light-dark(#000,#fff);margin:0;padding:0 .2em;-moz-tab-size:4;-o-tab-size:4;tab-size:4;white-space:pre-wrap">src/submodule/mod.rs</code>.</p><p style=grid-column:2;margin:0;padding:0>With a single re-export per module this can be solved!</p><pre class=_1 style="background:light-dark(#ddd,#222);border-block:1px solid light-dark(#aaa,#444);display:grid;grid-column:2;grid-column:1/4;grid-template-columns:subgrid;justify-content:flex-start;margin:0;overflow:auto hidden;padding:.4em 0"><code class="_2 language-rust" style=color:light-dark(#222,#ddd);grid-column:2;margin:0;padding:0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;white-space:pre><span class=_3 style=color:light-dark(#85a,#f90);margin:0;padding:0>mod</span> <span class=_5 style=color:light-dark(#47a,#fff);font-weight:500;margin:0;padding:0>config</span>; <span class=_3 style=color:light-dark(#85a,#f90);margin:0;padding:0>pub</span> <span class=_3 style=color:light-dark(#85a,#f90);margin:0;padding:0>use</span> <span class=_5 style=color:light-dark(#47a,#fff);font-weight:500;margin:0;padding:0>config</span><span class=_3 style=color:light-dark(#85a,#f90);margin:0;padding:0>::*</span>;
<span class=_3 style=color:light-dark(#85a,#f90);margin:0;padding:0>mod</span> <span class=_5 style=color:light-dark(#47a,#fff);font-weight:500;margin:0;padding:0>tracker</span>; <span class=_3 style=color:light-dark(#85a,#f90);margin:0;padding:0>pub</span> <span class=_3 style=color:light-dark(#85a,#f90);margin:0;padding:0>use</span> <span class=_5 style=color:light-dark(#47a,#fff);font-weight:500;margin:0;padding:0>tracker</span><span class=_3 style=color:light-dark(#85a,#f90);margin:0;padding:0>::*</span>;
</code></pre><p style=grid-column:2;margin:0;padding:0>One extra statement to free you from hierarchy. Re-exports are well-supported by Rust so rustdoc and the compiler will pick <code class="_2 language-rust" style="background:light-dark(#ddd,#000);border:1px solid light-dark(#999,#555);border-radius:.3em;color:light-dark(#000,#fff);margin:0;padding:0 .2em;-moz-tab-size:4;-o-tab-size:4;tab-size:4;white-space:pre-wrap"><span class=_5 style=color:light-dark(#47a,#fff);font-weight:500;margin:0;padding:0>mylib</span><span class=_3 style=color:light-dark(#85a,#f90);margin:0;padding:0>::</span><span class=_5 style=color:light-dark(#47a,#fff);font-weight:500;margin:0;padding:0>Config</span></code> as the path to show users rather than <code class="_2 language-rust" style="background:light-dark(#ddd,#000);border:1px solid light-dark(#999,#555);border-radius:.3em;color:light-dark(#000,#fff);margin:0;padding:0 .2em;-moz-tab-size:4;-o-tab-size:4;tab-size:4;white-space:pre-wrap"><span class=_5 style=color:light-dark(#47a,#fff);font-weight:500;margin:0;padding:0>mylib</span><span class=_3 style=color:light-dark(#85a,#f90);margin:0;padding:0>::</span><span class=_5 style=color:light-dark(#47a,#fff);font-weight:500;margin:0;padding:0>config</span><span class=_3 style=color:light-dark(#85a,#f90);margin:0;padding:0>::</span><span class=_5 style=color:light-dark(#47a,#fff);font-weight:500;margin:0;padding:0>Config</span></code>. (I don’t know the exact heuristics but when working like this there will be a single public export, so there isn’t much cleverness needed.)</p><p style=grid-column:2;margin:0;padding:0><code class="_2 language-rust" style="background:light-dark(#ddd,#000);border:1px solid light-dark(#999,#555);border-radius:.3em;color:light-dark(#000,#fff);margin:0;padding:0 .2em;-moz-tab-size:4;-o-tab-size:4;tab-size:4;white-space:pre-wrap"><span class=_3 style=color:light-dark(#85a,#f90);margin:0;padding:0>pub</span> <span class=_3 style=color:light-dark(#85a,#f90);margin:0;padding:0>use</span></code> also only sets the <strong style=margin:0;padding:0>maximum</strong> export visibility. This means that <code class="_2 language-rust" style="background:light-dark(#ddd,#000);border:1px solid light-dark(#999,#555);border-radius:.3em;color:light-dark(#000,#fff);margin:0;padding:0 .2em;-moz-tab-size:4;-o-tab-size:4;tab-size:4;white-space:pre-wrap"><span class=_3 style=color:light-dark(#85a,#f90);margin:0;padding:0>pub</span></code>, <code class="_2 language-rust" style="background:light-dark(#ddd,#000);border:1px solid light-dark(#999,#555);border-radius:.3em;color:light-dark(#000,#fff);margin:0;padding:0 .2em;-moz-tab-size:4;-o-tab-size:4;tab-size:4;white-space:pre-wrap"><span class=_3 style=color:light-dark(#85a,#f90);margin:0;padding:0>pub</span>(<span class=_3 style=color:light-dark(#85a,#f90);margin:0;padding:0>crate</span>)</code> and private things in the module will have the expected visibility. So the definitions in the files look the same as they normally would and the export lines only need to be added when you add or remove modules.</p><p style=grid-column:2;margin:0;padding:0>The only real downside of this approach is the <code class="_2 language-text" style="background:light-dark(#ddd,#000);border:1px solid light-dark(#999,#555);border-radius:.3em;color:light-dark(#000,#fff);margin:0;padding:0 .2em;-moz-tab-size:4;-o-tab-size:4;tab-size:4;white-space:pre-wrap">src/lib.rs</code> will see everything as local and can reference <code class="_2 language-rust" style="background:light-dark(#ddd,#000);border:1px solid light-dark(#999,#555);border-radius:.3em;color:light-dark(#000,#fff);margin:0;padding:0 .2em;-moz-tab-size:4;-o-tab-size:4;tab-size:4;white-space:pre-wrap"><span class=_5 style=color:light-dark(#47a,#fff);font-weight:500;margin:0;padding:0>Config</span></code> rather than <code class="_2 language-rust" style="background:light-dark(#ddd,#000);border:1px solid light-dark(#999,#555);border-radius:.3em;color:light-dark(#000,#fff);margin:0;padding:0 .2em;-moz-tab-size:4;-o-tab-size:4;tab-size:4;white-space:pre-wrap"><span class=_3 style=color:light-dark(#85a,#f90);margin:0;padding:0>crate::</span><span class=_5 style=color:light-dark(#47a,#fff);font-weight:500;margin:0;padding:0>Config</span></code>. But this is a very minor issue and the solution is not to put anything into <code class="_2 language-text" style="background:light-dark(#ddd,#000);border:1px solid light-dark(#999,#555);border-radius:.3em;color:light-dark(#000,#fff);margin:0;padding:0 .2em;-moz-tab-size:4;-o-tab-size:4;tab-size:4;white-space:pre-wrap">lib.rs</code>. (Keeping only module definitions and re-exports in <code class="_2 language-text" style="background:light-dark(#ddd,#000);border:1px solid light-dark(#999,#555);border-radius:.3em;color:light-dark(#000,#fff);margin:0;padding:0 .2em;-moz-tab-size:4;-o-tab-size:4;tab-size:4;white-space:pre-wrap">lib.rs</code> is a very common pattern anyways.)</p><p style=grid-column:2;margin:0;padding:0>The nice thing about just re-exporting everything is that it also frees you from deciding where to put things based on what you want the exported name to be. You don’t need to put <code class="_2 language-rust" style="background:light-dark(#ddd,#000);border:1px solid light-dark(#999,#555);border-radius:.3em;color:light-dark(#000,#fff);margin:0;padding:0 .2em;-moz-tab-size:4;-o-tab-size:4;tab-size:4;white-space:pre-wrap"><span class=_5 style=color:light-dark(#47a,#fff);font-weight:500;margin:0;padding:0>std</span><span class=_3 style=color:light-dark(#85a,#f90);margin:0;padding:0>::</span><span class=_5 style=color:light-dark(#47a,#fff);font-weight:500;margin:0;padding:0>collections</span><span class=_3 style=color:light-dark(#85a,#f90);margin:0;padding:0>::</span><span class=_5 style=color:light-dark(#47a,#fff);font-weight:500;margin:0;padding:0>hash_map</span><span class=_3 style=color:light-dark(#85a,#f90);margin:0;padding:0>::</span><span class=_5 style=color:light-dark(#47a,#fff);font-weight:500;margin:0;padding:0>HashMap</span></code> and <code class="_2 language-rust" style="background:light-dark(#ddd,#000);border:1px solid light-dark(#999,#555);border-radius:.3em;color:light-dark(#000,#fff);margin:0;padding:0 .2em;-moz-tab-size:4;-o-tab-size:4;tab-size:4;white-space:pre-wrap"><span class=_5 style=color:light-dark(#47a,#fff);font-weight:500;margin:0;padding:0>std</span><span class=_3 style=color:light-dark(#85a,#f90);margin:0;padding:0>::</span><span class=_5 style=color:light-dark(#47a,#fff);font-weight:500;margin:0;padding:0>collections</span><span class=_3 style=color:light-dark(#85a,#f90);margin:0;padding:0>::</span><span class=_5 style=color:light-dark(#47a,#fff);font-weight:500;margin:0;padding:0>hash_map</span><span class=_3 style=color:light-dark(#85a,#f90);margin:0;padding:0>::</span><span class=_5 style=color:light-dark(#47a,#fff);font-weight:500;margin:0;padding:0>Entry</span></code> in the same file to have them appear at the same place, you can put them into separate files if you wish or the same file if you need access to non-public APIs. (The Rust <code class="_2 language-rust" style="background:light-dark(#ddd,#000);border:1px solid light-dark(#999,#555);border-radius:.3em;color:light-dark(#000,#fff);margin:0;padding:0 .2em;-moz-tab-size:4;-o-tab-size:4;tab-size:4;white-space:pre-wrap"><span class=_4 style=color:light-dark(#47a,#fff);font-weight:700;margin:0;padding:0>std</span></code> implementation does actually re-export these from various places, re-exports are very common and not something to be feared.)</p><p style=grid-column:2;margin:0;padding:0>Personally I just put every single type and most functions into separate files. I find that it keeps everything simple and organized and I prefer switching between different files than different parts of a file when going back and forth. So for example in rl-core I define <code class="_2 language-rust" style="background:light-dark(#ddd,#000);border:1px solid light-dark(#999,#555);border-radius:.3em;color:light-dark(#000,#fff);margin:0;padding:0 .2em;-moz-tab-size:4;-o-tab-size:4;tab-size:4;white-space:pre-wrap"><span class=_5 style=color:light-dark(#47a,#fff);font-weight:500;margin:0;padding:0>rl_core</span><span class=_3 style=color:light-dark(#85a,#f90);margin:0;padding:0>::</span><span class=_5 style=color:light-dark(#47a,#fff);font-weight:500;margin:0;padding:0>Config</span></code> in <code class="_2 language-text" style="background:light-dark(#ddd,#000);border:1px solid light-dark(#999,#555);border-radius:.3em;color:light-dark(#000,#fff);margin:0;padding:0 .2em;-moz-tab-size:4;-o-tab-size:4;tab-size:4;white-space:pre-wrap">src/config.rs</code> and <code class="_2 language-rust" style="background:light-dark(#ddd,#000);border:1px solid light-dark(#999,#555);border-radius:.3em;color:light-dark(#000,#fff);margin:0;padding:0 .2em;-moz-tab-size:4;-o-tab-size:4;tab-size:4;white-space:pre-wrap"><span class=_5 style=color:light-dark(#47a,#fff);font-weight:500;margin:0;padding:0>rl_core</span><span class=_3 style=color:light-dark(#85a,#f90);margin:0;padding:0>::</span><span class=_5 style=color:light-dark(#47a,#fff);font-weight:500;margin:0;padding:0>Tracker</span></code> in <code class="_2 language-text" style="background:light-dark(#ddd,#000);border:1px solid light-dark(#999,#555);border-radius:.3em;color:light-dark(#000,#fff);margin:0;padding:0 .2em;-moz-tab-size:4;-o-tab-size:4;tab-size:4;white-space:pre-wrap">lib/tracker.rs</code>.</p><h3 id=rust-traits class=_8 style="grid-column:2;margin:.4em 0 0;padding:0"><a href=https://kevincox.ca/2025/07/20/no-imports/#rust-traits style=color:inherit!important;margin:0;padding:0;text-decoration:inherit;text-shadow:inherit!important>Traits</a></h3><p style=grid-column:2;margin:0;padding:0>Traits are where things start to get hairy. To call a trait method as a method the trait needs to be in scope. So if you want to call <code class="_2 language-rust" style="background:light-dark(#ddd,#000);border:1px solid light-dark(#999,#555);border-radius:.3em;color:light-dark(#000,#fff);margin:0;padding:0 .2em;-moz-tab-size:4;-o-tab-size:4;tab-size:4;white-space:pre-wrap"><span class=_4 style=color:light-dark(#47a,#fff);font-weight:700;margin:0;padding:0>foo</span><span class=_3 style=color:light-dark(#85a,#f90);margin:0;padding:0>.</span><span class=_5 style=color:light-dark(#47a,#fff);font-weight:500;margin:0;padding:0>a_method</span>()</code> where <code class="_2 language-rust" style="background:light-dark(#ddd,#000);border:1px solid light-dark(#999,#555);border-radius:.3em;color:light-dark(#000,#fff);margin:0;padding:0 .2em;-moz-tab-size:4;-o-tab-size:4;tab-size:4;white-space:pre-wrap"><span class=_4 style=color:light-dark(#47a,#fff);font-weight:700;margin:0;padding:0>a_method</span></code> is <code class="_2 language-rust" style="background:light-dark(#ddd,#000);border:1px solid light-dark(#999,#555);border-radius:.3em;color:light-dark(#000,#fff);margin:0;padding:0 .2em;-moz-tab-size:4;-o-tab-size:4;tab-size:4;white-space:pre-wrap"><span class=_5 style=color:light-dark(#47a,#fff);font-weight:500;margin:0;padding:0>somelib</span><span class=_3 style=color:light-dark(#85a,#f90);margin:0;padding:0>::</span><span class=_5 style=color:light-dark(#47a,#fff);font-weight:500;margin:0;padding:0>TheType</span><span class=_3 style=color:light-dark(#85a,#f90);margin:0;padding:0>::</span><span class=_4 style=color:light-dark(#47a,#fff);font-weight:700;margin:0;padding:0>a_method</span></code> you need to <code class="_2 language-rust" style="background:light-dark(#ddd,#000);border:1px solid light-dark(#999,#555);border-radius:.3em;color:light-dark(#000,#fff);margin:0;padding:0 .2em;-moz-tab-size:4;-o-tab-size:4;tab-size:4;white-space:pre-wrap"><span class=_3 style=color:light-dark(#85a,#f90);margin:0;padding:0>use</span> <span class=_5 style=color:light-dark(#47a,#fff);font-weight:500;margin:0;padding:0>somelib</span><span class=_3 style=color:light-dark(#85a,#f90);margin:0;padding:0>::</span><span class=_5 style=color:light-dark(#47a,#fff);font-weight:500;margin:0;padding:0>TheType</span></code>.</p><p style=grid-column:2;margin:0;padding:0>However you always have the option to call the method using a fully-qualified path. For example <code class="_2 language-rust" style="background:light-dark(#ddd,#000);border:1px solid light-dark(#999,#555);border-radius:.3em;color:light-dark(#000,#fff);margin:0;padding:0 .2em;-moz-tab-size:4;-o-tab-size:4;tab-size:4;white-space:pre-wrap"><span class=_5 style=color:light-dark(#47a,#fff);font-weight:500;margin:0;padding:0>somelib</span><span class=_3 style=color:light-dark(#85a,#f90);margin:0;padding:0>::</span><span class=_5 style=color:light-dark(#47a,#fff);font-weight:500;margin:0;padding:0>TheType</span><span class=_3 style=color:light-dark(#85a,#f90);margin:0;padding:0>::</span><span class=_5 style=color:light-dark(#47a,#fff);font-weight:500;margin:0;padding:0>a_method</span>(<span class=_3 style=color:light-dark(#85a,#f90);margin:0;padding:0>&amp;</span><span class=_4 style=color:light-dark(#47a,#fff);font-weight:700;margin:0;padding:0>foo</span>)</code> or <code class="_2 language-rust" style="background:light-dark(#ddd,#000);border:1px solid light-dark(#999,#555);border-radius:.3em;color:light-dark(#000,#fff);margin:0;padding:0 .2em;-moz-tab-size:4;-o-tab-size:4;tab-size:4;white-space:pre-wrap">&lt;<span class=_5 style=color:light-dark(#47a,#fff);font-weight:500;margin:0;padding:0>Foo</span> <span class=_3 style=color:light-dark(#85a,#f90);margin:0;padding:0>as</span> <span class=_5 style=color:light-dark(#47a,#fff);font-weight:500;margin:0;padding:0>somelib</span><span class=_3 style=color:light-dark(#85a,#f90);margin:0;padding:0>::</span><span class=_5 style=color:light-dark(#47a,#fff);font-weight:500;margin:0;padding:0>TheType</span>&gt;<span class=_3 style=color:light-dark(#85a,#f90);margin:0;padding:0>::</span><span class=_5 style=color:light-dark(#47a,#fff);font-weight:500;margin:0;padding:0>static_func</span>()</code>. For the occasional method call I often do this because I do like seeing where <code class="_2 language-rust" style="background:light-dark(#ddd,#000);border:1px solid light-dark(#999,#555);border-radius:.3em;color:light-dark(#000,#fff);margin:0;padding:0 .2em;-moz-tab-size:4;-o-tab-size:4;tab-size:4;white-space:pre-wrap"><span class=_3 style=color:light-dark(#85a,#f90);margin:0;padding:0>.</span><span class=_5 style=color:light-dark(#47a,#fff);font-weight:500;margin:0;padding:0>a_method</span>()</code> comes from, it isn’t always clear if it is a method directly on <code class="_2 language-rust" style="background:light-dark(#ddd,#000);border:1px solid light-dark(#999,#555);border-radius:.3em;color:light-dark(#000,#fff);margin:0;padding:0 .2em;-moz-tab-size:4;-o-tab-size:4;tab-size:4;white-space:pre-wrap"><span class=_4 style=color:light-dark(#47a,#fff);font-weight:700;margin:0;padding:0>foo</span></code> or which trait defined it. However nesting a few calls with this quickly becomes messy and sometimes I make a practical choice to just use the <code class="_2 language-rust" style="background:light-dark(#ddd,#000);border:1px solid light-dark(#999,#555);border-radius:.3em;color:light-dark(#000,#fff);margin:0;padding:0 .2em;-moz-tab-size:4;-o-tab-size:4;tab-size:4;white-space:pre-wrap"><span class=_3 style=color:light-dark(#85a,#f90);margin:0;padding:0>use</span></code>. I prefer to do this just for a short scope to help keep the origin information close to the code, but for very common traits I will sometimes add a global import like <code class="_2 language-rust" style="background:light-dark(#ddd,#000);border:1px solid light-dark(#999,#555);border-radius:.3em;color:light-dark(#000,#fff);margin:0;padding:0 .2em;-moz-tab-size:4;-o-tab-size:4;tab-size:4;white-space:pre-wrap"><span class=_3 style=color:light-dark(#85a,#f90);margin:0;padding:0>use</span> <span class=_5 style=color:light-dark(#47a,#fff);font-weight:500;margin:0;padding:0>std</span><span class=_3 style=color:light-dark(#85a,#f90);margin:0;padding:0>::</span><span class=_5 style=color:light-dark(#47a,#fff);font-weight:500;margin:0;padding:0>io</span><span class=_3 style=color:light-dark(#85a,#f90);margin:0;padding:0>::</span><span class=_5 style=color:light-dark(#47a,#fff);font-weight:500;margin:0;padding:0>Write</span> <span class=_3 style=color:light-dark(#85a,#f90);margin:0;padding:0>as</span> _</code> (make the trait methods available but don’t actually pull the name into scope).</p><h3 id=rust-std class=_8 style="grid-column:2;margin:.4em 0 0;padding:0"><a href=https://kevincox.ca/2025/07/20/no-imports/#rust-std style=color:inherit!important;margin:0;padding:0;text-decoration:inherit;text-shadow:inherit!important><code class="_2 language-rust" style="background:light-dark(#ddd,#000);border:1px solid light-dark(#999,#555);border-radius:.3em;color:light-dark(#000,#fff);margin:0;padding:0 .2em;-moz-tab-size:4;-o-tab-size:4;tab-size:4;white-space:pre-wrap"><span class=_4 style=color:light-dark(#47a,#fff);font-weight:700;margin:0;padding:0>std</span></code></a></h3><p style=grid-column:2;margin:0;padding:0>The elephant in the room is that in Rust <code class="_2 language-rust" style="background:light-dark(#ddd,#000);border:1px solid light-dark(#999,#555);border-radius:.3em;color:light-dark(#000,#fff);margin:0;padding:0 .2em;-moz-tab-size:4;-o-tab-size:4;tab-size:4;white-space:pre-wrap"><span class=_4 style=color:light-dark(#47a,#fff);font-weight:700;margin:0;padding:0>std</span></code> (and <code class="_2 language-rust" style="background:light-dark(#ddd,#000);border:1px solid light-dark(#999,#555);border-radius:.3em;color:light-dark(#000,#fff);margin:0;padding:0 .2em;-moz-tab-size:4;-o-tab-size:4;tab-size:4;white-space:pre-wrap"><span class=_4 style=color:light-dark(#47a,#fff);font-weight:700;margin:0;padding:0>core</span></code>) love <em style=margin:0;padding:0>love</em> <strong style=margin:0;padding:0>love</strong> hierarchy and lots of names are super long. I wish I could have a long talk with whoever decided it was really important that <code class="_2 language-rust" style="background:light-dark(#ddd,#000);border:1px solid light-dark(#999,#555);border-radius:.3em;color:light-dark(#000,#fff);margin:0;padding:0 .2em;-moz-tab-size:4;-o-tab-size:4;tab-size:4;white-space:pre-wrap"><span class=_5 style=color:light-dark(#47a,#fff);font-weight:500;margin:0;padding:0>std</span><span class=_3 style=color:light-dark(#85a,#f90);margin:0;padding:0>::</span><span class=_5 style=color:light-dark(#47a,#fff);font-weight:500;margin:0;padding:0>sync</span><span class=_3 style=color:light-dark(#85a,#f90);margin:0;padding:0>::</span><span class=_4 style=color:light-dark(#47a,#fff);font-weight:700;margin:0;padding:0>atomic</span></code> really needed to be nested under <code class="_2 language-rust" style="background:light-dark(#ddd,#000);border:1px solid light-dark(#999,#555);border-radius:.3em;color:light-dark(#000,#fff);margin:0;padding:0 .2em;-moz-tab-size:4;-o-tab-size:4;tab-size:4;white-space:pre-wrap"><span class=_4 style=color:light-dark(#47a,#fff);font-weight:700;margin:0;padding:0>sync</span></code>. I think we would have been fine to reserve the top level <code class="_2 language-rust" style="background:light-dark(#ddd,#000);border:1px solid light-dark(#999,#555);border-radius:.3em;color:light-dark(#000,#fff);margin:0;padding:0 .2em;-moz-tab-size:4;-o-tab-size:4;tab-size:4;white-space:pre-wrap"><span class=_5 style=color:light-dark(#47a,#fff);font-weight:500;margin:0;padding:0>std</span><span class=_3 style=color:light-dark(#85a,#f90);margin:0;padding:0>::</span><span class=_4 style=color:light-dark(#47a,#fff);font-weight:700;margin:0;padding:0>atomic</span></code> for these operations even though they are synchronization related.</p><p style=grid-column:2;margin:0;padding:0>It is what it is. I’m not going to advocate for The Great std Naming Reform even if I would prefer it. We have a pattern, and we can deal with it. Most of the names aren’t so long that they need to be imported so most of the time I just spell them out. Occasionally I find myself repeating a long name frequently and just import it. I have yet to be stricken by a bolt of lightning from the heavens.</p></main>]]></content></entry><entry xml:base="/2025/05/03/toronto-fish-chips/"><title>Toronto Fish &amp; Chips</title><link rel="alternate" type="text/html" href="https://kevincox.ca/2025/05/03/toronto-fish-chips/"/><id>https://kevincox.ca/2025/05/03/toronto-fish-chips/</id><published>2025-05-03T17:00:00-04:00</published><updated>2025-05-22T15:30:00-04:00</updated><category term="Life"/><category term="Toronto"/><category term="Food"/><content type="html"><![CDATA[<main class=_0 style="--gap:1em;display:grid;gap:var(--gap);grid:none/1fr minmax(0,36em) 1fr;margin:0;overflow:auto;padding:0"><p style=grid-column:2;margin:0;padding:0>Over the past year I’ve been on a small adventure to find some excellent Fish &amp; Chips in Toronto, Canada. While I haven’t found the perfect chip shop I have gone to a handful of the most popular places and would like to share what I learned.</p><p style=grid-column:2;margin:0;padding:0>I’ve been to quite a few restaurants, but I’m only going to discuss the ones that stand out. I don’t think you will be disappointed by any location on this list.</p><h2 id=locations class=_1 style="grid-column:2;margin:.4em 0 0;padding:0"><a href=https://kevincox.ca/2025/05/03/toronto-fish-chips/#locations style=color:inherit!important;margin:0;padding:0;text-decoration:inherit;text-shadow:inherit!important>Locations</a></h2><h3 id=len-duckworth-fish-chips class=_1 style="grid-column:2;margin:.4em 0 0;padding:0"><a href=https://kevincox.ca/2025/05/03/toronto-fish-chips/#len-duckworth-fish-chips style=color:inherit!important;margin:0;padding:0;text-decoration:inherit;text-shadow:inherit!important>Len Duckworth Fish &amp; Chips</a></h3><p style=grid-column:2;margin:0;padding:0><a href=https://lenduckworthfishandchips.ca/ class=_2 style=color:inherit;margin:0;padding:0;text-decoration:underline;-webkit-text-decoration-color:gray;text-decoration-color:gray>lenduckworthfishandchips.ca</a> - Last Visited 2025</p><p style=grid-column:2;margin:0;padding:0>Thanks to <a href=https://noc.social/@kazaii class=_2 style=color:inherit;margin:0;padding:0;text-decoration:underline;-webkit-text-decoration-color:gray;text-decoration-color:gray>Mark Prosser</a> who sent in this recommendation after the initial list post went out. This one had managed to escape my searching but I eagerly gave it a try.</p><p style=grid-column:2;margin:0;padding:0>The fish was excellent, I tried the halibut as they claim it to be their specialty, and it did not disappoint.</p><p style=grid-column:2;margin:0;padding:0>The chips were also stellar, if I really nitpick they could have been slightly crispier, but they were great as they are with a crisp outside and fluffy inside.</p><p style=grid-column:2;margin:0;padding:0>We shared the Sumptuous Seafood platter and everything on it was top notch. The shrimp was super flavourful and while I rarely enjoy battered scallops these were really good. I would strongly recommend this platter which fairly easily serves two. We also got a side of calamari which was also amazing with some sort of curry mayo dip. To top it off we got the pumpkin pie which was also delicious.</p><h3 id=olde-yorke-fish-chips class=_1 style="grid-column:2;margin:.4em 0 0;padding:0"><a href=https://kevincox.ca/2025/05/03/toronto-fish-chips/#olde-yorke-fish-chips style=color:inherit!important;margin:0;padding:0;text-decoration:inherit;text-shadow:inherit!important>Olde Yorke Fish &amp; Chips</a></h3><p style=grid-column:2;margin:0;padding:0><a href=https://oldeyorkefishandchips.com/ class=_2 style=color:inherit;margin:0;padding:0;text-decoration:underline;-webkit-text-decoration-color:gray;text-decoration-color:gray>oldeyorkefishandchips.com</a> - Last visited 2025</p><p style=grid-column:2;margin:0;padding:0>This is probably the location that I have seen recommended the most. And for good reason! This place serves probably the best battered fish I have ever had. We tried the Cod and Haddock, both were great. The cod was more flavourful, but the haddock was closer to a classic white flaky fish. I definitely want to go back and try the halibut.</p><p style=grid-column:2;margin:0;padding:0>The chips however were a bit mediocre. Not bad by and stretch, but when it says “chips” above your door I expect better. The outside was a touch tougher than I would like, and the inside wasn’t fluffy like it should be.</p><p style=grid-column:2;margin:0;padding:0>We also tried onion rings and a deep-fried Mars bar which were both fantastic. (I don’t know if they allow subbing in onion rings for the chips but if they do, I would recommend that.)</p><h3 id=mcnies-fish-chips class=_1 style="grid-column:2;margin:.4em 0 0;padding:0"><a href=https://kevincox.ca/2025/05/03/toronto-fish-chips/#mcnies-fish-chips style=color:inherit!important;margin:0;padding:0;text-decoration:inherit;text-shadow:inherit!important>McNies Fish &amp; Chips</a></h3><p style=grid-column:2;margin:0;padding:0><a href=https://mcnies.com/ class=_2 style=color:inherit;margin:0;padding:0;text-decoration:underline;-webkit-text-decoration-color:gray;text-decoration-color:gray>mcnies.com</a> - Last visited 2024</p><p style=grid-column:2;margin:0;padding:0>This is an old classic of mine as I practiced karate next door for many years. It seems to mostly fly under the radar online, but I think it deserves to be better known. The highlight here is definitely the chips. They are just perfect with crispy outside and fluffy inside.</p><p style=grid-column:2;margin:0;padding:0>The fish however isn’t as good as it could be. The batter in particular is a bit breadier than I would like as opposed to being super light and crispy.</p><p style=grid-column:2;margin:0;padding:0>Other things I have tried are the onion rings, which are possibly my favourite ever (I’m super picky about rings) and the scallops which I wouldn’t recommend (but I don’t think battered scallops are my cup of tea).</p><h3 id=sea-witch-fish-chips class=_1 style="grid-column:2;margin:.4em 0 0;padding:0"><a href=https://kevincox.ca/2025/05/03/toronto-fish-chips/#sea-witch-fish-chips style=color:inherit!important;margin:0;padding:0;text-decoration:inherit;text-shadow:inherit!important>Sea Witch Fish &amp; Chips</a></h3><p style=grid-column:2;margin:0;padding:0><a href=https://www.seawitchfc.com/ class=_2 style=color:inherit;margin:0;padding:0;text-decoration:underline;-webkit-text-decoration-color:gray;text-decoration-color:gray>seawitchfc.com</a> - Last visited 2024</p><p style=grid-column:2;margin:0;padding:0>This is also an online favourite. One of the easiest to get to as it is just a streetcar or short walk from the 1 subway line. They also have an awesome mural on the side of the building, so it is hard to miss.</p><p style=grid-column:2;margin:0;padding:0>While I enjoyed my meal here it did not stand out in any way. Solid fish and chips that I would definitely go to often if I lived nearby, but if you are going out of your way I would probably try one of the other options first.</p><h2 id=overall class=_1 style="grid-column:2;margin:.4em 0 0;padding:0"><a href=https://kevincox.ca/2025/05/03/toronto-fish-chips/#overall style=color:inherit!important;margin:0;padding:0;text-decoration:inherit;text-shadow:inherit!important>Overall</a></h2><p style=grid-column:2;margin:0;padding:0>I prefer Len Duckworth’s overall. The fish may not have quite been as good as Olde York but it was still excellent and everything else was top of the charts. It is also just a short walk from Main Street Station which makes it very easy to get to from basically anywhere in Toronto. Second place goes to Olde York, the fish is just that good, and I can get great chips elsewhere. McNie’s still has a place in my heart and I would probably go from-time to time if it wasn’t so far away from where I live. While Sea Witch is solid all around I think I would only go back if I was in the area, if I am going out of my way I would probably find something new first.</p><h2 id=others class=_1 style="grid-column:2;margin:.4em 0 0;padding:0"><a href=https://kevincox.ca/2025/05/03/toronto-fish-chips/#others style=color:inherit!important;margin:0;padding:0;text-decoration:inherit;text-shadow:inherit!important>Others?</a></h2><p style=grid-column:2;margin:0;padding:0>Did I miss your favourite? Let me know and I’ll update the list after some thorough research!</p></main>]]></content></entry><entry xml:base="/2025/05/03/dead-birds/"><title>Dead Bird Features</title><link rel="alternate" type="text/html" href="https://kevincox.ca/2025/05/03/dead-birds/"/><id>https://kevincox.ca/2025/05/03/dead-birds/</id><published>2025-05-03T08:00:00-04:00</published><updated>2025-05-03T08:00:00-04:00</updated><content type="html"><![CDATA[<main class=_0 style="--gap:1em;display:grid;gap:var(--gap);grid:none/1fr minmax(0,36em) 1fr;margin:0;overflow:auto;padding:0"><p style=grid-column:2;margin:0;padding:0>Anyone who has had an outdoor cat will have experienced a thoughtful gift left on your doorstep in the morning, usually a dead bird or mouse. These gifts are made with love, and take effort to acquire and return to your doorstep. However, us humans generally do not appreciate them.</p><p style=grid-column:2;margin:0;padding:0>I often find similar features in software. For example, the wiki that our company uses recently released a new feature: when you copy from a document it would format the copied text as Markdown. For example if I copied part of a code snippet inside a second-level list it would be formatted as:</p><pre class=_1 style="background:light-dark(#ddd,#222);border-block:1px solid light-dark(#aaa,#444);display:grid;grid-column:2;grid-column:1/4;grid-template-columns:subgrid;justify-content:flex-start;margin:0;overflow:auto hidden;padding:.4em 0"><code class="_2 language-markdown" style=color:light-dark(#222,#ddd);grid-column:2;margin:0;padding:0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;white-space:pre><span class=_3 style=color:light-dark(#780,#bd3);margin:0;padding:0>1</span><span class=_4 style=color:light-dark(#111,#eee);margin:0;padding:0>.</span>
    <span class=_3 style=color:light-dark(#780,#bd3);margin:0;padding:0>1</span><span class=_4 style=color:light-dark(#111,#eee);margin:0;padding:0>.</span> <span class=_3 style=color:light-dark(#780,#bd3);margin:0;padding:0>```</span><span class=_5 style=color:light-dark(#47a,#fff);font-weight:500;margin:0;padding:0>bash</span>
       -funsafe-math-optimizations
       <span class=_3 style=color:light-dark(#780,#bd3);margin:0;padding:0>```</span>
</code></pre><p style=grid-column:2;margin:0;padding:0>Whoever implemented this feature clearly worked really hard to ensure that parent context like the two lists above were included in this copying. However ultimately it isn’t what users want. When copying the entire list keeping the formatting could be useful, however when copying just one word the context is typically not desired. In this case the result is that I can’t paste it into my terminal.</p><p style=grid-column:2;margin:0;padding:0>Often these features are technically interesting. It is a fun coding project to take a snippet of the document and turn it into accurate Markdown. It is easy to forget to take a step back and think if the user actually wants this.</p><p style=grid-column:2;margin:0;padding:0>I find that these features often occur when overriding default behaviours. Web apps are a common culprit. Overriding copying is a common example, but also overriding spell checking, form filling, link following or undo. Many times there is a key feature that the developer wanted to provide, and they made impressive effort re-implementing browser behaviour. At best the feature is useful, but the reimplemented browser behaviour falls short of native. But very often the feature that was provided isn’t actually even helpful.</p></main>]]></content></entry></feed>